Как обеспечить обратные вызовы для пользовательского элемента

Я решил попробовать свои силы в создании собственного веб-компонента, написав Пользовательский элемент.

export default class DropDown extends HTMLElement {
...

Когда я все установил на место, казалось, что все работает нормально. Но затем я решил добавить несколько перехватчиков обратного вызова, чтобы приложение могло реагировать на события в компоненте.

Логичное место для их добавления — в конструкторе, верно?

 /**
   * 
   * @param {Object} callbacks
   * @param {Function} callbacks.itemSelected 
   * @param {Function} callbacks.dropDownClicked
   */
  constructor(callbacks = {}) {
    super();
    if (callbacks) this.#callbacks = callbacks;
  }

  #handleItemSelected(selection) {
    console.info('handleItemSelected()');
    if (this.#callbacks.itemSelected) this.#callbacks.itemSelected(this, selection);
    if (this.#displaySelection) this.#valuePara.textContent = selection;
  }

И затем в моем коде приложения:

function demoHandler(thisVal, selection) {
    console.info('demoHandler ' + thisVal + ' ' + selection);
}

new DropDown({ itemSelected: demoHandler });

Но обратный вызов не выполнялся. Я понял, что this.#callbacks пусто. Поэтому я записал различные вызовы методов/функций. Вот порядок их выполнения:

constructor()
attributeChangedCallback()
connectedCallback()
constructor()
<user click>
handleItemSelected()

Ух ты. constructor() вызывается платформой Custom Element. Когда мой код приложения вызывает его снова позже, это бессмысленно, поскольку объект, который создает мое приложение, не является объектом, используемым платформой. Более того, приложение не может получить ссылку на объект из экземпляра платформы. Без этой ссылки на объект я не знаю, как передать функции обратного вызова для выполнения объекта.

Как я могу добавить эти обратные вызовы к своему объекту?

🤔 А знаете ли вы, что...
JavaScript позволяет создавать мобильные приложения для iOS и Android с использованием фреймворков, таких как React Native и NativeScript.


180
1

Ответ:

Решено

Я подошел к этой проблеме путем создания общедоступных методов класса. Затем вы можете запросить элемент и напрямую вызвать пользовательский метод.

Вот упрощенный пример того, что я имею в виду. В следующем определении класса есть функция changeToRed, которую можно вызывать напрямую, поскольку это такое же свойство объекта, как и любое другое.

class CustomElement extends HTMLElement {
  
  constructor() {
    super();
  }
  
  changeToRed() {
    this.style.color = 'red';
  }

  connectedCallback() {
    this.innerHTML = `<p>I'm a custom element.</p>`
  }
}

customElements.define("custom-element", CustomElement);

Чтобы получить доступ к этому методу, давайте сначала создадим экземпляр пользовательского элемента в HTML и присвоим ему id.

<custom-element id = "custom-element"></custom-element>

Теперь мы можем получить доступ к этому пользовательскому элементу через DOM и использовать общедоступный метод.

const customEl = document.getElementById('custom-element');
customEl.changeToRed();

См. рабочий пример на CodePen.

Кроме того, кстати, я действительно считаю, что лучше использовать структуру с настраиваемыми элементами. Вам, конечно, это не обязательно, но это значительно облегчит вам жизнь, если вы будете строить что-то любой сложности. Я начал с ванильного JS/TS, затем перешел на Lit, Vue, Svelte и React. В конце концов я остановился на Stencil.js. Мой опыт показал, что ванильные веб-компоненты очень быстро стали очень громоздкими. Чтобы что-то сделать, потребовалось неоправданное количество кода. JSX — ваш друг. Это сэкономит ваше время и приведет к уменьшению полезной нагрузки приложения.


Интересные вопросы для изучения