考虑此HTML template
,其中包含两个平面x-element
,一个嵌套。
<template id="fooTemplate">
<x-element>Enter your text node here.</x-element>
<x-element>
<x-element>Hello, World?</x-element>
</x-element>
</template>
如何初始化(射击构造函数)从fooTemplate
文档片段克隆而来的所有自定义元素而不将其附加到DOM,都不能通过使用is="x-element"
扩展内置元素来实现;要么是整个片段。
class XElement extends HTMLElement {
constructor() { super(); }
foo() { console.log( this ); }
} customElements.define( 'x-element', XElement );
const uselessf = function( temp ) {
const frag = window[ temp ].content.cloneNode( true );
/* Your magic code goes here:
*/ do_black_magic( frag );
for (const e of frag.querySelectorAll('x-element') )
e.foo(); // This should work.
return frag;
};
window['someNode'].appendChild( uselessf('fooTemplate') );
请注意,脚本使用defer
属性执行。
答案 0 :(得分:2)
我们可以使用以下箭头功能初始化模板:
const initTemplate = temp =>
document.createRange().createContextualFragment( temp.innerHTML );
const frag = initTemplate( window['someTemplate'] );
或者在template
原型上定义了此方法(我更喜欢这样):
Object.defineProperty(HTMLTemplateElement.prototype, 'initialise', {
enumerable: false,
value() {
return document.createRange().createContextualFragment( this.innerHTML );
}
});
const frag = window['someTemplate'].initialise();
无论如何,此代码都可以正常工作:
for (const elem of frag.querySelectorAll('x-element') )
elem.foo();
window['someNode'].appendChild( frag );
我不确定这些方法是否是初始化模板中自定义元素的最有效方法。
还请注意,无需克隆模板。
答案 1 :(得分:0)
您只需在处理前立即将模板克隆添加到document
中,就可以避免先前答案中的“ createContextualFragment”攻击。
假设我们定义了这两个变量...
const containerEl = document.querySelector('div.my-container')
const templateEl = document.querySelector('#fooTemplate')
...而不是这样做(其中frag
包含未初始化的自定义元素)...
const frag = templateEl.content.cloneNode(true)
manipulateTemplateContent(frag)
containerEl.appendChild(frag)
...首先将模板克隆附加到文档 ,然后对其进行操作。用户不会注意到任何差异-它是在同一帧内执行的所有同步代码。
const frag = templateEl.content.cloneNode(true)
containerEl.appendChild(frag)
manipulateTemplateContent(containerEl)
答案 2 :(得分:0)
TLDR:
使用 document.importNode(template.content, true);
而不是 template.content.cloneNode(true);
Read more about document.importNode() here.
说明:
由于自定义元素是在不同的文档/上下文(模板的 DocumentFragment)中创建的,因此它不知道根/全局文档中的自定义元素定义。您可以通过读取 Node.ownerDocument
属性 (MDN) 来获取元素所属的文档,在这种情况下,该属性与 window.document
元素不同。
这就是说您需要在全局文档的上下文中创建自定义元素才能“应用”自定义元素。这可以通过调用 document.importNode(node, [true])
(MDN) 来完成,它的工作方式类似于 node.cloneNode([true])
,但会在全局文档上下文中创建元素的副本。
或者,您也可以使用 document.adoptNode(node)
(MDN) 首先将 DocumentFragment 应用于全局文档,然后通过 node.cloneNode([true])
创建它的副本。请注意,如果您在 HTML 元素上使用 adoptNode()
,它将从其原始文档中删除。
示例代码:
class XElement extends HTMLElement {
constructor() { super(); console.log("Custom Element Constructed") }
}
customElements.define( 'x-element', XElement );
const externalFragment = fooTemplate.content;
console.log(
"Is ownerDocument equal?",
externalFragment.ownerDocument === document
);
console.log("import start");
const importedFragment = document.importNode(externalFragment, true);
console.log("import end");
console.log(
"Is ownerDocument equal?",
importedFragment.ownerDocument === document
);
<template id="fooTemplate">
<x-element>Hello, World?</x-element>
</template>
注意: 将一个文档中的元素附加到另一个文档会强制使用 implicit adoption of the node。这就是在这种情况下将元素附加到全局 DOM 的原因。