Главная » Хабрахабр » Веб-компоненты. Часть 3: html шаблоны и импорты

Веб-компоненты. Часть 3: html шаблоны и импорты

Вступление

Данная статья является третьей и последней статьей в серии статей о веб-компонентах.Первые две статьи доступны по ссылкам: Приветствую коллеги.

Веб компоненты. Часть 1: Пользовательские элементы
Веб-компоненты. Часть 2: Теневой DOM

В данной статье речь пойдет о <template> элементе а также об HTML импортах.

HTML Templates элемент

Элемент <template> представляет собой инструмент, позволяющий хранить контент на стороне клиента без его рендеринга на страницу, однако с возможностью его отображения в процессе исполнения посредством JavaScript.

Содержимое элемента можно клонировать и вставлять в документ из скриптов, что и используется как самостоятельно для шаблонизации так и при создании веб-компонентов. Содержимое элемента при парсинге страницы обрабатывается исключительно в части валидации содержимого, но без его рендеринга (согласно спецификации, при рендеринге этот элемент ничего не представляет).

Содержимое <template>

Это означает, что в содержимом шаблона можно, например, указать элемент img не указав значение атрибутов src и alt, таким образом: К содержимому <template>, как и к любому узлу, не имеющему браузерного контекста, не применимы никакие требования соответствия, кроме требований к правильности HTML и XML синтаксиса.

<template> <div> <img src="}" alt="{{alt}}"> </div> </template>

При этом пропуск закрывающего тега </div> был бы нарушением HTML синтаксиса и не является допустимым для содержимого <template>. однако, вне элемента <template> такой синтаксис валидным не является.

Все элементы указанные внутри тега <template> в html коде не являются его дочерними элементами.

approptiate template contents owner document, определяемый по этому алгоритму, документа в котором указан <template> и указывает значением свойства .content созданный DocumentFragment. Бразуеры при создании элемента <template> создают DocumentFragment чьим документом является т.н.

То есть свойство .content у элемента template содержит DocumentFragment, и элементы, которые в html коде были указаны внутри тегов <template> являются дочерними элементами именно этого DocumentFragment.

При этом элементу <template>, как и любому другому, можно добавить дочерние элементы (appendChild()), но это будет считаться нарушение модели содержимого шаблона.

Клонирование шаблона

При клонирования содержимого шаблона важно помнить, что первый аргумент в .cloneNode([deep])
или второй в .importNode(externalNode, deep) передавать обязательно надо (согласно спецификации, если аргумент не будет передан, дальнейшее выполнение происходить не должно).

Разница только в том, когда документ обновится (для .cloneNode() — после вызова appendChild(); для .importNode() — после клонирования). Кстати, да, не смотря на то что большинство примеров используют именно .cloneNode(), использование .importNode() тоже возможно.

Show me the code ©

Я продолжу пример компоненты таб, с кодом которых я работала в примерах предыдущих статей. Использование шаблонов действительно очень простое.

Начинать я буду с того, что создам в html разметке два элемента <template> и перенесу в них ту разметку, что была в методе .render() классов TabNavigationItem и TabContentItem (я также изменила некоторые стили но на функциональность это не влияет):

<template id="tab-nav"> <style> :host{ padding: 10px; background-color: rgb(81,180,186); transition: background-color 1s; text-align: center; } :host-context(.active) { background-color: rgb(93, 209, 216); } a{ text-decoration: none; color: rgb(3,32,40); } </style> <a href="#${this._target}"><slot></slot></a>
</template>

и:

<template id="tab-content"> <style> :host { display: none; padding: 20px; width: 100%; background-color: rgb(255,212,201); } :host-context(.active){ display: block; } </style> <div><slot></slot></div>
</template>

Для TabNavigationItem это будет: В конструкторе каждого класса я сохраню свойство template.

this.template = document.getElementById('tab-nav');

a для TabContentItem:

this.template = document.getElementById('tab-content');

В метод render() каждому из этих классов я добавлю следующий код, предварительно удалив запись .innerHTML:

const content = this.template.content.cloneNode(true); this.shadowRoot.appendChild(content);

Получившийся код можно посмотреть тут

Это плавно переводит нас к теме: В этом примере оба шаблона указаны в html, что выглядит громозким и не есть гуд.

HTML импорты

Система взаимоотношений между документами хорошо описана в черновике спецификации и не является предметом данной статьи. Импорты представляют собой HTML документы которые подключены в качестве внешних ресурсов другим HTML документом.

Общая схема видна на изображении:

.

Для того чтобы реализовать импорты был добавлен новый тип в HTML link types (значения атрибута rel).

Слово import, указанное в значение атрибута rel элемента <link> собственно и создает ссылку к импортируемому ресурсу (дефолтным типом ресурса является text/html).

У элемента <link> может присутствовать атрибут async.

Расширения, предлагаемые черновиком спецификации предлагаются в АПИ HTMLLinkElement: добавляется свойство import, доступное только для чтения и содержащее импортируемый документ.

Свойство может содержать значение null в двух случаях: когда <link> не представляет import или <link> не находится в документе.

В спецификации отдельно указано что один и тот же объект должен будет возвращаться всегда.

В контексте импортов, есть так называемый мастер-документ (master document), которым является тот документ, который импортирует ресурсы одновременно не являясь при этом чьим либо импортируемым ресурсом.

Так, если Content Security Header Field поставлен в значение импорта, браузер должен принудительно исполнять политику именно мастер-документа к импортируемому документу. ContentSecurityPolicy такого документа должна ограничивать все импорты.

На практике

В ней я создам два файла, в которые я перенесу разметку компоненты. Для компонента таб, я создаю папку templates.

<!--templates/tab-content.html--> <template id="tab-content"> <style> :host { display: none; padding: 20px; width: 100%; background-color: rgb(255,212,201); } :host-context(.active){ display: block; } </style> <div><slot></slot></div> </template> <!--templates/tab-nav.html--> <template id="tab-nav"> <style> :host{ padding: 10px; background-color: rgb(81,180,186); transition: background-color 1s; text-align: center; } :host-context(.active) { background-color: rgb(93, 209, 216); } a{ text-decoration: none; color: rgb(3,32,40); } </style> <a href="#${this._target}"><slot></slot></a> </template>

В <head> файла index.html я импортирую шаблоны:

<link rel="import" href="templates/tab-nav.html" id="tab-nav"> <link rel="import" href="templates/tab-content.html" id="tab-content">

Элементам <link> я добавляю id атрибуты, так как мне понадобится обращаться к ним из js.
Теперь в конструкторах классов TabNavigationItem и TabContentItem для получения документа шаблона мне достаточно будет найти соответствующий элемент <link> и обратится к его свойству import, после чего поиск шаблона я буду выполнять уже в импортируемом документе:

class TabNavigationItem extends HTMLElement { constructor() { super(); this._target = null; this.attachShadow({mode: 'open'}); const templateImport = document.getElementById('tab-nav').import; this.template = templateImport.getElementById('tab-nav'); } //... } class TabContentItem extends HTMLElement { constructor() { super(); this._target = null; this.attachShadow({mode: 'open'}); const templateImport = document.getElementById('tab-content').import; this.template = templateImport.getElementById('tab-content'); } //... }

Финальную версию можно взять тут.

О поддержке

Поддержка HTML templates: Edge c 16, Firefox c 59, Chrome c 49, Safari c 11.
С поддержкой импортов печальнее: Chrome c 49.
Потому примеры из этой статьи можно посмотреть только в последних Chrome.

Существуют полифилы:

Webcomponents
Polymer-project

Почтитать подробнее о шаблонах и импортах:

HTML спецификация
HTML5 спецификация
HTML Imports черновик спецификации

На этом все, спасибо за внимание,
Таня


Оставить комментарий

Ваш email нигде не будет показан
Обязательные для заполнения поля помечены *

*

x

Ещё Hi-Tech Интересное!

Реализация целочисленного БПФ на ПЛИС

Всем привет! Однако, эти ядра не оптимальны, обладают набором «особенностей» и требуют дальнейшей доработки. Однажды меня спросили заказчики, нет ли у меня в проектах целочисленного БПФ, на что я всегда отвечал, что это уже сделано другими в виде готовых, хоть ...

Nginx-переменные с njs: просто, безболезненно и через JavaScript

njs — это JavaScript-интерпретатор в легковесном веб-сервере, с помощью которого можно создавать новые nginx-переменные и обработчики стадий запроса. Чем njs хорош? Чего не умеет? И зачем вообще его сделали? На эти и другие вопросы ответит Дмитрий Волынцев (xeioex), разработчик nginx ...