如何拥有一个“联系回来”'当所有子自定义元素已连接时

时间:2018-02-07 12:21:26

标签: javascript html web-component native-web-component

我正在使用Web Components v1。

假设有两个自定义元素:

父 - element.html

<template id="parent-element">
    <child-element></child-element>
</template>

儿童-element.html

<template id="child-element">
<!-- some markup here -->
</template>

我尝试在connectedCallback中使用parent-element初始化整个父/子DOM结构,这需要与child-element中定义的方法进行交互。< / p>

但是,在child-element connectedCallbackcustomElement解雇时似乎没有正确定义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(); } }

父 - element.js

el

这不起作用,因为HTMLElement是预期的child-element而不是parent-element

一旦其模板中的所有子自定义元素都已正确附加,我就需要this.parentElement的回调。

this question中的解决方案似乎不起作用; null child-element connectedCallback()内的@return map-get($props, color);

ilmiont

6 个答案:

答案 0 :(得分:3)

connectedCallback存在时间问题。在升级任何自定义元素子元素之前,它首次被调用。 <{1}}只是在调用<child-element>时的HTMLElement。

要获得升级的子元素,您需要在超时时间内执行此操作。

运行以下代码并观察控制台输出。当我们尝试调用孩子的方法时,它会失败。同样,这是因为Web组件的创建方式。以及调用connectedCallback的时间。

但是,在connectedCallback内,对孩子方法的调用有效。这是因为您允许将child元素的时间升级到自定义元素。

  如果你问我,有点傻。我希望在所有孩子升级之后还有另一个函数被调用。但我们与我们的工作合作。

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

答案 1 :(得分:3)

在ShadowDOM模板中使用广告位元素。

custom-elements 添加到ShadowDOM模板是一种不好的做法。每个自定义元素都必须能够不受DOM中其他自定义元素的依赖。在ShadowDOM中使用本机HTML元素没有问题,因为它们将始终有效。

插槽元素

为此,引入了slot element。使用slot元素,您可以在ShadowDOM模板中创建占位符。只需将元素放在DOM中的 custom-element 中即可使用这些占位符。

但是您怎么知道占位符是否已填充元素?

插槽元素可以监听称为slotchange的唯一事件。只要将一个(或多个)元素放置在slot元素的位置,就会触发该事件。

在事件的侦听器内部,您可以使用HTMLSlotElement.assignedNodes()HTMLSlotElement.assignedElements()方法访问占位符中的所有元素。它们返回一个数组,其中元素放置在slot中。

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. I have slots.");
    
    // Select the slot element and listen for the slotchange event.
    const slot = this.shadowRoot.querySelector('slot');
    slot.addEventListener('slotchange', (event) => {
      const children = event.target.assignedElements();
      children.forEach(child => child.shout());
    });

  }
}

customElements.define('parent-element', ParentElement);

class ChildElement extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({mode: 'open'});
    this.shadowRoot.innerHTML = '<h3>Child Element</h3>';
  }

  shout() {
    console.log("I'm a child, placed inside a slot.");
  }

}

customElements.define('child-element', ChildElement);
<parent-element>
  <child-element></child-element>
  <child-element></child-element>
  <child-element></child-element>
</parent-element>

答案 2 :(得分:2)

经过一些工作,我有各种各样的解决方案。

当然this.parentElement在子元素中不起作用;它是影子DOM的根源!

我目前的解决方案,对我的具体情况来说没问题,如下:

<强>父 - element.js

init() {
    //Code to run on initialisation goes here
    this.shadow_root.querySelector("child-element").my_method();
}

儿童-element.js

connectedCallback() {
    this.getRootNode().host.init();
}

因此,在子元素中,我们得到根节点(模板阴影DOM),然后是它的主机,父元素,并调用init(...),此时父节点可以访问子节点并且它是完全定义的。 / p>

由于几个原因,这个解决方案并不理想,所以我并没有将其标记为已被接受。

1)如果有多个孩子要等待或者更深层次的嵌套,那么编排回调会变得更加复杂。

2)我担心child-element的含义,如果我想在独立容量中使用这个元素(即在其他地方,完全独立于嵌套在parent-element中)我将拥有修改它以明确检查getRootNode().hostparent-element的实例。

所以这个解决方案现在可以工作,但感觉很糟糕,我认为当它的整个DOM结构(包括其阴影DOM中的嵌套自定义元素)被初始化时,需要有一个对父进行激活的回调。

答案 3 :(得分:1)

如果要避免由于setTimeout的延迟而引起的任何视觉故障,可以使用MutationObserver

class myWebComponent extends HTMLElement 
{
      connectedCallback() {

        var instance = this;
        var childrenConnectedCallback = () => {
            var addedNode = instance.childNodes[(instance.childNodes.length - 1)];
            /*  callback here */
        }

        var observer = new MutationObserver(childrenConnectedCallback);
        var config = { attributes: false, childList: true, subtree: true };
        observer.observe(instance, config);

        //make sure to disconnect
        setTimeout(() => {
            observer.disconnect();
        }, 0);

     }
}

答案 4 :(得分:0)

我们遇到了非常相关的问题,自定义元素(v1)的connectedCallback中没有孩子。

首先,我们尝试使用一种非常复杂的方法来修复connectedCallback,Google AMP团队也正在使用该方法(mutationObserver并检查nextSibling的组合)最终导致到https://github.com/WebReflection/html-parsed-element

不幸的是,这本身就产生了问题,因此我们回到始终强制执行升级的情况(即,包括仅在页面末尾注册自定义元素的脚本)。

答案 5 :(得分:-2)

document.addEventListener('DOMContentLoaded', defineMyCustomElements);

u可以延迟定义ur类,直到加载dom之后。