简体   繁体   English

文档片段内自定义元素的初始化

[英]Initialisation of Custom Elements Inside Document Fragment

Consider this HTML template with two flat x-element s and one nested.考虑这个带有两个扁平x-element和一个嵌套的 HTML template

<template id="fooTemplate">
  <x-element>Enter your text node here.</x-element>
  <x-element>
    <x-element>Hello, World?</x-element>
  </x-element>
</template>

How to initialise (fire constructor) all custom elements in cloned from fooTemplate document fragment without appending it to DOM, neither by extending built-in elements with is="x-element" ;如何初始化(触发构造函数)从fooTemplate文档片段克隆的所有自定义元素,而不将其附加到 DOM,也不是通过使用is="x-element"扩展内置is="x-element" either entire fragment.要么是整个片段。

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

Note that script executes with defer attribute.请注意,脚本使用defer属性执行。

We can initialise template with this arrow function:我们可以用这个箭头函数初始化模板:

const initTemplate = temp =>
  document.createRange().createContextualFragment( temp.innerHTML );

const frag = initTemplate( window['someTemplate'] );

Or with this method defined on template prototype (I prefer this way):或者使用在template原型上定义的这种方法(我更喜欢这种方式):

Object.defineProperty(HTMLTemplateElement.prototype, 'initialise', {
  enumerable: false,
  value() {
    return document.createRange().createContextualFragment( this.innerHTML );
  }
});

const frag = window['someTemplate'].initialise();

In any case in result this code will work fine:在任何情况下,此代码都可以正常工作:

for (const elem of  frag.querySelectorAll('x-element') )
  elem.foo();

window['someNode'].appendChild( frag );

I'm not sure if these methods are the most effective way to initialise custom elements in template.我不确定这些方法是否是在模板中初始化自定义元素的最有效方法。

Also note that there is no need for cloning template.另请注意,不需要克隆模板。

You can avoid the "createContextualFragment" hack from the previous answer by simply adding the template clone to the document immediately before processing it.您可以通过处理之前立即将模板克隆添加到document来避免上一个答案中的“createContextualFragment”hack。

Assuming we have these two variables defined...假设我们定义了这两个变量......

const containerEl = document.querySelector('div.my-container')
const templateEl = document.querySelector('#fooTemplate')

...instead of doing this (where frag contains uninitialised custom elements)... ...而不是这样做(其中frag包含未初始化的自定义元素)...

const frag = templateEl.content.cloneNode(true)
manipulateTemplateContent(frag)
containerEl.appendChild(frag)

...append the template clone to the document first , then manipulate it. ...追加模板克隆到文档然后再对其进行操作。 The user won't notice any difference - it's all synchronous code executed within the same frame.用户不会注意到任何区别——所有同步代码都在同一帧内执行。

const frag = templateEl.content.cloneNode(true)
containerEl.appendChild(frag)
manipulateTemplateContent(containerEl)

TLDR:域名注册地址:

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

Explanation:解释:

Since the custom element is created in a different document/context (the DocumentFragment of the template) it doesn't know about the custom elements definition in the root/global document.由于自定义元素是在不同的文档/上下文(模板的DocumentFragment )中创建的,因此它不知道根/全局文档中的自定义元素定义。 You can get the document an element belongs to by reading the Node.ownerDocument property ( MDN ) which in this case will be different to the window.document element.您可以通过读取Node.ownerDocument属性 ( MDN ) 来获取元素所属的文档,在这种情况下,该属性与window.document元素不同。

This being said you need to create the custom element in the context of the global document in order to "apply" the custom element.话虽如此,您需要在全局文档的上下文中创建自定义元素,以便“应用”自定义元素。 This can be done by calling document.importNode(node, [true]) ( MDN ) which works like node.cloneNode([true]) , but creates a copy of the element in the global document context.这可以通过调用document.importNode(node, [true]) ( MDN ) 来完成,它的工作方式类似于node.cloneNode([true]) ,但会在全局文档上下文中创建元素的副本。

Alternatively you can also use document.adoptNode(node) ( MDN ) to first adopt the DocumentFragment to the global document and then create copies of it via node.cloneNode([true]) .或者,您也可以使用document.adoptNode(node) ( MDN ) 首先采用 DocumentFragment 到全局文档,然后通过node.cloneNode([true])创建它的副本。 Note though if you use adoptNode() on an HTML element it will be removed from its original document.请注意,如果您在 HTML 元素上使用adoptNode() ,它将从其原始文档中删除。

Illustrative Example Code:示例代码:

 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>

Note: Appending an element from one document to another document forces an implicit adoption of the node .注意:将一个文档中的元素附加到另一个文档会强制隐式采用 node That's why appending the element to the global DOM works in this case.这就是为什么在这种情况下将元素附加到全局 DOM 的原因。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM