aboutsummaryrefslogtreecommitdiff
path: root/files/ru/web/web_components/using_custom_elements/index.html
diff options
context:
space:
mode:
authorFlorian Merz <me@fiji-flo.de>2021-02-11 14:51:05 +0100
committerFlorian Merz <me@fiji-flo.de>2021-02-11 14:51:05 +0100
commitc058fa0fb22dc40ef0225b21a97578cddd0aaffa (patch)
treedf20f8b4c724b61cb9c34cdb450a7ac77d690bd0 /files/ru/web/web_components/using_custom_elements/index.html
parent8260a606c143e6b55a467edf017a56bdcd6cba7e (diff)
downloadtranslated-content-c058fa0fb22dc40ef0225b21a97578cddd0aaffa.tar.gz
translated-content-c058fa0fb22dc40ef0225b21a97578cddd0aaffa.tar.bz2
translated-content-c058fa0fb22dc40ef0225b21a97578cddd0aaffa.zip
unslug ru: move
Diffstat (limited to 'files/ru/web/web_components/using_custom_elements/index.html')
-rw-r--r--files/ru/web/web_components/using_custom_elements/index.html243
1 files changed, 243 insertions, 0 deletions
diff --git a/files/ru/web/web_components/using_custom_elements/index.html b/files/ru/web/web_components/using_custom_elements/index.html
new file mode 100644
index 0000000000..bcb5b5d733
--- /dev/null
+++ b/files/ru/web/web_components/using_custom_elements/index.html
@@ -0,0 +1,243 @@
+---
+title: Использование пользовательских элементов
+slug: Web/Web_Components/Использование_пользовательских_элементов
+translation_of: Web/Web_Components/Using_custom_elements
+---
+<div>{{DefaultAPISidebar("Web Components")}}</div>
+
+<p class="summary">Одна из ключевых особенностей стандарта Веб-компонент это возможность создавать пользовательские элементы на HTML-странице, инкапсулирующие функциональность, вместо того чтобы создавать длинyю, вложенную группу элементов, которые бы вместе реализовывали нужную пользовательскую фичу. Эта статья является вводной по использованию пользовательских HTML-компонент.</p>
+
+<div class="note">
+<p><strong>Заметка</strong>: Пользовательские элементы поддерживаются по умолчанию в Firefox, Chrome и Opera. Safari пока поддерживает только автономные пользовательские компоненты, Edge также работает над реализацией.</p>
+</div>
+
+<h2 id="Высокоуровневый_обзор">Высокоуровневый обзор</h2>
+
+<p>Контроллером пользовательских элементов веб-документа является объект {{domxref("CustomElementRegistry")}} — этот элемент позволяет регистрировать пользовательские элементы на веб-странице, возвращает информацию о зарегистрированных элементах и т.п.</p>
+
+<p>Чтобы зарегистрировать пользовательский элемент на странице, используйте метод {{domxref("CustomElementRegistry.define()")}} method. Он принимает аргументы:</p>
+
+<ul>
+ <li>{{domxref("DOMString")}} - имя элемента. Обратите внимание, что в именах пользовательских элементах <a href="https://stackoverflow.com/questions/22545621/do-custom-elements-require-a-dash-in-their-name">должен содержаться дефис</a>; они не могут состоять только из одного слова.</li>
+ <li>Объект типа <a href="/en-US/docs/Web/JavaScript/Reference/Classes">class</a>, определяющий поведение элемента.</li>
+ <li>Опционально объект options, имеющий свойство <code>extends</code>, соответствующее встроенному элементу, от которого наследует объект.</li>
+</ul>
+
+<p>К примеру, мы можем определить пользовательский элемент <a href="https://mdn.github.io/web-components-examples/word-count-web-component/">word-count element</a>:</p>
+
+<pre class="brush: js">customElements.define('word-count', WordCount, { extends: 'p' });</pre>
+
+<p>Этот элемент называется <code>word-count</code>, объект соответствующего класса называется <code>WordCount</code>, и он наследует элементу {{htmlelement("p")}}.</p>
+
+<p>Объект класса пользовательского элемента определяется с помощью синтаксиса классов ES 2015. Например, <code>WordCount</code> имеют следующую структуру:</p>
+
+<pre class="brush: js">class WordCount extends HTMLParagraphElement {
+ constructor() {
+ // Всегда первым делом вызывайте super() в конструкторе
+ super();
+
+ // Далее пишется функциональность элемента
+
+ ...
+ }
+}</pre>
+
+<p>Это простой пример, но его можно дополнить. Можно определить специальные lifecycle callbacks, которые будут вызваны в определенные моменты жизненного цикла элемента. Например, <code>connectedCallback</code> будет вызван, когда пользовательский элемент оказывается впервые встроен в DOM, а <code>attributeChangedCallback</code> вызывается, когда пользовательскому элементу добавляют, удаляют или изменяют какой-то аттрибут.</p>
+
+<p>Подробнее об этом в секции {{anch("Using the lifecycle callbacks")}} ниже.</p>
+
+<p>Есть два типа пользовательских элементов:</p>
+
+<ul>
+ <li><strong>Автономные пользователькие элементы</strong> независимы — они не наследуют встроенным HTML-элементам. Их используют на странице просто как обычный HTML-элемент. Например, <code>&lt;popup-info&gt;</code> или <code>document.createElement("popup-info")</code>.</li>
+ <li><strong>Модифицированные встроенные элементы</strong> наследуют базовым HTML-элементам. Чтобы создать один из них, вы определяете элемент, от которого они унаследованы (как в примерах выше), и используете их как обычный базовый элемент, но с добавлением атрибута/свойства с именем пользовательского элемента {{htmlattrxref("is")}}. Например <code>&lt;p is="word-count"&gt;</code>, или <code>document.createElement("p", { is: "word-count" })</code>.</li>
+</ul>
+
+<h2 id="Разбор_простых_примеров">Разбор простых примеров</h2>
+
+<p>А сейчас давайте разберем еще несколько простых примеров, иллюстрирующих подробности создания пользовательских элементов.</p>
+
+<h3 id="Автономные_пользовательские_элементы">Автономные пользовательские элементы</h3>
+
+<p>Рассмотрим пример автономного пользовательского элемента — <code><a href="https://github.com/mdn/web-components-examples/tree/master/popup-info-box-web-component">&lt;popup-info-box&gt;</a></code> (см. <a href="https://mdn.github.io/web-components-examples/popup-info-box-web-component/">работающий пример</a>). Он содержит изображение и строку, и встраивает изображение в страницу. Когда на изображение наводят фокус, компонент показывает всплывающую подсказку с текстом.</p>
+
+<p>Прежде всего файл JavaScript определяет класс <code>PopUpInfo</code>, наследующий от {{domxref("HTMLElement")}}. Автономные пользовательские элементы почти всегда наследуют <code>HTMLElement</code>.</p>
+
+<pre class="brush: js">class PopUpInfo extends HTMLElement {
+ constructor() {
+ // Всегда первым делом вызывайте super() в конструкторе
+ super();
+
+ // далее следует функциональность элемента
+
+ ...
+ }
+}</pre>
+
+<p>В этом фрагменте кода содержится определение конструктора {{jsxref("Classes.constructor","constructor")}} класса, которое всегда начинается с вызова <code><a href="/en-US/docs/Web/JavaScript/Reference/Operators/super">super()</a></code> чтобы отработала цепочка прототипного наследования.</p>
+
+<p>Внутри конструктора мы определяем всю функциональность, которую получит элемент при создании его объекта. В данном случае мы добавляем shadow root к пользовательскому элементу, производим манипуляции с DOM, чтобы создать определенную структуру shadow DOM внутри элемента — которая затем присоединяется к shadow root — и наконец добавляем CSS к shadow root, чтобы задать его стиль.</p>
+
+<pre class="brush: js">// Создание shadow root
+var shadow = this.attachShadow({mode: 'open'});
+
+// Создание spans
+var wrapper = document.createElement('span');
+wrapper.setAttribute('class','wrapper');
+var icon = document.createElement('span');
+icon.setAttribute('class','icon');
+icon.setAttribute('tabindex', 0);
+var info = document.createElement('span');
+info.setAttribute('class','info');
+
+// Берем содержимое атрибута и добавляем его в span
+var text = this.getAttribute('text');
+info.textContent = text;
+
+// Вставляем иконку
+var imgUrl;
+if(this.hasAttribute('img')) {
+ imgUrl = this.getAttribute('img');
+} else {
+ imgUrl = 'img/default.png';
+}
+var img = document.createElement('img');
+img.src = imgUrl;
+icon.appendChild(img);
+
+// Создаем CSS для shadow dom
+var style = document.createElement('style');
+
+style.textContent = '.wrapper {' +
+// CSS truncated for brevity
+
+// добавляем созданные элементы к shadow dom
+
+shadow.appendChild(style);
+shadow.appendChild(wrapper);
+wrapper.appendChild(icon);
+wrapper.appendChild(info);</pre>
+
+<p>Наконец, регистрируем пользовательский элемент в <code>CustomElementRegistry</code> с помощью метода <code>define()</code>, который упоминался ранее — в качестве параметров мы передаем ему имя элемента и имя класса, который содержит его функциональность:</p>
+
+<pre class="brush: js">customElements.define('popup-info', PopUpInfo);</pre>
+
+<p>Теперь он доступен для использования на нашей странице. В HTML мы используем его так:</p>
+
+<pre class="brush: html">&lt;popup-info img="img/alt.png" text="Код валидации вашей карты (CVC)
+ это дополнительная мера безопасности — это последние 3 или 4 цифры
+ на обороте вашей карты."&gt;</pre>
+
+<div class="note">
+<p><strong>Замечение</strong>: Вы можете прочитать <a href="https://github.com/mdn/web-components-examples/blob/master/popup-info-box-web-component/main.js">полный исходный код на JavaScript</a> здесь.</p>
+</div>
+
+<h3 id="Модифицированные_встроенные_элементы">Модифицированные встроенные элементы</h3>
+
+<p>Тепреь давайте взглянем на другой пример модифицированного пользовательского элемента — <a href="https://github.com/mdn/web-components-examples/tree/master/expanding-list-web-component">раскрывающийся список</a> (<a href="https://mdn.github.io/web-components-examples/expanding-list-web-component/">см. действующий пример</a>). Он превращает любой ненумерованный список в раскрывающееся/складывающееся меню.</p>
+
+<p>Первым делом определим класс элемента наподобие того, как это делалось выше:</p>
+
+<pre class="brush: js">class ExpandingList extends HTMLUListElement {
+ constructor() {
+ // Всегда первым делом вызываем super() в конструкторе
+ super();
+
+ // ниже следует функциональность элемента
+
+ ...
+ }
+}</pre>
+
+<p>Здесь мы не будем во всех подробностях описывать функциональность элемента, вы можете понять как он работает, посмотрев исходный код. Единственное принципиальное различие с предыдующим примером состоит в том, что мы используем интерфейс {{domxref("HTMLUListElement")}}, а не {{domxref("HTMLElement")}}. Так что у него есть все характеристики элемента {{htmlelement("ul")}}, плюс дополнительная функциональность, которую определили мы. Это и отличает модифицированный встроенный элемент от автономного пользовательского элемента.</p>
+
+<p>Далее мы регистрируем этот элемент с помощью метода <code>define()</code> как в прошлом примере, только на сей раз мы добавляем объект options, который определяет, какому встроенному элементу наследует данный:</p>
+
+<pre class="brush: js">customElements.define('expanding-list', ExpandingList, { extends: "ul" });</pre>
+
+<p>Встроенный элемент используется на веб-странице немного по-другому:</p>
+
+<pre class="brush: html">&lt;ul is="expanding-list"&gt;
+
+ ...
+
+&lt;/ul&gt;</pre>
+
+<p>Вы задаете элемент <code>&lt;ul&gt;</code> как обычно, но указываете имя модифицированного элемента в атрибуте <code>is</code>.</p>
+
+<div class="note">
+<p><strong>Замечание</strong>: Полный <a href="https://github.com/mdn/web-components-examples/blob/master/expanding-list-web-component/main.js">исходный код на JavaScript</a> доступен здесь.</p>
+</div>
+
+<h2 id="Использование_lifecycle_callbacks">Использование lifecycle callbacks</h2>
+
+<p>Вы можете определить несколько разных коллбеков в конструкторе пользовательских элементов, которые сработают на разных этапах жизненного цикла элемента:</p>
+
+<ul>
+ <li><code>connectedCallback</code>: Срабатывает, когда пользовательский элемент впервые добавляется в DOM.</li>
+ <li><code>disconnectedCallback</code>: Срабатывает, когда пользовательский элемент удаляется из DOM.</li>
+ <li><code>adoptedCallback</code>: Срабатывает, когда пользовательский элемент перемещен в новый документ.</li>
+ <li><code>attributeChangedCallback</code>: Срабатывает, когда пользовательскому элементу добавляют, удаляют или изменяют атрибут.</li>
+</ul>
+
+<p>Посмотрим на них в действии. Код ниже взят из примера <a href="https://github.com/mdn/web-components-examples/tree/master/life-cycle-callbacks">life-cycle-callbacks</a> (<a href="https://mdn.github.io/web-components-examples/life-cycle-callbacks/">см. его в действии</a>). Это тривиальный пример, создающий на странице цветной квадрат. Вот как выглядит код пользовательского элемента:</p>
+
+<pre class="brush: html">&lt;custom-square l="100" c="red"&gt;&lt;/custom-square&gt;</pre>
+
+<p>Конструктор класса очень простой — мы просто добавляем shadow DOM к элементу, а затем добавляем пустые элементы {{htmlelement("div")}} и {{htmlelement("style")}} к shadow root:</p>
+
+<pre class="brush: js">var shadow = this.attachShadow({mode: 'open'});
+
+var div = document.createElement('div');
+var style = document.createElement('style');
+shadow.appendChild(style);
+shadow.appendChild(div);</pre>
+
+<p>Наиболее важная функция в этом примере <code>updateStyle()</code> — она принимает элемент, находит его shadow root, находит его элемент <code>&lt;style&gt;</code>, и добавляет {{cssxref("width")}}, {{cssxref("height")}}, и {{cssxref("background-color")}} к стилям.</p>
+
+<pre class="brush: js">function updateStyle(elem) {
+ var shadow = elem.shadowRoot;
+ var childNodes = shadow.childNodes;
+ for(var i = 0; i &lt; childNodes.length; i++) {
+ if(childNodes[i].nodeName === 'STYLE') {
+ childNodes[i].textContent = 'div {' +
+ ' width: ' + elem.getAttribute('l') + 'px;' +
+ ' height: ' + elem.getAttribute('l') + 'px;' +
+ ' background-color: ' + elem.getAttribute('c');
+ }
+ }
+}</pre>
+
+<p>Сами изменения стилей обрабатываются коллбеками жизненного цикла, находящимися внутри конструктора. <code>connectedCallback()</code> срабатывает, когда элемент встраивается в DOM — здесь мы запускаем функцию <code>updateStyle()</code> которая обсеспечивает, чтобы квадрат имел стиль, описанный в его атрибутах:</p>
+
+<pre class="brush: js">connectedCallback() {
+ console.log('Пользовательский элемент квадрат добавлен на страницу.');
+ updateStyle(this);
+}</pre>
+
+<p>коллбеки <code>disconnectedCallback()</code> и <code>adoptedCallback()</code> логируют простые сообщения на консоль, которые уведомляют нас, что элемент удален из DOM или перемещен на другую страницу:</p>
+
+<pre class="brush: js">disconnectedCallback() {
+ console.log('Пользователский элемент квадрат удален.');
+}
+
+adoptedCallback() {
+ console.log('Пользовательский элемент квадарат перемещен на другую страницу.');
+}</pre>
+
+<p>Коллбек <code>attributeChangedCallback()</code> запускается когда один из аттрибутов элемента меняется. Как видно из его свойств, можно воздействовать на индивидуальные аттрибуты, глядя на их имена, и новые и старые значения аттрибутов. В данном случае, однако, мы просто снова запускаем функцию <code>updateStyle()</code> чтобы убедиться, что атрибуты квадрата получили новые значения:</p>
+
+<pre class="brush: js">attributeChangedCallback(name, oldValue, newValue) {
+ console.log('Атрибуты пользовательского элемента квадрат изменились.');
+ updateStyle(this);
+}</pre>
+
+<p>Обратите внимание, что нужно наблюдать за атрибутами, чтобы запустить коллбек <code>attributeChangedCallback()</code> когда они изменятся. Это делается через вызов геттера <code>observedAttributes()</code> в конструкторе, который содержит оператор <code>return</code> возвращающий массив с именами атрибутов, которые вы хотите наблюдать:</p>
+
+<pre class="brush: js">static get observedAttributes() {return ['w', 'l']; }</pre>
+
+<p>В нашем случае он расположен в начале конструктора.</p>
+
+<div class="note">
+<p><strong>Замечение</strong>: Смотрите <a href="https://github.com/mdn/web-components-examples/blob/master/life-cycle-callbacks/main.js">полный исходный код на JavaScript</a> здесь.</p>
+</div>