# Пользовательские элементы: Custom Elements Платформа "веб-компоненты" включает в себя несколько стандартов [Web Components](http://www.w3.org/standards/techs/components#w3c_all), которые находятся в разработке. Начнём мы со стандарта [Custom Elements](http://www.w3.org/TR/custom-elements/), который позволяет создавать свои типы элементов. [cut] ## Зачем Custom Elements? Критично настроенный читатель скажет: "Зачем ещё стандарт для своих типов элементов? Я могу создать любой элемент и прямо сейчас! В любом из современных браузеров можно писать любой HTML, используя свои теги: ``. Или создавать элементы из JavaScript при помощи `document.createElement('mytag')`." Однако, по умолчанию элемент с нестандартным названием (например ``) воспринимается браузером, как нечто неопределённо-непонятное. Ему соответствует класс [HTMLUnknownElement](http://www.w3.org/TR/html5/dom.html#htmlunknownelement), и у него нет каких-либо особых методов. **Стандарт Custom Elements позволяет описывать для новых элементов свои свойства, методы, объявлять свой DOM, подобие конструктора и многое другое.** Давайте посмотрим это на примерах. ```warn header="Для примеров рекомендуется Chrome" Так как спецификация не окончательна, то для запуска примеров рекомендуется использовать Google Chrome, лучше -- последнюю сборку [Chrome Canary](https://www.google.ru/chrome/browser/canary.html), в которой, как правило, отражены последние изменения. ``` ## Новый элемент Для описания нового элемента используется вызов `document.registerElement(имя, { prototype: прототип })`. Здесь: - `имя` -- имя нового тега, например `"mega-select"`. Оно обязано содержать дефис `"-"`. Спецификация требует дефис, чтобы избежать в будущем конфликтов со стандартными элементами HTML. Нельзя создать элемент `timer` или `myTimer` -- будет ошибка. - `прототип` -- объект-прототип для нового элемента, он должен наследовать от `HTMLElement`, чтобы у элемента были стандартные свойства и методы. Вот, к примеру, новый элемент ``: ```html run *!* */!* 0 ``` Использовать новый элемент в HTML можно и до его объявления через `registerElement`. Для этого в браузере предусмотрен специальный режим "обновления" существующих элементов. Если браузер видит элемент с неизвестным именем, в котором есть дефис `-` (такие элементы называются "unresolved"), то: - Он ставит такому элементу специальный CSS-псевдокласс `:unresolved`, для того, чтобы через CSS можно было показать, что он ещё "не подгрузился". - При вызове `registerElement` такие элементы автоматически обновятся до нужного класса. В примере ниже регистрация элемента происходит через 2 секунды после его появления в разметке: ```html run no-beautify Hello, world! ``` Можно создавать такие элементы и в JavaScript -- обычным вызовом `createElement`: ```js var timer = document.createElement('my-timer'); ``` ## Расширение встроенных элементов Выше мы видели пример создания элемента на основе базового `HTMLElement`. Но можно расширить и другие, более конкретные HTML-элементы. Для расширения встроенных элементов у `registerElement` предусмотрен параметр `extends`, в котором можно задать, какой тег мы расширяем. Например, кнопку: ```html run ``` Важные детали: Прототип теперь наследует не от `HTMLElement`, а от `HTMLButtonElement` : Чтобы расширить элемент, нужно унаследовать прототип от его класса. В HTML указывается при помощи атрибута `is="..."` : Это принципиальное отличие разметки от обычного объявления без `extends`. Теперь `` работать не будет, нужно использовать исходный тег и `is`. Работают методы, стили и события кнопки. : При клике на кнопку её не отличишь от встроенной. И всё же, это новый элемент, со своими методами, в данном случае `tick`. При создании нового элемента в JS, если используется `extends`, необходимо указать и исходный тег в том числе: ```js var timer = document.createElement("button", "my-timer"); ``` ## Жизненный цикл В прототипе своего элемента мы можем задать специальные методы, которые будут вызываться при создании, добавлении и удалении элемента из DOM:
createdCallbackЭлемент создан
attachedCallbackЭлемент добавлен в документ
detachedCallbackЭлемент удалён из документа
attributeChangedCallback(name, prevValue, newValue)Атрибут добавлен, изменён или удалён
Как вы, наверняка, заметили, `createdCallback` является подобием конструктора. Он вызывается только при создании элемента, поэтому всю дополнительную инициализацию имеет смысл описывать в нём. Давайте используем `createdCallback`, чтобы инициализировать таймер, а `attachedCallback` -- чтобы автоматически запускать таймер при вставке в документ: ```html run 0 ``` ## Итого Мы рассмотрели, как создавать свои DOM-элементы при помощи стандарта [Custom Elements](http://www.w3.org/TR/custom-elements/). Далее мы перейдём к изучению дополнительных возможностей по работе с DOM.