Nested element (web component) cannot get its template

I made a simple example using web components with two custom elements (v1), where one is nested in the other. index.html:

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Example</title> <meta name="description" content=""> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="import" href="app-container.html"> </head> <body> <app-container></app-container> </body> </html> 

container-application.html:

 <link rel="import" href="toolbar.html"> <template id="app-container"> <app-toolbar></app-toolbar> </template> <script> customElements.define('app-container', class extends HTMLElement { constructor() { super(); let shadowRoot = this.attachShadow({ mode: 'open' }); const content = document.currentScript.ownerDocument.querySelector('#app-container').content; shadowRoot.appendChild(content.cloneNode(true)); } }); </script> 

toolbar.html:

 <template id="app-toolbar"> <p>Ok!</p> </template> <script> customElements.define('app-toolbar', class extends HTMLElement { constructor() { super(); let shadowRoot = this.attachShadow({ mode: 'open' }); const content = document.currentScript.ownerDocument.querySelector('#app-toolbar').content; shadowRoot.appendChild(content.cloneNode(true)); } }); </script> 

But in toolbar.html document.currentScript is the same as in app-container.html, and therefore querySelector('#app-toolbar') cannot find the template with id app-toolbar . How to solve this problem?

Example tested on Chrome 55 (without polyfill).

+6
source share
1 answer

document.currentScript contains a link to a script that is currently being processed and executed. Therefore, it is no longer valid for your purpose when the constructor() function is called (from another script).

Instead, you shoud store its value in a variable at the beginning of the script and use this variable in the constructor:

 <script> var currentScript = document.currentScript customElements.define( ... ) ... </script> 

If you have multiple scripts, you must use different names.

Alternatively, you can encapsulate an ephemeral value in closure:

 (function(owner) { customElements.define('app-container', class extends HTMLElement { constructor() { super(); let shadowRoot = this.attachShadow({ mode: 'open' }); const content = owner.querySelector('#app-container').content; shadowRoot.appendChild(content.cloneNode(true)); } }); })(document.currentScript.ownerDocument); 

Here, the value of document.currentScript.ownerDocument assigned to the owner argument, which is still defined correctly when constructor() called.

owner defined locally, so you can use the same name in another document.

+14
source

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


All Articles