Перейти к содержимому

Пользовательские элементы

Пользовательские элементы (Custom Elements) являются одним из фундаментальных столпов Web Components, позволяя разработчикам определять собственные HTML-теги и компоненты. Эта возможность расширяет стандартный словарь HTML, давая возможность создавать переиспользуемые и инкапсулированные элементы с пользовательской логикой. Давайте глубже погрузимся в мир пользовательских элементов и узнаем, как использовать их возможности.

Определение пользовательского элемента

Для создания пользовательского элемента используется синтаксис class в JavaScript для определения нового класса, расширяющего встроенный класс HTMLElement. Этот класс инкапсулирует поведение и свойства элемента. После определения он регистрируется в браузере с помощью customElements.define().

Пример: Создание простого пользовательского элемента


html
<my-custom-element></my-custom-element>
<script>
  class MyCustomElement extends HTMLElement {
    constructor() {
      super();
      this.attachShadow({ mode: 'open' });
      this.shadowRoot.innerHTML = `<p>Hello, World!</p>`;
    }
  }
  
  customElements.define('my-custom-element', MyCustomElement);
</script>

В этом примере определяется простой пользовательский элемент с именем my-custom-element, который отображает текст «Hello, World!» внутри shadow DOM. Чтобы использовать этот элемент, просто добавьте <code><my-custom-element></code> в ваш HTML.

note

Пользовательские элементы v1 поддерживаются во всех современных браузерах (Chrome 54+, Firefox 52+, Safari 10.1+, Edge 79+). Всегда проверяйте совместимость с браузерами, если ваша целевая аудитория использует устаревшие среды.

Колбэки жизненного цикла

Пользовательские элементы имеют набор колбэков жизненного цикла, которые позволяют разработчикам выполнять код в определенные моменты жизни элемента:

  • connectedCallback(): Вызывается каждый раз, когда пользовательский элемент добавляется в документ.
  • disconnectedCallback(): Вызывается каждый раз, когда пользовательский элемент удаляется из DOM документа.
  • attributeChangedCallback(name, oldValue, newValue): Вызывается каждый раз, когда один из атрибутов пользовательского элемента добавляется, удаляется или изменяется.
  • adoptedCallback(): Вызывается каждый раз, когда пользовательский элемент перемещается в новый документ.
КолбэкКогда срабатывает
connectedCallback()Элемент добавлен в DOM
disconnectedCallback()Элемент удален из DOM
attributeChangedCallback(name, oldValue, newValue)Изменен наблюдаемый атрибут
adoptedCallback()Элемент перемещен в новый документ

Пример: Использование колбэков жизненного цикла


javascript
<lifecycle-element></lifecycle-element>
<script>
class LifecycleElement extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
    this.shadowRoot.innerHTML = `
      <style>
        #status {
          color: blue;
          font-weight: bold;
        }
      </style>
      <p>Lifecycle Element</p>
      <p id="status">Element not connected</p>
    `;
  }

  connectedCallback() {
    this.shadowRoot.getElementById('status').textContent = 'Element connected to the page.';
  }

  disconnectedCallback() {
    this.shadowRoot.getElementById('status').textContent = 'Element disconnected from the page.';
  }
}

customElements.define('lifecycle-element', LifecycleElement);
</script>

Атрибуты и свойства

Пользовательские элементы могут иметь атрибуты и свойства для управления их состоянием и поведением. Атрибуты задаются непосредственно в HTML и всегда являются строками, тогда как свойства устанавливаются на объекте DOM элемента и могут иметь любой тип данных. Обратите внимание, что attributeChangedCallback срабатывает только для атрибутов, явно перечисленных в методе элемента static get observedAttributes().

Пример: Управление атрибутами и свойствами


javascript
<attribute-element id="element" data-content="Initial content"></attribute-element>
<button onclick="buttonClicked()">Click to change attribute</button>
<script>
  class AttributeElement extends HTMLElement {
    constructor() {
      super();
      this.attachShadow({ mode: 'open' });
      this.shadowRoot.innerHTML = `<p>Attribute Example: <span id="content"></span></p>`;
    }
  
    static get observedAttributes() {
      return ['data-content'];
    }
  
    attributeChangedCallback(name, oldValue, newValue) {
      if (name === 'data-content') {
        this.shadowRoot.getElementById('content').textContent = newValue;
      }
    }
  
    set content(value) {
      this.setAttribute('data-content', value);
    }
  
    get content() {
      return this.getAttribute('data-content');
    }
  }
  
  customElements.define('attribute-element', AttributeElement);

  function buttonClicked() {
    alert('button clicked!');
    const ourCustomElement = document.getElementById('element');
    ourCustomElement.content = 'New content';
  }
</script>

Здесь attribute-element обновляет свое содержимое на основе атрибута data-content. Свойство content предоставляет удобный способ получения и установки этого атрибута программно.

Расширение встроенных элементов

Пользовательские элементы могут расширять встроенные HTML-элементы, добавляя новую функциональность при сохранении их исходного поведения.

Пример: Расширение встроенного элемента


javascript
<button is="fancy-button">Click me!</button>
<script>
  class FancyButton extends HTMLButtonElement {
    constructor() {
      super();
      this.addEventListener('click', () => {
        alert('Fancy button clicked!');
      });
    }
  }
  
  customElements.define('fancy-button', FancyButton, { extends: 'button' });
</script>

Здесь fancy-button расширяет стандартный элемент <button>, добавляя сообщение alert при нажатии на кнопку.

Лучшие практики для пользовательских элементов

  1. Используйте Shadow DOM: Всегда инкапсулируйте внутреннюю структуру и стили вашего пользовательского элемента с помощью Shadow DOM.
  2. Определяйте четкие API: Предоставляйте четкие и интуитивно понятные API для ваших пользовательских элементов через хорошо документированные атрибуты и свойства.
  3. Управление жизненным циклом: Правильно управляйте колбэками жизненного цикла элемента, чтобы обеспечить надежное поведение и избежать утечек памяти.
  4. Доступность: Убедитесь, что ваши пользовательские элементы доступны, добавив соответствующие ARIA-роли и свойства.
  5. Тестирование: Тщательно тестируйте ваши пользовательские элементы в разных браузерах и средах, чтобы обеспечить совместимость и стабильность.

Заключение

Пользовательские элементы предлагают мощный способ расширения HTML, позволяя создавать переиспользуемые и инкапсулированные компоненты с пользовательской логикой. Используя возможности пользовательских элементов, включая колбэки жизненного цикла, атрибуты, свойства и Shadow DOM, разработчики могут создавать сложные и поддерживаемые веб-приложения.

Начните экспериментировать с пользовательскими элементами в своих проектах уже сегодня и откройте новые возможности для веб-разработки. Приведенные здесь примеры — это только начало. Используйте их как основу для создания собственных инновационных пользовательских элементов.

Practice

Какие из следующих утверждений о пользовательских элементах в JavaScript являются верными?

Считаете ли это полезным?

Предпросмотр dual-run — сравните с маршрутами Symfony на продакшене.