diff --git a/1-js/2-first-steps/17-function-basics/style.png b/1-js/2-first-steps/17-function-basics/style.png deleted file mode 100644 index f61d2f804..000000000 Binary files a/1-js/2-first-steps/17-function-basics/style.png and /dev/null differ diff --git a/1-js/5-functions-closures/2-closures/1.png b/1-js/5-functions-closures/2-closures/1.png deleted file mode 100755 index 737bb3c5b..000000000 Binary files a/1-js/5-functions-closures/2-closures/1.png and /dev/null differ diff --git a/1-js/5-functions-closures/2-closures/1@2x.png b/1-js/5-functions-closures/2-closures/1@2x.png deleted file mode 100755 index 43c32b228..000000000 Binary files a/1-js/5-functions-closures/2-closures/1@2x.png and /dev/null differ diff --git a/1-js/5-functions-closures/2-closures/2.png b/1-js/5-functions-closures/2-closures/2.png deleted file mode 100755 index d86914553..000000000 Binary files a/1-js/5-functions-closures/2-closures/2.png and /dev/null differ diff --git a/1-js/5-functions-closures/2-closures/2@2x.png b/1-js/5-functions-closures/2-closures/2@2x.png deleted file mode 100755 index b47c9e448..000000000 Binary files a/1-js/5-functions-closures/2-closures/2@2x.png and /dev/null differ diff --git a/1-js/5-functions-closures/2-closures/3.png b/1-js/5-functions-closures/2-closures/3.png deleted file mode 100755 index ec0326e8f..000000000 Binary files a/1-js/5-functions-closures/2-closures/3.png and /dev/null differ diff --git a/1-js/5-functions-closures/2-closures/3@2x.png b/1-js/5-functions-closures/2-closures/3@2x.png deleted file mode 100755 index 14b1f1db8..000000000 Binary files a/1-js/5-functions-closures/2-closures/3@2x.png and /dev/null differ diff --git a/1-js/5-functions-closures/2-closures/4.png b/1-js/5-functions-closures/2-closures/4.png deleted file mode 100755 index 71591c2f0..000000000 Binary files a/1-js/5-functions-closures/2-closures/4.png and /dev/null differ diff --git a/1-js/5-functions-closures/2-closures/4@2x.png b/1-js/5-functions-closures/2-closures/4@2x.png deleted file mode 100755 index 5c89966c3..000000000 Binary files a/1-js/5-functions-closures/2-closures/4@2x.png and /dev/null differ diff --git a/1-js/5-functions-closures/2-closures/5.png b/1-js/5-functions-closures/2-closures/5.png deleted file mode 100755 index a1bad61ce..000000000 Binary files a/1-js/5-functions-closures/2-closures/5.png and /dev/null differ diff --git a/1-js/5-functions-closures/2-closures/5@2x.png b/1-js/5-functions-closures/2-closures/5@2x.png deleted file mode 100755 index 08bf3d2fe..000000000 Binary files a/1-js/5-functions-closures/2-closures/5@2x.png and /dev/null differ diff --git a/1-js/5-functions-closures/2-closures/6.png b/1-js/5-functions-closures/2-closures/6.png deleted file mode 100755 index 028308ffc..000000000 Binary files a/1-js/5-functions-closures/2-closures/6.png and /dev/null differ diff --git a/2-ui/1-document/15-metrics/clientWidthNoPadding.png b/2-ui/1-document/15-metrics/clientWidthNoPadding.png deleted file mode 100755 index a5d9f26b6..000000000 Binary files a/2-ui/1-document/15-metrics/clientWidthNoPadding.png and /dev/null differ diff --git a/2-ui/5-widgets/6-widget-tasks/6-slider-events/slider.png b/2-ui/5-widgets/5-custom-events/4-slider-events/slider.png old mode 100755 new mode 100644 similarity index 100% rename from 2-ui/5-widgets/6-widget-tasks/6-slider-events/slider.png rename to 2-ui/5-widgets/5-custom-events/4-slider-events/slider.png diff --git a/2-ui/5-widgets/6-widget-tasks/6-slider-events/solution.md b/2-ui/5-widgets/5-custom-events/4-slider-events/solution.md similarity index 72% rename from 2-ui/5-widgets/6-widget-tasks/6-slider-events/solution.md rename to 2-ui/5-widgets/5-custom-events/4-slider-events/solution.md index a51add2ae..94c33f0e4 100644 --- a/2-ui/5-widgets/6-widget-tasks/6-slider-events/solution.md +++ b/2-ui/5-widgets/5-custom-events/4-slider-events/solution.md @@ -1,4 +1,6 @@ +Для решения этой задачи достаточно создать две функции: `valueToPosition` будет получать по значению положение бегунка, а `positionToValue` -- наоборот, транслировать текущую координату бегунка в значение. + Как сопоставить позицию слайдера и значение? Для этого посмотрим крайние значения слайдера. Допустим, размер бегунка `10px`. @@ -17,10 +19,16 @@ pixelsPerValue = (sliderElem.clientWidth-thumbElem.clientWidth) / max; Используя `pixelsPerValue` мы сможем переводить позицию бегунка в значение и обратно. -Крайнее левое значение `thumbElem.style.left` равно нулю, крайнее правой -- как раз ширине доступной области `sliderElem.clientWidth - thumbElem.clientWidth`. Поэтому можно получив значение, поделив его на `pixelsPerValue`: +Крайнее левое значение `thumbElem.style.left` равно нулю, крайнее правой -- как раз ширине доступной области `sliderElem.clientWidth - thumbElem.clientWidth`. Поэтому можно получить значение слайдера, поделив его на `pixelsPerValue`: ```js -value = Math.round( newLeft / pixelsPerValue); +function positionToValue(left) { + return Math.round( left / pixelsPerValue); +} + +function valueToPosition(value) { + return pixelsPerValue * value; +} ``` diff --git a/2-ui/5-widgets/5-custom-events/4-slider-events/solution.view/index.html b/2-ui/5-widgets/5-custom-events/4-slider-events/solution.view/index.html new file mode 100644 index 000000000..c605ee284 --- /dev/null +++ b/2-ui/5-widgets/5-custom-events/4-slider-events/solution.view/index.html @@ -0,0 +1,44 @@ + + + + + + + + + + + +
+
+
+ + + Slide:  + Change:  + + + + + + + + + diff --git a/2-ui/5-widgets/5-custom-events/4-slider-events/solution.view/slider.css b/2-ui/5-widgets/5-custom-events/4-slider-events/solution.view/slider.css new file mode 100644 index 000000000..ab985b399 --- /dev/null +++ b/2-ui/5-widgets/5-custom-events/4-slider-events/solution.view/slider.css @@ -0,0 +1,19 @@ +.slider { + margin: 5px; + width: 310px; + height: 15px; + border-radius: 5px; + background: #E0E0E0; + background: -moz-linear-gradient(left top, #E0E0E0, #EEEEEE) repeat scroll 0 0 transparent; + background: -webkit-gradient(linear, left top, right bottom, from(#E0E0E0), to(#EEEEEE)); + background: linear-gradient(left top, #E0E0E0, #EEEEEE); +} +.thumb { + position: relative; + top: -5px; + width: 10px; + height: 25px; + border-radius: 3px; + background: blue; + cursor: pointer; +} \ No newline at end of file diff --git a/2-ui/5-widgets/5-custom-events/4-slider-events/solution.view/slider.js b/2-ui/5-widgets/5-custom-events/4-slider-events/solution.view/slider.js new file mode 100644 index 000000000..21fd7ebe0 --- /dev/null +++ b/2-ui/5-widgets/5-custom-events/4-slider-events/solution.view/slider.js @@ -0,0 +1,88 @@ +function Slider(options) { + var elem = options.elem; + + var thumbElem = elem.querySelector('.thumb'); + + var max = options.max || 100; + var sliderCoords, thumbCoords, shiftX, shiftY; + + // [<*>----------------] + // |...............| + // first last + var pixelsPerValue = (elem.clientWidth - thumbElem.clientWidth) / max; + + elem.ondragstart = function() { + return false; + }; + + elem.onmousedown = function(event) { + if (event.target.closest('.thumb')) { + startDrag(event.clientX, event.clientY); + return false; // disable selection start (cursor change) + } + } + + function startDrag(startClientX, startClientY) { + thumbCoords = thumbElem.getBoundingClientRect(); + shiftX = startClientX - thumbCoords.left; + shiftY = startClientY - thumbCoords.top; + + sliderCoords = elem.getBoundingClientRect(); + + document.addEventListener('mousemove', onDocumentMouseMove); + document.addEventListener('mouseup', onDocumentMouseUp); + } + + function moveTo(clientX) { + // вычесть координату родителя, т.к. position: relative + var newLeft = clientX - shiftX - sliderCoords.left; + + // курсор ушёл вне слайдера + if(newLeft < 0) { + newLeft = 0; + } + var rightEdge = elem.offsetWidth - thumbElem.offsetWidth; + if(newLeft > rightEdge) { + newLeft = rightEdge; + } + + thumbElem.style.left = newLeft + 'px'; + + elem.dispatchEvent(new CustomEvent('slide', { + bubbles: true, + detail: positionToValue(newLeft) + })); + } + + function valueToPosition(value) { + return pixelsPerValue * value; + } + + function positionToValue(left) { + return Math.round( left / pixelsPerValue); + } + + function onDocumentMouseMove(e) { + moveTo(e.clientX); + } + + function onDocumentMouseUp() { + endDrag(); + } + + function endDrag() { + document.removeEventListener('mousemove', onDocumentMouseMove); + document.removeEventListener('mouseup', onDocumentMouseUp); + + elem.dispatchEvent(new CustomEvent('change', { + bubbles: true, + detail: positionToValue(parseInt(thumbElem.style.left)) + })); + } + + function setValue(value) { + thumbElem.style.left = valueToPosition(value) + 'px'; + } + + this.setValue = setValue; +} diff --git a/2-ui/5-widgets/5-custom-events/4-slider-events/task.md b/2-ui/5-widgets/5-custom-events/4-slider-events/task.md new file mode 100644 index 000000000..bfb371cc6 --- /dev/null +++ b/2-ui/5-widgets/5-custom-events/4-slider-events/task.md @@ -0,0 +1,46 @@ +# Слайдер с событиями + +[importance 5] + +На основе слайдера из задачи [](/task/slider-widget) создайте графический компонент, который умеет возвращать/получать значение. + +Синтаксис: + +```js +var slider = new Slider({ + elem: document.getElementById('slider'), + max: 100 // слайдер на самой правой позиции соответствует 100 +}); +``` + +Метод `setValue` устанавливает значение: + +```js +slider.setValue(50); +``` + +У слайдера должно быть два события: `slide` при каждом передвижении и `change` при отпускании мыши (установке значения). + +Пример использования: + +```js +var sliderElem = document.getElementById('slider'); + +sliderElem.addEventListener('slide', function(event) { + document.getElementById('slide').innerHTML = event.detail; +}); + +sliderElem.addEventListener('change', function(event) { + document.getElementById('change').innerHTML = event.detail; +}); +``` + +В действии: +[iframe src="solution" height="80"] + + + +Исходный документ -- возьмите решение задачи [](/task/slider-widget). \ No newline at end of file diff --git a/2-ui/5-widgets/6-what-next/article.md b/2-ui/5-widgets/6-what-next/article.md new file mode 100644 index 000000000..719c411a7 --- /dev/null +++ b/2-ui/5-widgets/6-what-next/article.md @@ -0,0 +1,32 @@ +# Что изучать дальше + +Если вы прочитали весь учебник и сделали задачи, то на текущий момент вы обладаете важнейшими фундаментальными знаниями и навыками JavaScript. + +[cut] + +В этом разделе мы изучали основы создания компонентов на JavaScript. Если проект большой и сложный, то понадобятся дополнительные инструменты для связывания компонент между собой, для привязки к ним данных и так далее. + +Сейчас существует много фреймворков. Всё активно развивается, меняется, кипит и булькает, может быть из этого получится "общепринятая" архитектура, а может и нет. Сейчас явного победителя нет, выбор фреймворка зависит от проекта и личных предпочтений разработчиков. + +Примеры удачных фреймворков, которые можно изучить: + + + +Также для работы с браузерами понадобятся различные [API](https://ru.wikipedia.org/wiki/%D0%98%D0%BD%D1%82%D0%B5%D1%80%D1%84%D0%B5%D0%B9%D1%81_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F_%D0%BF%D1%80%D0%B8%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9), в частности: + + + +В дополнительных разделах учебника мы обязательно разберём что-то из этого. + +...И, конечно, понадобится система сборки проектов, например [WebPack](http://webpack.github.io/). + +Успехов вам! diff --git a/2-ui/5-widgets/6-widget-tasks/6-slider-events/solution.view/index.html b/2-ui/5-widgets/6-widget-tasks/6-slider-events/solution.view/index.html deleted file mode 100755 index 755942b35..000000000 --- a/2-ui/5-widgets/6-widget-tasks/6-slider-events/solution.view/index.html +++ /dev/null @@ -1,183 +0,0 @@ - - - - - - - - - -
-
-
- -Slide:  -Change:  - - - - - - diff --git a/2-ui/5-widgets/6-widget-tasks/6-slider-events/task.md b/2-ui/5-widgets/6-widget-tasks/6-slider-events/task.md deleted file mode 100644 index 19ccb7b58..000000000 --- a/2-ui/5-widgets/6-widget-tasks/6-slider-events/task.md +++ /dev/null @@ -1,45 +0,0 @@ -# Слайдер с событиями - -[importance 5] - -На основе слайдера из задачи [](/task/slider) создайте графический компонент, который умеет возвращать/получать значение. - -Синтаксис: - -```js -var slider = new Slider({ - elem: $('#slider'), - max: 100 // слайдер на самой правой позиции соответствует 100 -}); -``` - -Метод `setValue` устанавливает значение: - -```js -slider.setValue(50); -``` - -У слайдера должно быть два события: `slide` при каждом передвижении и `change` при отпускании мыши (установке значения). - -Пример использования: - -```js -$(slider).on({ - slide: function(value) { - $('#slide').html(value); - }, - change: function(value) { - $('#change').html(value); - } -}); -``` - -В действии: -[iframe src="solution" height="60"] - - - -Исходный документ -- возьмите решение из задачи [](/task/slider). \ No newline at end of file diff --git a/2-ui/5-widgets/6-widget-tasks/article.md b/2-ui/5-widgets/6-widget-tasks/article.md deleted file mode 100644 index 9ce2d3120..000000000 --- a/2-ui/5-widgets/6-widget-tasks/article.md +++ /dev/null @@ -1,2 +0,0 @@ -# Практика, практика, практика! - diff --git a/2-ui/5-widgets/7-what-next/article.md b/2-ui/5-widgets/7-what-next/article.md deleted file mode 100644 index c4c730f60..000000000 --- a/2-ui/5-widgets/7-what-next/article.md +++ /dev/null @@ -1,21 +0,0 @@ -# Что изучать дальше - -Если вы прочитали весь учебник и сделали задачи, то на текущий момент вы обладаете важнейшими фундаментальными знаниями JavaScript и квалификацией, чтобы создавать графические компоненты, достойные современного сайта. - -Ещё предстоит изучить: - - -Кое-что из этого вы можете узнать из дополнительных глав учебника. Многое -- из Open Source. - -Обратите внимание на раздел [](/books). - -Присоединитесь к какому-нибудь интересному проекту и сделайте что-то хорошее. Разберитесь в том, как оно работает. Пофиксите пару багов. - -Успехов вам! \ No newline at end of file diff --git a/3-more/1-webcomponents/1-webcomponents-intro/article.md b/3-more/1-webcomponents/1-webcomponents-intro/article.md index f94feefcb..7b55df9da 100644 --- a/3-more/1-webcomponents/1-webcomponents-intro/article.md +++ b/3-more/1-webcomponents/1-webcomponents-intro/article.md @@ -58,7 +58,7 @@
  • "Твиты"
  • -Как мы решаем, что именно выделять в компонент? Конечно, нам подсказывает опыт, но +Как мы решаем, что именно выделять в компонент? Это нам подсказывает опыт и здравый смысл. В случае с твиттером разбиение на компоненты особенно очевидно. Странца "сама распадается" на чётко очерченные блоки, каждый из которых выполняет свою роль. @@ -87,5 +87,7 @@ Веб-компоненты (Web Components) -- это не один стандарт, а целая платформа, комплекс стандартов, которые вместе добавляют в браузер технологии для удобной реализации компонент. -Если глядеть "сверху", то веб-компоненты -- это возможность добавлять свои элементы в браузер, например `document.createElement("tweets-list")`. Но "под капотом" кроются расширенные возможности по инкапсуляции поддерева DOM и стилей, по генерации событий и многое другое. +Если глядеть "сверху", то веб-компоненты -- это возможность добавлять свои элементы в браузер, например `document.createElement("tweets-list")`, которые описываются с помощью классов JavaScript, могут иметь свои методы и свойства. + +Также "под капотом" кроются расширенные возможности по инкапсуляции поддерева DOM и стилей, по генерации событий и многое другое, что мы рассмотрим далее. diff --git a/3-more/1-webcomponents/2-webcomponent-core/article.md b/3-more/1-webcomponents/2-webcomponent-core/article.md index 426b444a9..7bd2b6f3b 100644 --- a/3-more/1-webcomponents/2-webcomponent-core/article.md +++ b/3-more/1-webcomponents/2-webcomponent-core/article.md @@ -1,19 +1,21 @@ -# Веб-компоненты +# Свои элементы: Custom Elements -Сердцем платформы "веб-компоненты" является стандарт [Web Components](http://w3c.github.io/webcomponents/explainer/), который находится в разработке и позволяет описывать свои элементы. +Платформа "веб-компоненты" включает в себя несколько стандартов [Web Components](http://www.w3.org/standards/techs/components#w3c_all), которые находятся в разработке. + +Начнём мы со стандарта [Custom Elements](http://www.w3.org/TR/custom-elements/), который позволяет создавать свои типы элементов. [cut] -## Зачем Web Components? +## Зачем Custom Elements? -Критично настроенный читатель скажет: "Зачем ещё стандарт для создания своих элементов? Я могу создать любой элемент и прямо сейчас! В любом из современных браузеров можно писать любой HTML, используя свои теги. В чём же разница?" +Критично настроенный читатель скажет: "Зачем ещё стандарт для создания своих элементов? Я могу создать любой элемент и прямо сейчас! В любом из современных браузеров можно писать любой HTML, используя свои теги: ``. Или создавать элементы из JavaScript при помощи `document.createElement('mytag')`. В чём же разница?" -Она в том, что обычно элемент с нестандартным названием (например ``) воспринимается браузером, как нечто неопределённо-непонятное. Ему соответствует класс [HTMLUnknownElement](http://www.w3.org/TR/html5/dom.html#htmlunknownelement), и у него нет каких-либо особых методов. +Она в том, что обычно элемент с нестандартным названием (например ``) воспринимается браузером, как нечто неопределённо-непонятное. Ему соответствует класс [HTMLUnknownElement](http://www.w3.org/TR/html5/dom.html#htmlunknownelement), и у него нет каких-либо особых методов. -**Стандарт Web Components позволяет описывать для новых элементов свои свойства, методы, объявлять свой DOM, подобие конструктора и многое другое.** +**Стандарт Custom Elements позволяет описывать для новых элементов свои свойства, методы, объявлять свой DOM, подобие конструктора и многое другое.** Давайте посмотрим это на примерах. -[warn header="Только Chrome"] -Так как спецификация не окончательна, то для запуска примеров рекомендуется использовать Chrome Canary. +[warn header="Для примеров рекомендуется Chrome"] +Так как спецификация не окончательна, то для запуска примеров рекомендуется использовать Google Chrome, лучше -- последнюю сборку [Chrome Canary](https://www.google.ru/chrome/browser/canary.html), в которой, как правило, отражены последние изменения. [/warn] ## Новый элемент @@ -22,7 +24,7 @@ Здесь:
      -
    • `имя` -- имя нового тега, например `"mega-select"`. Оно обязано содержать дефис `-`. Спецификация требует дефис, чтобы избежать в будущем конфликтов со стандартными элементами HTML. Нельзя создать элемент `timer` или `myTimer` -- будет ошибка.
    • +
    • `имя` -- имя нового тега, например `"mega-select"`. Оно обязано содержать дефис `"-"`. Спецификация требует дефис, чтобы избежать в будущем конфликтов со стандартными элементами HTML. Нельзя создать элемент `timer` или `myTimer` -- будет ошибка.
    • `прототип` -- объект-прототип для нового элемента, он должен наследовать от `HTMLElement`, чтобы у элемента были стандартные свойства и методы.
    @@ -62,7 +64,7 @@ ``` -**Метод `registerElement` может быть вызван в любое время, даже и после появления `` в HTML.** +Использовать новый элемент в HTML можно и до его объявления через `registerElement`. Для этого в браузере предусмотрен специальный режим "обновления" существующих элементов. @@ -72,7 +74,7 @@
  • При вызове `registerElement` такие элементы автоматически обновятся до нужного класса.
  • -Вот демо того, что происходит, когда регистрация элемента происходит после его появления в разметке: +В примере ниже регистрация элемента происходит через 2 секунды после его появления в разметке: ```html @@ -85,7 +87,7 @@ } hello-world { - transition: color 5s; + transition: color 3s; } @@ -103,12 +105,15 @@ } }); +*!* + // у нового типа элементов есть метод sayHi +*/!* hello.sayHi(); }, 2000); ``` -Создание таких элементов в JavaScript никак не отличается от обычных: +Можно создавать такие элементы и в JavaScript -- обычным вызовом `createElement`: ```js var timer = document.createElement('my-timer'); @@ -116,9 +121,11 @@ var timer = document.createElement('my-timer'); ## Расширение встроенных элементов +Выше мы видели пример создания элемента на основе базового `HTMLElement`. Но можно расширить и другие, более конкретные HTML-элементы. + Для расширения встроенных элементов у `registerElement` предусмотрен параметр `extends`, в котором можно задать, какой тег мы расширяем. -Например: +Например, кнопку: ```html @@ -167,7 +174,7 @@ var timer = document.createElement("button", "my-timer"); ## Жизненный цикл -Следующие методы автоматически вызываются во время жизненного цикла элемента: +В прототипе своего элемента мы можем задать специальные методы, которые будут вызываться при создании, добавлении и удалении элемента из DOM: @@ -176,9 +183,9 @@ var timer = document.createElement("button", "my-timer");
    `createdCallback`Элемент создан
    `attributeChangedCallback(name, prevValue, newValue)`Атрибут добавлен, изменён или удалён
    -Как вы, наверняка, заметили, `createdCallback` является подобием конструктора. Можно добавить к элементу свойства, запросить с сервера данные и так далее. +Как вы, наверняка, заметили, `createdCallback` является подобием конструктора. Он вызывается только при создании элемента, поэтому всю дополнительную инициализацию имеет смысл описывать в нём. -Давайте используем `attachedCallback`, чтобы автоматически запускать таймер при вставке в документ: +Давайте используем `createdCallback`, чтобы инициализовать таймер, а `attachedCallback` -- чтобы автоматически запускать таймер при вставке в документ: ```html @@ -186,8 +193,15 @@ var timer = document.createElement("button", "my-timer"); var MyTimerProto = Object.create(HTMLElement.prototype); MyTimerProto.tick = function() { - this.innerHTML++; + this.timer++; + this.innerHTML = this.timer; + }; + +*!* + MyTimerProto.createdCallback = function() { + this.timer = 0; }; +*/!* *!* MyTimerProto.attachedCallback = function() { @@ -203,10 +217,9 @@ var timer = document.createElement("button", "my-timer"); 0 ``` -Спецификация: -
      -
    • [Introduction to Web Components](http://w3c.github.io/webcomponents/explainer/)
    • -
    • [Custom Elements](http://w3c.github.io/webcomponents/spec/custom/)
    • -
    +## Итого + +Мы рассмотрели, как создавать свои DOM-элементы при помощи стандарта [Custom Elements](http://www.w3.org/TR/custom-elements/). +Далее мы перейдём к изучению дополнительных возможностей по работе с DOM. diff --git a/3-more/1-webcomponents/3-shadow-dom/article.md b/3-more/1-webcomponents/3-shadow-dom/article.md index 9a4697aa3..cbdb0bfed 100644 --- a/3-more/1-webcomponents/3-shadow-dom/article.md +++ b/3-more/1-webcomponents/3-shadow-dom/article.md @@ -1,8 +1,8 @@ -# Shadow DOM +# Shadow DOM, шаблоны и стили -Спецификация [Shadow DOM](http://w3c.github.io/webcomponents/spec/shadow/index.html) готовится как отдельный стандарт и применяется, в том числе, отдельно от веб-компонентов. +Спецификация [Shadow DOM](http://w3c.github.io/webcomponents/spec/shadow/) является отдельным стандартом. Частично он уже используется для обычных DOM-элементов, но также применяется для создания веб-компонентов. -*Shadow DOM* -- это внутренний DOM элемента, который существует отдельно от внешнего документа. В нём могут быть свои ID, свои стили и так далее. Причём снаружи оно, без применения специальных техник, не видно, поэтому не возникает конфликтов. +*Shadow DOM* -- это внутренний DOM элемента, который существует отдельно от внешнего документа. В нём могут быть свои ID, свои стили и так далее. Причём снаружи его, без применения специальных техник, не видно, поэтому не возникает конфликтов. [cut] ## Внутри браузера @@ -14,13 +14,11 @@ Например, вот такое содержимое будет у ``: -То, что находится под `shadow-root` -- это и есть Shadow DOM. +То, что находится под `#shadow-root` -- это и есть Shadow DOM. **Получить элементы из Shadow DOM можно только при помощи специальных JavaScript-вызовов или селекторов. Это не обычные дети, а намного более мощное средство отделения содержимого.** -В Shadow DOM выше можно увидеть полезный атрибут `pseudo`. С его помощью можно стилизовать подэлементы через CSS. - -Например, сделаем поле редактирования даты красным: +В Shadow DOM выше можно увидеть полезный атрибут `pseudo`. Он нестандартный, существует по историческим причинам. С его помощью можно стилизовать подэлементы через CSS, например, сделаем поле редактирования даты красным: ```html @@ -35,7 +33,9 @@ input::-webkit-datetime-edit { ``` -Если говорить хронологически, то сначала браузеры начали экспериментировать внутри себя с инкапсуляцией внутренних DOM-структур, а уже потом, через некоторое время, появился стандарт Shadow DOM, который позволяет делать то же самое разработчикам. +Ещё раз заметим, что `pseudo` -- нестандартный атрибут. Если говорить хронологически, то сначала браузеры начали экспериментировать внутри себя с инкапсуляцией внутренних DOM-структур, а уже потом, через некоторое время, появился стандарт Shadow DOM, который позволяет делать то же самое разработчикам. + +Далее мы рассмотрим работу с Shadow DOM из JavaScript, по стандарту [Shadow DOM](http://w3c.github.io/webcomponents/spec/shadow/). ## Создание Shadow DOM @@ -44,7 +44,7 @@ Shadow DOM можно создать внутри любого элемента Например: ```html - +

    Доброе утро, страна!

    ``` -Теперь, если у вас последний Chrome, вы увидите две строчки: "Доброе утро, страна!" в заголовке, а затем "Привет из подполья". +Теперь вы увидите две строчки: "Доброе утро, страна!" в заголовке, а затем "Привет из подполья". Shadow DOM примера выше в инструментах разработки: @@ -80,21 +78,21 @@ Shadow DOM примера выше в инструментах разработ Важные детали:
    • Тег `` влияет только на отображение, он не перемещает узлы физически. Как видно из картинки выше, текстовый узел "Доброе утро, страна!" остался внутри `p#elem`. Его можно даже получить при помощи `elem.firstElementChild`.
    • -
    • Внутри `` показывается не `

      `, а его содержимое, то есть в данном случае текст "Привет из подполья".

    • +
    • Внутри `` показывается не элемент целиком `

      `, а его содержимое, то есть в данном случае текст "Доброе утро, страна!".

    -**Атрибутом `select` можно указать конкретный селектор содержимого, которое нужно переносить. Например, `` перенесёт только заголовки.** +**В `` атрибутом `select` можно указать конкретный селектор содержимого, которое нужно переносить. Например, `` перенесёт только заголовки.** -Внутри Shadow DOM можно использовать `` много раз, указывая таким образом, где конкретно какие части исходного содержимого разместить, но при этом дублирование узлов невозможно. Если узел показан в одном ``, то в следующем он будет пропущен. +Внутри Shadow DOM можно использовать `` много раз с разными значениями `select`, указывая таким образом, где конкретно какие части исходного содержимого разместить. Но при этом дублирование узлов невозможно. Если узел показан в одном ``, то в следующем он будет пропущен. -Например, если сначала идёт ``, а затем ``, то в первом `` будут показаны заголовки `

    ` с классом `big`, а во втором -- все остальные. +Например, если сначала идёт ``, а затем ``, то в первом `` будут показаны заголовки `

    ` с классом `title`, а во втором -- все остальные, кроме уже показанных. -**Содержимое внутри `...` будет показано только в том случае, если узлов для вставки нет.** +В примере выше тег `` внутри пуст. Если в нём указать содержимое, то оно будет показано только в том случае, если узлов для вставки нет. Например потому что ни один узел не подпал под указанный `select`, или все они уже отображены другими, более ранними ``. -Ещё пример: +Например: ```html - +

    Новости

    @@ -103,23 +101,24 @@ Shadow DOM примера выше в инструментах разработ + + ``` При запуске мы увидим, что:
    • Первый `` выведет заголовок.
    • -
    • Второй `` вывел бы автора, но так как такого элемента нет -- выводится содержимое самого ``.
    • -
    • Третий `` выведет остальное содержимое (уже без заголовка `

      `, он выведен ранее!)

    • +
    • Второй `` вывел бы автора, но так как такого элемента нет -- выводится содержимое самого ``, то есть "Без автора".
    • +
    • Третий `` выведет остальное содержимое исходного элемента -- уже без заголовка `

      `, он выведен ранее!

    -Ещё раз обратим внимание, что `` физически не перемещает узлы по DOM. Он только показывает, где их отображать, а также, как мы увидим далее, влияет на применение стилей. Поэтому в примере выше `innerHTML` выведет содержимое `elem`, как если бы не было никакого Shadow DOM. +Ещё раз обратим внимание, что `` физически не перемещает узлы по DOM. Он только показывает, где их отображать, а также, как мы увидим далее, влияет на применение стилей. ## Корень shadowRoot @@ -127,23 +126,30 @@ Shadow DOM примера выше в инструментах разработ Он представляет собой специальный объект, поддерживающий основные методы CSS-запросов и подробно описанный в стандарте как [ShadowRoot](http://w3c.github.io/webcomponents/spec/shadow/#shadowroot-object). -Например: +Если нужно работать с содержимым в Shadow DOM, то нужно перейти к нему через `elem.shadowRoot`. Можно и создать новое Shadow DOM-дерево из JavaScript, например: ```html - +

    Доброе утро, страна!

    ``` @@ -153,23 +159,25 @@ Shadow DOM примера выше в инструментах разработ ## Шаблоны