From 0e31dfe42cd17c1e717d1eab645ef50c96c13667 Mon Sep 17 00:00:00 2001
From: dmitryskripunov <dskripunov@gmail.com>
Date: Tue, 7 Jan 2020 19:11:22 +0000
Subject: [PATCH 1/4] Translate customelements.md via GitLocalize

---
 .../web-components/customelements.md          | 1127 +++++++++++++++++
 1 file changed, 1127 insertions(+)
 create mode 100644 src/content/ru/fundamentals/web-components/customelements.md

diff --git a/src/content/ru/fundamentals/web-components/customelements.md b/src/content/ru/fundamentals/web-components/customelements.md
new file mode 100644
index 00000000000..d40ca2b3f19
--- /dev/null
+++ b/src/content/ru/fundamentals/web-components/customelements.md
@@ -0,0 +1,1127 @@
+project_path: "/web/fundamentals/_project.yaml"
+book_path: "/web/fundamentals/_book.yaml"
+description: Custom elements allow web developers to define new HTML tags, extend
+  existing ones, and create reusable web components.
+
+{# wf_updated_on: 2018-09-20 #}
+{# wf_published_on: 2016-06-28 #}
+{# wf_blink_components: Blink>DOM #}
+
+# Custom Elements v1: веб-компоненты для повторного использования {: .page-title }
+
+{% include "web/_shared/contributors/ericbidelman.html" %}
+
+### Краткое изложение {: #tldr .hide-from-toc }
+
+При помощи [Custom
+Elements](https://html.spec.whatwg.org/multipage/scripting.html#custom-elements)
+(* пользовательские элементы. Здесь и далее примеч. пер.) веб-разработчики могут
+** создавать новые теги HTML **, совершенствовать существующие или улучшать
+созданные другими разработчиками компоненты. Этот API – фундамент [ Web
+Components ](http://webcomponents.org/). За счет него у нас имеется основанный
+на веб-стандартах способ создания компонентов для повторного использования при
+помощи лишь чистого кода JS/HTML/CSS. Благодаря нему нам необходимо писать
+меньше кода и мы получаем модульный код, который можем повторно использовать в
+нашем приложении.
+
+## Введение
+
+Обратите внимание: В данной статье описывается новая <a
+href="https://html.spec.whatwg.org/multipage/scripting.html#custom-elements"
+target="_blank"> спецификация Custom Elements </a>. Если вы использовали
+пользовательские элементы, то, вероятно, знакомы с <a
+href="https://www.chromestatus.com/features/4642138092470272"> версией 0,
+поддержка которой реализована в Chrome 33 </a>. Принцип работы тот же, однако в
+спецификацию версии 1 внесены важные поправки в API. Читайте далее, чтобы
+узнать, что нового появилось в этой версии или ознакомьтесь с разделом <a
+href="#historysupport"> История и поддержка браузером </a> для получения
+дополнительной информации.
+
+Браузер предоставляет нам великолепный инструмент для структурирования
+веб-приложений. Этот инструмент называется HTML. Вы, должно быть, слышали о нем!
+Это декларативный, портируемый язык с хорошей поддержкой и с ним легко работать.
+Каким бы великолепным не казался HTML, его словарный состав и расширяемость
+ограничены. В [ живом стандарте HTML ](https://html.spec.whatwg.org/multipage/)
+всегда не хватало способа автоматического объединения поведения, реализуемого
+при помощи JS, с вашей разметкой... до сих пор.
+
+За счет Пользовательских элементов осуществляется модернизация HTML, заполнение
+недостающих кусочков мозаики и объединение структуры и поведения. Если мы не
+можем решить проблему за счет имеющихся средств HTML, то можем создать для ее
+решения пользовательский элемент. ** Благодаря Пользовательским элементам
+расширяются функциональные возможности браузера и в то же время сохраняются
+преимущества использования HTML **.
+
+## Определение нового элемента {: #define}
+
+Для того чтобы определить новый элемент HTML, нам необходимо воспользоваться
+возможностями JavaScript!
+
+Свойство ` customElements ` глобального объекта window используется для
+определения нового пользовательского элемента и обучения браузера тому, как его
+отображать. Вызовите ` customElements.define() `, передав в качестве параметров
+имя тега, который хотите создать, и  `класс` JavaScript, который наследуется от
+базового класса `HTMLElement`.
+
+**Пример:** - определение боковой выдвижной навигационной панели для мобильных
+устройств, `<app-drawer>`:
+
+```
+class AppDrawer extends HTMLElement {...}
+window.customElements.define('app-drawer', AppDrawer);
+
+// Or use an anonymous class if you don't want a named constructor in current scope.
+window.customElements.define('app-drawer', class extends HTMLElement {...});
+```
+
+Пример использования:
+
+```
+<app-drawer></app-drawer>
+```
+
+Важно помнить, что использование пользовательского элемента ничем не отличается
+от использования `<div>` или любого другого элемента. Его образцы могут быть
+объявлены на странице, созданы динамически при помощи кода JavaScript, могут
+быть добавлены обработчики событий и т.д. Читайте далее для ознакомления с
+большим количеством примеров.
+
+### Определение JavaScript API элемента {: #jsapi}
+
+Функциональные возможности пользовательского элемента определяются при помощи
+[`class`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes)
+ES2015, который расширяет `HTMLElement`. За счет наследования от `HTMLElement`
+гарантируется, что пользовательский элемент перенимает весь DOM API, и
+обеспечивается то, что любые добавленные к классу свойства/методы становятся
+частью интерфейса DOM элемента. По сути, используйте класс для создания
+**публичного JavaScript API**.
+
+**Пример**: определение интерфейса DOM `<app-drawer>`:
+
+```
+class AppDrawer extends HTMLElement {
+
+  // A getter/setter for an open property.
+  get open() {
+    return this.hasAttribute('open');
+  }
+
+  set open(val) {
+    // Reflect the value of the open property as an HTML attribute.
+    if (val) {
+      this.setAttribute('open', '');
+    } else {
+      this.removeAttribute('open');
+    }
+    this.toggleDrawer();
+  }
+
+  // A getter/setter for a disabled property.
+  get disabled() {
+    return this.hasAttribute('disabled');
+  }
+
+  set disabled(val) {
+    // Reflect the value of the disabled property as an HTML attribute.
+    if (val) {
+      this.setAttribute('disabled', '');
+    } else {
+      this.removeAttribute('disabled');
+    }
+  }
+
+  // Can define constructor arguments if you wish.
+  constructor() {
+    // If you define a constructor, always call super() first!
+    // This is specific to CE and required by the spec.
+    super();
+
+    // Setup a click listener on <app-drawer> itself.
+    this.addEventListener('click', e => {
+      // Don't toggle the drawer if it's disabled.
+      if (this.disabled) {
+        return;
+      }
+      this.toggleDrawer();
+    });
+  }
+
+  toggleDrawer() {
+    ...
+  }
+}
+
+customElements.define('app-drawer', AppDrawer);
+```
+
+В этом примере мы создаем выдвижную панель со свойствами `open`, `disabled` и
+методом `toggleDrawer()`. Он также [отражает свойства как атрибуты
+HTML](#reflectattr).
+
+Удобная особенность пользовательских элементов - то, что **`this` внутри
+определения класса относится к самому элементу DOM** , то есть к экземпляру
+класса. В нашем примере `this` относится к `<app-drawer>` . За счет этого
+элемент может подключить слушатель `click` к себе самому! И вы не ограничены
+слушателями событий! Весь API DOM доступен внутри кода элемента. Используйте
+`this` для доступа к свойствам элемента, обращения к его дочерним элементам (
+`this.children` ), запроса узлов ( `this.querySelectorAll('.items')` ) и т.д.
+
+**Правила создания пользовательских элементов**
+
+1. Имя пользовательского элемента **должен содержать дефис (-)**. Таким образом,
+`<x-tags>` , `<my-element>` и `<my-awesome-app>` - допустимые имена, а `<tabs>`
+и `<foo_bar>` - нет. Благодаря этому парсер HTML может отличить пользовательские
+элементы от стандартных. Это также обеспечивает прямую совместимость при
+добавлении новых тегов в HTML.
+2. Вы не можете зарегистрировать один и тот же тэг более одного раза. При
+попытке это выполнить будет выкинута ошибка `DOMException`. Как только вы
+сообщили браузеру о новом тэге, то все. Назад дороги нет.
+3. Пользовательские элементы не могут быть самозакрывающимися, поскольку
+согласно стандарту HTML только [несколько
+элементов](https://html.spec.whatwg.org/multipage/syntax.html#void-elements)
+могут быть самозакрывающимися. Всегда добавляйте закрывающийся тэг (
+<code><app-drawer></app-drawer></code> drawer
+<code><app-drawer></app-drawer></code> ).
+
+## Реакции (ответные действия) пользовательского элемента {: #reactions}
+
+Пользовательский элемент может определять специальные хуки жизненного цикла для
+запуска кода в интересные периоды его существования. Они называются **реакциями
+пользовательского элемента.**
+
+<table>
+  <thead>
+    <tr>
+      <th>Имя</th>
+      <th>Вызывается</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td><code>constructor</code></td>
+<td>Экземпляр элемента создан или <a href="#upgrades">обновлён</a>.
+Полезна для инициализации состояния, установки слушаетелей событий или        
+<a href="#shadowdom">создания теневого dom (shadow dom)</a>. Смотри <a
+href="https://html.spec.whatwg.org/multipage/scripting.html#custom-element-conformance">спецификацию</a>
+чтобы узнать об ограничениях использования <code>constructor</code>.</td>
+    </tr>
+    <tr>
+      <td><code>connectedCallback</code></td>
+<td>Вызывается каждый раз когда элемент добавляется в DOM. Полезна для
+запуска кода установки, такого как получение ресурсов или отрисовки. Как
+правило, вы должны попытаться отложить работу до этого времени.</td>
+    </tr>
+    <tr>
+      <td><code>disconnectedCallback</code></td>
+<td>Вызывается каждый раз когда элемент удаляется из DOM. Удобна для
+запуска кода очистки.</td>
+    </tr>
+    <tr>
+      <td><code>attributeChangedCallback(attrName, oldVal, newVal)</code></td>
+<td>Вызывается когда <a href="#attrchanges">наблюдаемый атрибут</a> был
+добавлен, удалён, обновлён или заменён. Так же вызывается для установки
+значений, когда элемент создаётся парсером, или <a href="#upgrades">улучшается
+(upgrade)</a>.
+<b>Примечание:</b> только атрибуты из списка в свойстве
+<code>observedAttributes</code> получат этот вызов.</td>
+    </tr>
+    <tr>
+      <td><code>adoptedCallback</code></td>
+<td>Пользовательский элемент был перемещён в новый <code>document</code>
+(например кто-то вызвал <code>document.adoptNode(el)</code>)</td>
+    </tr>
+  </tbody>
+</table>
+
+Обратите внимание: Браузер вызывает `attributeChangedCallback()` при изменении
+значений любого атрибут, указанный в массиве `observedAttributes` (обратитесь к
+разделу, посвященное [Отслеживание изменений значений атрибутов](#attrchanges)
+). По сути, это делается для оптимизации производительности. Когда пользователи
+изменяют общие свойства, например, `style` или `class`, вы наверняка не захотите
+получить тонны вызовов этих функций.
+
+**Вызовы реакций синхронны**. Если кто-либо вызывает `el.setAttribute()` для
+вашего элемента, то браузер тут же вызывает `attributeChangedCallback()`.
+Подобным образом будет вызван `disconnectedCallback()` сразу после того, как ваш
+элемент удален из DOM (например пользователь вызвал `el.remove()`).
+
+**Пример**: добавление реакций пользовательского элемента для `<app-drawer>`:
+
+```
+class AppDrawer extends HTMLElement {
+  constructor() {
+    super(); // always call super() first in the constructor.
+    ...
+  }
+  connectedCallback() {
+    ...
+  }
+  disconnectedCallback() {
+    ...
+  }
+  attributeChangedCallback(attrName, oldVal, newVal) {
+    ...
+  }
+}
+```
+
+Определяйте реакции, если/когда это имеет смысл. Если ваш элемент достаточно
+сложен и выполняет подключение к IndexedDB в `connectedCallback()`, то выполните
+необходимый для завершения работы с элементом код в disconnectedCallback(). Но
+будьте бдительны! Вы не можете полагаться во всех ситуациях исключительно на
+код, выполняемый при удалении элемента из DOM. Например,
+`disconnectedCallback()` никогда не будет вызван при закрытии пользователем
+вкладки.
+
+## Свойства и атрибуты
+
+### Преобразование значений свойств в значения атрибутов HTML {: #reflectattr}
+
+Преобразование значения свойств HTML обратно в DOM в значение атрибута HTML –
+обычное дело. Например, когда значения `hidden` или `id` изменяются в коде JS:
+
+```
+div.id = 'my-id';
+div.hidden = true;
+```
+
+значения применяются к существущему DOM в качестве атрибутов:
+
+```
+<div id="my-id" hidden>
+```
+
+Это явление называется «[преобразование значений свойств в значения
+атрибутов](https://html.spec.whatwg.org/multipage/infrastructure.html#reflecting-content-attributes-in-idl-attributes)».
+Почти все свойства в HTML способны на это. Почему? Атрибуты также полезны для
+декларативного конфигурирования элемента, и некоторые API, такие как API для
+обеспечения доступности пользовательского интерфейса или API для работы с
+селекторами CSS, в своей работе полагаются на атрибуты.
+
+Преобразование значений свойств полезно везде, где вы хотите **синхронизировать
+представление элемента в DOM с его состоянием в коде JavaScript**. Одна из
+причин, по которой вам могло бы захотеться преобразовать значение свойства – то,
+что благодаря этому определенные пользователем стилевые правила применяются при
+изменении состояния элемента в коде JavaScript.
+
+Вспомните наш `<app-drawer>`. Разработчик, который использует этот компонент,
+может захотеть, чтобы он постепенно исчез, и/или предотвратить взаимодействие
+пользователя при блокировке доступа к элементу:
+
+```
+app-drawer[disabled] {
+  opacity: 0.5;
+  pointer-events: none;
+}
+```
+
+При изменении в коде JS значения свойства `disabled` мы хотим, чтобы этот
+атрибут был добавлен в DOM, за счет чего были применены правила определённого
+пользователем селектора. Такое поведение может быть обеспечено за счет
+преобразования значений свойств в значение атрибута c тем же именем.
+
+```
+...
+
+get disabled() {
+  return this.hasAttribute('disabled');
+}
+
+set disabled(val) {
+  // Reflect the value of `disabled` as an attribute.
+  if (val) {
+    this.setAttribute('disabled', '');
+  } else {
+    this.removeAttribute('disabled');
+  }
+  this.toggleDrawer();
+}
+```
+
+### Отслеживание изменений значений атрибутов {: #attrchanges}
+
+HTML атрибуты это удобный для пользователей способ объявления начального
+состояния элемента:
+
+```
+<app-drawer open disabled></app-drawer>
+```
+
+Элементы могут отреагировать на изменения атрибутов за счет определения
+`attributeChangedCallback`. Браузер вызовет этот метод при изменении любых
+значений атрибутов, перечисленных в массиве `observedAttributes`.
+
+```
+class AppDrawer extends HTMLElement {
+  ...
+
+  static get observedAttributes() {
+    return ['disabled', 'open'];
+  }
+
+  get disabled() {
+    return this.hasAttribute('disabled');
+  }
+
+  set disabled(val) {
+    if (val) {
+      this.setAttribute('disabled', '');
+    } else {
+      this.removeAttribute('disabled');
+    }
+  }
+
+  // Only called for the disabled and open attributes due to observedAttributes
+  attributeChangedCallback(name, oldValue, newValue) {
+    // When the drawer is disabled, update keyboard/screen reader behavior.
+    if (this.disabled) {
+      this.setAttribute('tabindex', '-1');
+      this.setAttribute('aria-disabled', 'true');
+    } else {
+      this.setAttribute('tabindex', '0');
+      this.setAttribute('aria-disabled', 'false');
+    }
+    // TODO: also react to the open attribute changing.
+  }
+}
+```
+
+В этом примере мы задаем значения дополнительных атрибутов для `<app-drawer>`
+при изменении значения атрибута `disabled`. Хотя мы этого здесь не делаем, вы
+могли бы также **использовать `attributeChangedCallback` для синхронизации
+свойства элемента в JS с его атрибутом**.
+
+## Обновление элемента {: #upgrades}
+
+### Прогрессивно улучшенный HTML
+
+Мы уже узнали, что пользовательские элементы определяются при помощи вызова
+`customElements.define()`. Однако это не означает, что вы должны определить +
+зарегистрировать пользовательский элемент сразу.
+
+**Пользовательские элементы могут быть использованы *до* регистрации их
+определения** .
+
+Прогрессивное улучшение – возможность пользовательских элементов. Другими
+словами, вы можете объявить ряд элементов `<app-drawer>` на странице и вызвать
+`customElements.define('app-drawer', ...)` намного позже. Это так, поскольку
+браузер обрабатывает потенциальные пользовательские элементы иначе благодаря
+из-за возможности добавления тэгов с [неизвестными именами](#unknown). Процесс
+вызова `define()` и наделения существующего элемента определением класса
+называется «обновления элемента».
+
+Чтобы узнать, когда имя тэга становится определённым, вы можете использовать
+`window.customElements.whenDefined()`. Метод вернёт Promise, который выполнится
+когда элемент определится.
+
+```
+customElements.whenDefined('app-drawer').then(() => {
+  console.log('app-drawer defined');
+});
+```
+
+**Пример**: отложение выполнение кода до обновления дочерних элементов
+
+```
+<share-buttons>
+  <social-button type="twitter"><a href="...">Twitter</a></social-button>
+  <social-button type="fb"><a href="...">Facebook</a></social-button>
+  <social-button type="plus"><a href="...">G+</a></social-button>
+</share-buttons>
+
+
+
+// Fetch all the children of <share-buttons> that are not defined yet.
+let undefinedButtons = buttons.querySelectorAll(':not(:defined)');
+
+let promises = [...undefinedButtons].map(socialButton => {
+  return customElements.whenDefined(socialButton.localName);
+));
+
+// Wait for all the social-buttons to be upgraded.
+Promise.all(promises).then(() => {
+  // All social-button children are ready.
+});
+```
+
+Примечание: Я думаю о пользовательских элементах как о пребывающих в лимбе пока
+они не определены.
+[Спецификация](https://dom.spec.whatwg.org/#concept-element-custom-element-state)
+определяет состояние элемента как `undefined`, `uncustomized`, или `custom`.
+Встроенные элементы, такие как `<div>` всегда `defined`.
+
+## Контент, определенный в элементе {: #addingmarkup}
+
+Контентом пользовательских элементов можно управлять за счет использования API
+DOM в коде элемента. При этом нам оказываются полезными [реакции](#reactions).
+
+**Пример** : создание элемента с некоторым HTML-кодом по умолчанию:
+
+```
+customElements.define('x-foo-with-markup', class extends HTMLElement {
+  connectedCallback() {
+    this.innerHTML = "<b>I'm an x-foo-with-markup!</b>";
+  }
+  ...
+});
+```
+
+При объявлении этого тэга получим:
+
+```
+<x-foo-with-markup>
+ <b>I'm an x-foo-with-markup!</b>
+</x-foo-with-markup>
+```
+
+{% framebox height="100px" %}
+
+<style>
+  .demoarea {
+    padding: 8px; border: 1px dashed #ccc;
+  }
+  .demoarea::before {
+    display: block; content: 'DEMO';
+  }
+</style>
+
+<div class="demoarea">
+  <x-foo-with-markup></x-foo-with-markup>
+</div>
+
+<script>
+  const supportsCustomElementsV1 = 'customElements' in window;
+
+  if(supportsCustomElementsV1) {
+    customElements.define('x-foo-with-markup', class extends HTMLElement {
+      connectedCallback() {
+        this.innerHTML = "<b>I'm an x-foo-with-markup!</b>";
+      }
+    });
+  } else {
+    if (self.frameElement) {
+      self.frameElement.style.display = 'none';
+    }
+  }
+</script>
+
+{% endframebox %}
+
+Обратите внимание: Перезаписывание дочерних элементов компонента новым контентом
+обычно не является удачной идеей, поскольку это неожиданно. Пользователи будут
+удивлены, что их разметка удалена. Более удачный вариант добавления контента,
+определенного в элементе, – использование Shadow DOM, что мы далее и рассмотрим.
+
+### Создание элемента, в котором используется Shadow DOM {: #shadowdom}
+
+Обратите внимание: я не буду рассматривать возможности [Shadow
+DOM](http://w3c.github.io/webcomponents/spec/shadow/) в этом руководстве, но
+скажу, что это мощный API для совместного использования с пользовательскими
+элементами. Сама по себе технология Shadow DOM – инструмент для создания дерева
+узлов. При использовании этой технологии совместно с пользовательскими
+элементами получается потрясающий результат.
+
+За счет Shadow DOM в элементе можно хранить, отображать фрагмент DOM, который
+существует отдельно от остальных элементов страницы, и задавать для него
+стилевое оформление. Знаете, да вы могли бы даже поместить целое приложение в
+единственный элемент:
+
+```
+<!-- chat-app's implementation details are hidden away in Shadow DOM. -->
+<chat-app></chat-app>
+```
+
+Для того чтобы использовать Shadow DOM в пользовательском элементе, вызовите
+`this.attachShadow` внутри вашего `constructor`:
+
+```
+let tmpl = document.createElement('template');
+tmpl.innerHTML = `
+  <style>:host { ... }</style> <!-- look ma, scoped styles -->
+  <b>I'm in shadow dom!</b>
+  <slot></slot>
+`;
+
+customElements.define('x-foo-shadowdom', class extends HTMLElement {
+  constructor() {
+    super(); // always call super() first in the constructor.
+
+    // Attach a shadow root to the element.
+    let shadowRoot = this.attachShadow({mode: 'open'});
+    shadowRoot.appendChild(tmpl.content.cloneNode(true));
+  }
+  ...
+});
+```
+
+Обратите внимание: В примере выше мы используем `template` для клонирования DOM,
+а не `shadowRoot` `innerHTML` `shadowRoot` . Благодаря этому маневру сокращается
+время, используемое для парсинга HTML, поскольку контент шаблона подвергается
+парсингу только один раз, в то время как при вызове `innerHTML` для `shadowRoot`
+парсинг HTML будет выполняться при добавлении каждого образца элемента. Мы
+поговорим подробнее о шаблонах в следующем разделе.
+
+Пример использования:
+
+```
+<x-foo-shadowdom>
+  <p><b>User's</b> custom text</p>
+</x-foo-shadowdom>
+
+<!-- renders as -->
+<x-foo-shadowdom>
+  #shadow-root
+    <b>I'm in shadow dom!</b>
+    <slot></slot> <!-- slotted content appears here -->
+</x-foo-shadowdom>
+```
+
+{% framebox height="142px" %}
+
+<style>
+  .demoarea {
+    padding: 8px; border: 1px dashed #ccc;
+  }
+
+  .demoarea::before {
+    content: 'DEMO'; display: block;
+  }
+</style>
+
+<div class="demoarea">
+  <x-foo-shadowdom>
+    <p><b>User's</b> custom text</p>
+  </x-foo-shadowdom>
+</div>
+
+<script>
+  const supportsCustomElementsV1 = 'customElements' in window;
+
+  if(supportsCustomElementsV1) {
+    let tmpl = document.createElement('template');
+    tmpl.innerHTML = `
+      <b>I'm in shadow dom!</b>
+      <slot></slot>
+    `;
+
+    customElements.define('x-foo-shadowdom', class extends HTMLElement {
+      constructor() {
+        super(); // always call super() first in the constructor.
+        let shadowRoot = this.attachShadow({mode: 'open'});
+        shadowRoot.appendChild(tmpl.content.cloneNode(true));
+      }
+    });
+  } else {
+    if (self.frameElement) {
+      self.frameElement.style.display = 'none';
+    }
+  }
+</script>
+
+{% endframebox %}
+
+### Создание элементов из `<template>` {: #fromtemplate}
+
+Для тех, кто не знаком, [ элемент
+`<template>`](https://html.spec.whatwg.org/multipage/scripting.html#the-template-element)
+позволяет Вам объявлять фрагменты DOM, которые анализируются, инертны при
+загрузке страницы и могут быть активированы позже во время выполнения. Это
+другой API примитив в семействе веб-компонентов. **Шаблоны являются идеальным
+заполнителем для объявления структуры пользовательского элемента.**
+
+**Пример** : регистрация элемента с контентом Shadow DOM, созданным из
+`<template>` :
+
+```
+<template id="x-foo-from-template">
+  <style>
+    p { color: green; }
+  </style>
+  <p>I'm in Shadow DOM. My markup was stamped from a &lt;template&gt;.</p>
+</template>
+
+<script>
+  let tmpl = document.querySelector('#x-foo-from-template');
+  // If your code is inside of an HTML Import you'll need to change the above line to:
+  // let tmpl = document.currentScript.ownerDocument.querySelector('#x-foo-from-template');
+
+  customElements.define('x-foo-from-template', class extends HTMLElement {
+    constructor() {
+      super(); // always call super() first in the constructor.
+      let shadowRoot = this.attachShadow({mode: 'open'});
+      shadowRoot.appendChild(tmpl.content.cloneNode(true));
+    }
+    ...
+  });
+</script>
+```
+
+За счет этих нескольких строк кода многое происходит. Давайте рассмотрим
+ключевые моменты:
+
+1. Мы определяем новый элемент в HTML - `<x-foo-from-template>`
+2. Создаем Shadow DOM элемента из `<template>`
+3. DOM элемента локален для элемента благодаря Shadow DOM
+4. Внутренний CSS элемента ограничен самим элементом благодаря Shadow DOM
+
+{% framebox height="120px" %}
+
+<style>
+.demoarea {
+  padding: 8px; border: 1px dashed #ccc;
+}
+
+.demoarea::before {
+  content: 'DEMO'; display: block;
+}
+</style>
+
+<div class="demoarea">
+  <x-foo-from-template></x-foo-from-template>
+</div>
+
+<template id="x-foo-from-template">
+  <style>:host p { color: green; }</style>
+  <p>I'm in Shadow DOM. My markup was stamped from a <template>.</p>
+</template>
+
+<script>
+  const supportsCustomElementsV1 = 'customElements' in window;
+
+  if(supportsCustomElementsV1) {
+    customElements.define('x-foo-from-template', class extends HTMLElement {
+      constructor() {
+        super();
+        let shadowRoot = this.attachShadow({mode: 'open'});
+        const t = document.querySelector('#x-foo-from-template');
+        shadowRoot.appendChild(t.content.cloneNode(true));
+      }
+    });
+  } else {
+    if (self.frameElement) {
+      self.frameElement.style.display = 'none';
+    }
+  }
+</script>
+
+{% endframebox %}
+
+## Добавление стилевого оформления для пользовательского элемента {: #styling}
+
+Даже если бы такое оформление для элемента задано в нем самом при помощи Shadow
+DOM, пользователи могут добавить для вашего пользовательского элемента свое
+стилевое оформление. Эти стилевые правила называются «стилевые правила, заданные
+пользователем».
+
+```
+<!-- user-defined styling -->
+<style>
+  app-drawer {
+    display: flex;
+  }
+  panel-item {
+    transition: opacity 400ms ease-in-out;
+    opacity: 0.3;
+    flex: 1;
+    text-align: center;
+    border-radius: 50%;
+  }
+  panel-item:hover {
+    opacity: 1.0;
+    background: rgb(255, 0, 255);
+    color: white;
+  }
+  app-panel > panel-item {
+    padding: 5px;
+    list-style: none;
+    margin: 0 7px;
+  }
+</style>
+
+<app-drawer>
+  <panel-item>Do</panel-item>
+  <panel-item>Re</panel-item>
+  <panel-item>Mi</panel-item>
+</app-drawer>
+```
+
+Вы могли бы задать себе вопрос, как работает специфичность CSS, если для
+элемента добавлено стилевое оформление внутри Shadow DOM. С точки зрения
+специфичности стилевые правила, заданные пользователем, имеют преимущество. Они
+всегда переопределяют стилевые правила, заданные в самом элементе. Обратитесь к
+разделу «[Создание элемента, в котором используется Shadow
+DOM](#shadowdom)[».](#shadowdom)
+
+### Добавление соответствующего стиля для незарегистрированного элемента {: #prestyle}
+
+До [обновления](#upgrades) элемента вы можете выбрать его в CSS при помощи
+псевдокласса `:defined`. Это полезно при добавлении предварительного стилевого
+оформления для компонента. Например: вы можете захотеть предотвратить FOUC (*
+Flash of unstyled content – появление контента без стилевого оформления) за счет
+скрытия неопределенных компонентов и их постепенного проявления после их
+определения.
+
+**Пример** : скрываем `<app-drawer>` drawer `<app-drawer>` до его определения:
+
+```
+app-drawer:not(:defined) {
+  /* Pre-style, give layout, replicate app-drawer's eventual styles, etc. */
+  display: inline-block;
+  height: 100vh;
+  opacity: 0;
+  transition: opacity 0.3s ease-in-out;
+}
+```
+
+После того, как `<app-drawer>` определен, селектор ( `app-drawer:not(:defined)`)
+для него более не подходит.
+
+## Расширение возможностей элементов {: #extend}
+
+Пользовательские элементы API полезны для создания новых элементов HTML, однако
+он также полезен для расширения возможностей других пользовательских элементов
+или даже встроенных в браузера элементов HTML.
+
+### Расширение возможностей пользовательского элемента {: #extendcustomeel}
+
+Расширение возможностей другого пользовательского элемента осуществляется за
+счет унаследования определения его класса.
+
+**Пример** : создание `<fancy-app-drawer>` , в котором расширяются возможности
+`<app-drawer>`:
+
+```
+class FancyDrawer extends AppDrawer {
+  constructor() {
+    super(); // always call super() first in the constructor. This also calls the extended class' constructor.
+    ...
+  }
+
+  toggleDrawer() {
+    // Possibly different toggle implementation?
+    // Use ES2015 if you need to call the parent method.
+    // super.toggleDrawer()
+  }
+
+  anotherMethod() {
+    ...
+  }
+}
+
+customElements.define('fancy-app-drawer', FancyDrawer);
+```
+
+### Расширение возможностей собственных элементов HTML {: #extendhtml}
+
+Давайте предположим, что вы хотели бы создать более изящный элемент `<button>`.
+Вместо копирования поведения и функциональных возможностей `<button>` более
+удачный вариант – прогрессивное улучшение существующего элемента при помощи
+пользовательских элементов.
+
+**Настроенный встроенный элемент** - пользовательский элемент, который наследует
+возможности встроенных в браузер тэгов HTML. Основное преимущество наследования
+существующего элемента - получение всех его возможностей (свойств DOM, методов,
+доступности пользовательского интерфейса). Нет лучшего способа написания
+[прогрессивных веб-приложений](/web/progressive-web-apps/), чем **прогрессивное
+улучшение существующих HTML-элементов**.
+
+Обратите внимание: Только в Chrome 67 сейчас имеется поддержка встроенных
+элементов ( [статус](https://www.chromestatus.com/feature/4670146924773376) ). В
+Edge и Firefox будет реализована их поддержка, однако в Safari - нет. Из-за
+этого может пострадать доступность пользовательского интерфейса и возможность
+осуществления прогрессивного улучшения. Если вы считаете, что расширение
+возможностей встроенных элементов HTML полезно, выскажитесь на Github (проблемы
+<a href="https://github.com/w3c/webcomponents/issues/509">509</a> и <a
+href="https://github.com/w3c/webcomponents/issues/662">662</a> ).
+
+Для того чтобы получить возможность элемента, вам необходимо будет создать
+определение класса, которое наследует возможности от подходящего интерфейса DOM.
+Например, пользовательский элемент, который расширяет возможности `<button>` ,
+должен наследовать характеристики `HTMLButtonElement` , а не `HTMLElement` .
+Подобным образом элемент, расширяющий возможности `<img>` , должен наследовать
+характеристики `HTMLImageElement` .
+
+**Пример**: расширение возможностей `<button>`:
+
+```
+// See https://html.spec.whatwg.org/multipage/indices.html#element-interfaces
+// for the list of other DOM interfaces.
+class FancyButton extends HTMLButtonElement {
+  constructor() {
+    super(); // always call super() first in the constructor.
+    this.addEventListener('click', e => this.drawRipple(e.offsetX, e.offsetY));
+  }
+
+  // Material design ripple animation.
+  drawRipple(x, y) {
+    let div = document.createElement('div');
+    div.classList.add('ripple');
+    this.appendChild(div);
+    div.style.top = `${y - div.clientHeight/2}px`;
+    div.style.left = `${x - div.clientWidth/2}px`;
+    div.style.backgroundColor = 'currentColor';
+    div.classList.add('run');
+    div.addEventListener('transitionend', e => div.remove());
+  }
+}
+
+customElements.define('fancy-button', FancyButton, {extends: 'button'});
+```
+
+Обратите внимание, что при расширении возможностей встроенного элемента вызов
+`define()` происходит немного по-другому. За счет обязательного третьего уровня
+браузера сообщается, характеристики какого-то тэга вы наследуете. Это
+необходимо, поскольку для многих тэгов HTML используется один и тот же интерфейс
+DOM. Для элементов `<section>` , `<address>` и `<em>` (и многих других)
+используют `HTMLElement` ; для `<q>` и `<blockquote>` используется
+`HTMLQuoteElement` ; и т.д. За счет указания `{extends: 'blockquote'}` браузеру
+сообщается, что вы создаете `<blockquote>`, а не `<q>`. Обратитесь к
+[спецификации](https://html.spec.whatwg.org/multipage/indices.html#element-interfaces)
+HTML для ознакомления со всеми списком интерфейсов DOM HTML.
+
+Обратите внимание: за счет наследования характеристик `HTMLButtonElement` наша
+изящная кнопка наделяется всеми свойствами/методами DOM `<button>`. В результате
+отпадает необходимость реализации кучи возможностей: свойства `disabled`, метода
+`click()`, обработчиков для события `keydown` и настроки `tabindex`. Вместо
+этого мы можем сфокусироваться на прогрессивном улучшении `<button>` за счет
+добавления собственных возможностей, а именно метода `drawRipple()`. В итоге мы
+пишем меньше кода и чаще используем тот же самый.
+
+Пользователи настроенного встроенного элемента могут его использовать
+несколькими способами. Они могут объявить его за счет добавления `is=""` атрибут
+во встроенном тэге:
+
+```
+<!-- This <button> is a fancy button. -->
+<button is="fancy-button" disabled>Fancy button!</button>
+```
+
+создать образец в JavaScript:
+
+```
+// Custom elements overload createElement() to support the is="" attribute.
+let button = document.createElement('button', {is: 'fancy-button'});
+button.textContent = 'Fancy button!';
+button.disabled = true;
+document.body.appendChild(button);
+```
+
+или воспользоваться оператором `new` :
+
+```
+let button = new FancyButton();
+button.textContent = 'Fancy button!';
+button.disabled = true;
+```
+
+Вот еще пример, в котором расширяются возможности `<img>` .
+
+**Пример** : расширение возможностей `<img>` :
+
+```
+customElements.define('bigger-img', class extends Image {
+  // Give img default size if users don't specify.
+  constructor(width=50, height=50) {
+    super(width * 10, height * 10);
+  }
+}, {extends: 'img'});
+```
+
+Пользователи могут объявить этот элемент так:
+
+```
+<!-- This <img> is a bigger img. -->
+<img is="bigger-img" width="15" height="20">
+```
+
+или создать его образец в JavaScript:
+
+```
+const BiggerImage = customElements.get('bigger-img');
+const image = new BiggerImage(15, 20); // pass constructor values like so.
+console.assert(image.width === 150);
+console.assert(image.height === 200);
+```
+
+## Дополнительные моменты {: #details}
+
+### Сравнение неизвестных элементов с незарегистрированными пользовательскими элементами {: #unknown}
+
+HTML - нестрогая и гибкая в работе технология. Например, объявление на странице
+`<randomtagthatdoesntexist>` , и браузер успешно воспримет его. Почему элементы
+с нестандартными именами работают? Потому что [спецификация
+HTML](https://html.spec.whatwg.org/multipage/dom.html#htmlunknownelement)
+позволяет их использовать. После парсинга не определенные в спецификации
+элементов получается `HTMLUnknownElement` .
+
+В случае с пользовательскими элементами дело обстоит иначе. В результате
+парсинга потенциальных пользовательских элементов получается `HTMLElement` ,
+если при создании для них были указаны корректные имена (с "-"). Вы можете в
+этом удостовериться в браузере, который поддерживает пользовательские элементы.
+Запустите консоль при помощи комбинации pan1} Ctrl + <span
+class="kbd">Shift</span> + <span class="kbd">J</span> (или <span
+class="kbd">Cmd</span> + <span class="kbd">Opt</span> + <span
+class="kbd">J</span> для Mac) и в следующий код:
+
+```
+// "tabs" is not a valid custom element name
+document.createElement('tabs') instanceof HTMLUnknownElement === true
+
+// "x-tabs" is a valid custom element name
+document.createElement('x-tabs') instanceof HTMLElement === true
+```
+
+## Список API
+
+Свойство `customElements` глобального объекта window имеет полезные методы для
+работы с пользовательскими элементами.
+
+**`define (tagName, конструктор, параметры)`**
+
+Используется для определения пользовательского элемента в браузере.
+
+Пример:
+
+```
+customElements.define('my-app', class extends HTMLElement { ... });
+customElements.define(
+  'fancy-button', class extends HTMLButtonElement { ... }, {extends: 'button'});
+```
+
+**`get(tagName)`**
+
+При передаче ему в качестве допустимого имени тэга пользовательского элемента
+возвращает конструктор элемента. Возвращает `undefined` , если не было
+зарегистрировано ни одного определения элемента для переданного имени.
+
+Пример:
+
+```
+let Drawer = customElements.get('app-drawer');
+let drawer = new Drawer();
+```
+
+**`whenDefined(tagName)`**
+
+Возвращает Promise, который выполнится, когда пользовательский элемент
+определится. Если элемент уже определён, то выполнится немедленно. Но будет
+отклонён, если имя тэга не соответствует требованиям для имени пользовательского
+элемента.
+
+Пример:
+
+```
+customElements.whenDefined('app-drawer').then(() => {
+  console.log('ready!');
+});
+```
+
+## История версий и поддержка браузерами {: #historysupport}
+
+Если вы следили за Web Components последние несколько лет, то знаете, что в
+версии Chrome 36+ реализована версия API Custom Elements, в которой вместо
+`document.registerElement()` используется `customElements.define()` . Эта версия
+(v0) стандарта сейчас рассматривается как устаревшая. `customElements.define()`
+- новый востребованный метод, который поставщики браузеров начинают
+реализовывать. Она называется Custom Elements v1.
+
+Если вы, вдруг, заинтересованы в спецификации более ранней версии (v0), то
+ознакомьтесь со своим товаром [html5rocks
+article](http://www.html5rocks.com/en/tutorials/webcomponents/customelements/)
+{: .external}.
+
+### Поддержка браузерами
+
+Chrome 54 ([status](https://www.chromestatus.com/features/4696261944934400)),
+Safari 10.1 ([status](https://webkit.org/status/#feature-custom-elements)), и 
+Firefox 63 ([status](https://platform-status.mozilla.org/#custom-elements))
+поддерживают Custom Elements v1. Для Edge [началась разработка
+прототипа](https://developer.microsoft.com/microsoft-edge/platform/status/customelements/)
+
+Для того чтобы определить, поддерживает ли браузер создание пользовательских
+элементов, проверьте наличие `window.customElements` :
+
+```
+const supportsCustomElementsV1 = 'customElements' in window;
+```
+
+#### Полифилл {: #polyfill}
+
+Пока во многих браузерах не реализована поддержка этой возможности, вы можете
+использовать [отдельный
+полифилл](https://github.com/webcomponents/custom-elements/) для добавления
+поддержки пользовательских элементов v1. Однако, мы рекомендуем использовать
+[загрузчик
+webcomponents.js](https://github.com/webcomponents/webcomponentsjs#using-webcomponents-loaderjs)
+для оптимальной загрузки полифиллов веб-компонентов. Загрузчик проверяет
+поддержку возможностей для асинхронной загрузки только необходимых для браузера
+полифиллов.
+
+Обратите внимание: если выполняется транспиляция вашего проекта или в нем
+используется ES5, то обязательно ознакомьтесь с советами по добавлению
+[custom-elements-es5-adapter.js](https://github.com/webcomponents/webcomponentsjs#custom-elements-es5-adapterjs)
+в дополнение к полифиллам.
+
+Установите его при помощи команды:
+
+```
+npm install --save @webcomponents/webcomponentsjs
+```
+
+Использование:
+
+```
+<!-- Use the custom element on the page. -->
+<my-element></my-element>
+
+<!-- Load polyfills; note that "loader" will load these async -->
+<script src="node_modules/@webcomponents/webcomponentsjs/webcomponents-loader.js" defer></script>
+
+<!-- Load a custom element definitions in `waitFor` and return a promise -->
+<script type="module"> 
+  function loadScript(src) {
+    return new Promise(function(resolve, reject) {
+      const script = document.createElement('script');
+      script.src = src;
+      script.onload = resolve;
+      script.onerror = reject;
+      document.head.appendChild(script);
+    });
+  }
+
+  WebComponents.waitFor(() => {
+    // At this point we are guaranteed that all required polyfills have
+    // loaded, and can use web components APIs.
+    // Next, load element definitions that call `customElements.define`.
+    // Note: returning a promise causes the custom elements
+    // polyfill to wait until all definitions are loaded and then upgrade
+    // the document in one batch, for better performance.
+    return loadScript('my-element.js');
+  });
+</script>
+```
+
+Обратите внимание: нельзя добавить поддержку псевдокласса CSS `:defined` .
+
+## Заключение
+
+Пользовательские элементы предоставили нам новый инструмент для определения
+новых тэгов HTML в браузере и создании компонентов для повторного использования.
+При совместном использовании пользовательских элементов с другими новыми
+примитивами платформы как Shadow DOM и `<template>` мы начинаем понимать общую
+картину Веб-компонентов:
+
+- Кросс-браузерная технология (веб-стандарт) для создания и расширения
+переиспользуемых компонентов.
+- Для того, чтобы начать работать с этой технологией, не нужно подключать
+никаких библиотек или фреймворков. Чистый код JS / HTML / CSS - все, что
+необходимо для победы!
+- Предоставляет знакомую модель программирования. Только DOM / CSS / HTML.
+- Хорошо совместимы с другими новыми свойствами платформы (Shadow DOM,
+`<template>` , пользовательские свойства CSS и т.д.).
+- Тесно интегрирована с DevTools браузера.
+- Пользуется существующими доступностью пользовательского интерфейса.
+
+## Замечания и предложения {: #inline-feedback }
+
+{% include "web/_shared/helpful.html" %}

From 2459f8d88b9b3582079474eb5c7783d5047e37f4 Mon Sep 17 00:00:00 2001
From: Ilya Spiridonov <spiridonov.ilya@gmail.com>
Date: Tue, 7 Jan 2020 19:11:24 +0000
Subject: [PATCH 2/4] Translate customelements.md via GitLocalize

---
 src/content/ru/fundamentals/web-components/customelements.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/content/ru/fundamentals/web-components/customelements.md b/src/content/ru/fundamentals/web-components/customelements.md
index d40ca2b3f19..3c1e317d196 100644
--- a/src/content/ru/fundamentals/web-components/customelements.md
+++ b/src/content/ru/fundamentals/web-components/customelements.md
@@ -1,7 +1,7 @@
 project_path: "/web/fundamentals/_project.yaml"
 book_path: "/web/fundamentals/_book.yaml"
-description: Custom elements allow web developers to define new HTML tags, extend
-  existing ones, and create reusable web components.
+description: Пользовательские элементы позволяют разработчикам определять новые HTML
+  тэги, расширять существующие, а также создавать повторно используемые веб-компоненты.
 
 {# wf_updated_on: 2018-09-20 #}
 {# wf_published_on: 2016-06-28 #}

From 28e01e42d1fd375af45e46aa95dc61fe43fdf037 Mon Sep 17 00:00:00 2001
From: dmitryskripunov <dskripunov@gmail.com>
Date: Thu, 9 Jan 2020 05:40:46 +0000
Subject: [PATCH 3/4] Translate customelements.md via GitLocalize


From f136b9add43e4f4fbcb74952b838ddcd1edbf39b Mon Sep 17 00:00:00 2001
From: dmitryskripunov <dskripunov@gmail.com>
Date: Thu, 9 Jan 2020 05:44:16 +0000
Subject: [PATCH 4/4] Translate customelements.md via GitLocalize

---
 src/content/ru/fundamentals/web-components/customelements.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/content/ru/fundamentals/web-components/customelements.md b/src/content/ru/fundamentals/web-components/customelements.md
index 3c1e317d196..6b418198375 100644
--- a/src/content/ru/fundamentals/web-components/customelements.md
+++ b/src/content/ru/fundamentals/web-components/customelements.md
@@ -830,7 +830,7 @@ customElements.define('fancy-app-drawer', FancyDrawer);
 Edge и Firefox будет реализована их поддержка, однако в Safari - нет. Из-за
 этого может пострадать доступность пользовательского интерфейса и возможность
 осуществления прогрессивного улучшения. Если вы считаете, что расширение
-возможностей встроенных элементов HTML полезно, выскажитесь на Github (проблемы
+возможностей встроенных элементов HTML полезно, выскажитесь на GitHub (проблемы
 <a href="https://github.com/w3c/webcomponents/issues/509">509</a> и <a
 href="https://github.com/w3c/webcomponents/issues/662">662</a> ).
 
@@ -1091,7 +1091,7 @@ npm install --save @webcomponents/webcomponentsjs
 
   WebComponents.waitFor(() => {
     // At this point we are guaranteed that all required polyfills have
-    // loaded, and can use web components APIs.
+    // loaded, and can use Web Components APIs.
     // Next, load element definitions that call `customElements.define`.
     // Note: returning a promise causes the custom elements
     // polyfill to wait until all definitions are loaded and then upgrade