Web components. Part 3: html templates and imports

Introduction

Greetings colleagues. This article is the third and last article in a series of articles on web components. The first two articles are available at the links:


Web components. Part 1: Custom Elements
Web components. Part 2: Shadow DOM

This article will discuss the <template> element as well as HTML imports.



HTML Templates Element

The <template> element is a tool that allows you to store content on the client side without rendering it to a page, but with the ability to display it in the execution process using JavaScript.


The content of the element when parsing the page is processed solely in terms of content validation, but without rendering it (according to the specification, this element does not represent anything when rendering). The content of the element can be cloned and inserted into the document from scripts, which is used both independently for templating and when creating web components.


<Template> content

The content of the <template>, as well as any node that does not have a browser context, does not apply any compliance requirements, except the requirements for the correctness of HTML and XML syntax. This means that in the content of the template, you can, for example, specify the img element without specifying the value of the src and alt attributes, thus:


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

however, outside the <template> element such syntax is not valid. In this case, skipping the closing tag </ div> would be a violation of HTML syntax and is not valid for the content of <template>.


All elements specified inside the <template> tag in the html code are not its child elements.


Browser when creating an element <template> create DocumentFragment whose document is so-called. The approptiate template contents owner document, defined by this algorithm , the document in which <template> is specified and indicates the value of the .content property of the created DocumentFragment.


That is, the .content property of the template element contains a DocumentFragment, and the elements that were specified in the html code inside the <template> tags are children of this DocumentFragment.


In this case, the <template> element, like any other, can add child elements ( appendChild () ), but this will be considered a violation of the template's content model.


Template cloning

When cloning template contents, it is important to remember that the first argument in .cloneNode ([deep])
or the second in .importNode (externalNode, deep) must be passed (according to the specification, if the argument is not passed, no further execution should take place).


By the way, yes, despite the fact that most of the examples use exactly .cloneNode (), the use of .importNode () is also possible. The only difference is when the document is updated (for .cloneNode () - after calling appendChild (); for .importNode () - after cloning).


Show me the code ©

The use of templates is really very simple. I will continue the example of the components of the tab, with the code which I worked in the examples of previous articles.


I will start by creating two <template> elements in the html markup and dragging into them the markup that was in the .render () method of the TabNavigationItem and TabContentItem classes (I also changed some styles, but this does not affect the functionality):


  <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> 

and:


  <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> 

In the constructor of each class, I save the template property. For TabNavigationItem this will be:


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

a for TabContentItem:


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

I will add the following code to the render () method for each of these classes, after removing the .innerHTML entry:


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

The resulting code can be found here.


In this example, both templates are specified in html, which looks loud and does not have a buzz. This smoothly takes us to the topic:


HTML imports

Imports are HTML documents that are connected as external resources by another HTML document. The system of relationships between documents is well described in the draft specification and is not the subject of this article.

The general scheme is visible on the image:


.

In order to implement imports, a new type has been added to HTML link types (values ​​of the rel attribute).


The import word specified in the value of the rel attribute of the <link> element actually creates a link to the imported resource (the default resource type is text / html).


The <link> element may have an async attribute.


The extensions offered by the draft specification are offered in the API HTMLLinkElement: an import property is added that is read-only and contains the document to be imported.


The property may contain a null value in two cases: when the <link> does not represent the import or <link> is not in the document.


The specification states that the same object should always be returned.


In the context of imports, there is a so-called master document (master document), which is the document that imports resources at the same time without being someone else’s imported resource.

ContentSecurityPolicy of such a document should restrict all imports. So, if the Content Security Header Field is set to import, the browser must enforce the policy of the master document for the imported document.


On practice

For the tab component, I create the templates folder. In it, I will create two files in which I transfer the markup components.


  <!--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> 

In the <head> of the index.html file, I import the templates:


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

I add attributes to <link> elements, since I need to access them from js.
Now, in the constructors of the TabNavigationItem and TabContentItem classes, to retrieve a template document, it will be enough for me to find the corresponding <link> element and refer to its import property, after which I’ll search for the template in the imported document:


  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'); } //... } 

The final version can be found here .


About support

HTML templates support: Edge c 16, Firefox c 59, Chrome c 49, Safari c 11.
With the support of imports sadder: Chrome c 49.
Therefore, the examples from this article can be viewed only in the latest Chrome.


There are polyphiles:


Webcomponents
Polymer-project

Read more about templates and imports:

HTML specification
HTML5 specification
HTML Imports draft specification

That's all, thank you for your attention,
Tanya

Source: https://habr.com/ru/post/414905/


All Articles