5

考虑这个template带有两个 flatx-element和一个嵌套的 HTML。

<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属性执行。

4

3 回答 3

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

我不确定这些方法是否是在模板中初始化自定义元素的最有效方法。

另请注意,不需要克隆模板。

于 2019-04-22T09:49:31.507 回答
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)
于 2020-08-16T08:38:45.720 回答
0

TLDR:

使用document.importNode(template.content, true);而不是在template.content.cloneNode(true); 此处阅读有关 document.importNode() 的更多信息。

解释:

由于自定义元素是在不同的文档/上下文(模板的DocumentFragment)中创建的,因此它不知道根/全局文档中的自定义元素定义。Node.ownerDocument您可以通过读取属性 ( MDN )来获取元素所属的文档,在这种情况下,该属性与window.document元素不同。

话虽如此,您需要在全局文档的上下文中创建自定义元素才能“应用”自定义元素。这可以通过调用document.importNode(node, [true])( MDN ) 来完成,它的工作方式类似于node.cloneNode([true]),但会在全局文档上下文中创建元素的副本。

或者,您也可以使用document.adoptNode(node)( MDN ) 首先将 DocumentFragment 应用于全局文档,然后通过node.cloneNode([true]). 请注意,如果您adoptNode()在 HTML 元素上使用,它将从其原始文档中删除。

说明性示例代码:

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>

注意:将一个文档中的元素附加到另一个文档会强制隐式采用节点。这就是在这种情况下将元素附加到全局 DOM 的原因。

于 2021-04-25T11:15:12.463 回答