Ilm*_*ont 15 html javascript web-component native-web-component
我正在使用Web Components v1.
假设有两个自定义元素:
家长element.html
<template id="parent-element">
<child-element></child-element>
</template>
Run Code Online (Sandbox Code Playgroud)
儿童element.html
<template id="child-element">
<!-- some markup here -->
</template>
Run Code Online (Sandbox Code Playgroud)
我试图在连接时使用connectedCallbackin parent-element初始化整个父/子DOM结构,这需要与定义的方法进行交互child-element.
但是,似乎child-element没有正确定义当时connectedCallback被解雇customElement:
家长element.js
class parent_element extends HTMLElement {
connectedCallback() {
//shadow root created from template in constructor previously
var el = this.shadow_root.querySelector("child-element");
el.my_method();
}
}
Run Code Online (Sandbox Code Playgroud)
这不起作用,因为它el是一个HTMLElement而不是child-element预期的.
我parent-element的模板中的所有子自定义元素都已正确附加后,我需要回调.
这个问题的解决方案似乎不起作用; this.parentElement在null里面child-element connectedCallback().
ilmiont
Emi*_*ier 10
在 ShadowDOM 模板中使用插槽元素。
以某种方式构建您的自定义元素,以便它们可以存在于任何上下文中,例如作为子元素或父元素,而与其他自定义元素没有任何依赖关系。这种方法将为您提供模块化设计,您可以在任何情况下使用自定义元素。
但是您仍然希望在存在子元素时执行某些操作,例如选择它们或调用子元素的方法。
为了解决这个问题,<slot>引入了元素。使用插槽元素,您可以在 ShadowDOM 模板中创建占位符。这些占位符可以通过简单地将一个元素作为 DOM 中的子元素放置在您的自定义元素中来使用。然后子元素将被放置在<slot>放置元素的位置内。
插槽元素可以侦听名为 的唯一事件slotchange。每当一个元素(或多个元素)放置在元素的位置上时,就会触发这个slot。
在事件的侦听器中,您可以使用HTMLSlotElement.assignedNodes()或HTMLSlotElement.assignedElements()方法访问占位符中的所有元素。这些返回一个数组,其中的元素放置在slot.
现在您可以等待孩子被放置在插槽内,并对在场的孩子做一些事情。
这种方式允许您只操作 DOM,而让 ShadowDOM 独自完成它的工作。就像处理常规 HTML 元素一样。
是的,在调用自定义元素的slotchange所有connectedCallback方法后会触发该事件。这意味着在收听赛事时不会出现赛车状况或缺少设置。
class ParentElement extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: 'open'});
this.shadowRoot.innerHTML = `
<h2>Parent Element</h2>
<slot></slot>
`;
console.log("I'm a parent and have slots.");
// Select the slot element from the ShadowDOM..
const slot = this.shadowRoot.querySelector('slot');
// ..and listen for the slotchange event.
slot.addEventListener('slotchange', (event) => {
// Get the elements assigned to the slot..
const children = event.target.assignedElements();
// ..loop over them and call their methods.
children.forEach(child => {
if (child.tagName.toLowerCase() === 'child-element') {
child.shout()
}
});
});
}
connectedCallback() {
console.log("I'm a parent and am now connected");
}
}
customElements.define('parent-element', ParentElement);
class ChildElement extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: 'open'});
this.shadowRoot.innerHTML = `
<h3>Child Element</h3>
`;
}
connectedCallback() {
console.log("I'm a child and am now connected.");
}
shout() {
console.log("I'm a child and placed inside a slot.");
}
}
customElements.define('child-element', ChildElement);Run Code Online (Sandbox Code Playgroud)
<parent-element>
<child-element></child-element>
<child-element></child-element>
<child-element></child-element>
</parent-element>Run Code Online (Sandbox Code Playgroud)
在connectedCallback升级其任何自定义元素子元素之前,第一次调用 It存在计时问题。被调用<child-element>时只是一个 HTMLElement connectedCallback。
要获得升级后的子元素,您需要在超时时间内完成。
运行下面的代码并观察控制台输出。当我们尝试调用孩子的方法时,它失败了。同样,这是因为 Web 组件的创建方式。和时间的时间connectedCallback被调用。
但是,在对setTimeout孩子的方法的调用中起作用。这是因为您留出了时间让子元素升级为您的自定义元素。
如果你问我,有点傻。我希望在所有孩子都升级后调用另一个函数。但我们用我们拥有的东西工作。
class ParentElement extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: 'open'});
this.shadowRoot.innerHTML = '<h2>Parent Element</h2><child-element></child-element>';
}
connectedCallback() {
let el = this.shadowRoot.querySelector("child-element");
console.log('connectedCallback', el);
try {
el.childMethod();
}
catch(ex) {
console.error('Child element not there yet.', ex.message);
}
setTimeout(() => {
let el = this.shadowRoot.querySelector("child-element");
console.log('setTimeout', el);
el.childMethod();
});
}
}
customElements.define('parent-element', ParentElement);
class ChildElement extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: 'open'});
this.shadowRoot.innerHTML = '<h3>Child Element</h3>';
}
childMethod() {
console.info('In Child method');
}
}
customElements.define('child-element', ChildElement);Run Code Online (Sandbox Code Playgroud)
<parent-element></parent-element>Run Code Online (Sandbox Code Playgroud)
经过更多的工作,我有了一个解决方案。
当然this.parentElement在子元素中不起作用;它位于影子 DOM 的根部!
我目前的解决方案适合我的具体场景,如下:
父元素.js
init() {
//Code to run on initialisation goes here
this.shadow_root.querySelector("child-element").my_method();
}
Run Code Online (Sandbox Code Playgroud)
子元素.js
connectedCallback() {
this.getRootNode().host.init();
}
Run Code Online (Sandbox Code Playgroud)
因此,在子元素中,我们获取根节点(模板影子 DOM),然后获取其宿主、父元素,并调用init(...),此时父元素可以访问子元素,并且它已完全定义。
由于多种原因,该解决方案并不理想,因此我不会将其标记为已接受。
1)如果有多个子级需要等待,或者嵌套更深,那么协调回调将会变得更加复杂。
2)我担心 的影响child-element,如果我想以独立的方式使用这个元素(即在其他地方,完全独立于嵌套在 中parent-element),我将必须修改它以显式检查是否getRootNode().host是 的实例parent-element。
所以这个解决方案目前有效,但感觉很糟糕,我认为当整个 DOM 结构(包括其影子 DOM 中的嵌套自定义元素)初始化时,需要有一个回调在父级上触发。