Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
155 views
in Technique[技术] by (71.8m points)

javascript - How to have a 'connectedCallback' for when all child custom elements have been connected

I'm using Web Components v1.

Suppose two Custom Elements:

parent-element.html

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

child-element.html

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

I'm trying to use connectedCallback in parent-element to initialise the entire parent/child DOM structure when it is attached, which requires interaction with methods defined in child-element.

However, it seems child-element isn't properly defined at the time connectedCallback gets fired for customElement:

parent-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();
    }
}

This will not work, because el is an HTMLElement and not a child-element as expected.

I need a callback for parent-element once all child custom elements in its template have been properly attached.

The solution in this question does not seem to work; this.parentElement is null inside child-element connectedCallback().

ilmiont

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Use slot elements in your ShadowDOM template.

Build your custom elements in a way so that they can live in any context, like as a child or parent element, without having any dependencies with other custom-elements. This approach will give you a modular design in which you can utilize your custom-elements in any situation.

But you still want to do something whenever a child element is present, like selecting them or calling a method on a child.

Slot elements

To tackle this the <slot> element has been introduced. With slot elements you can create placeholders inside your ShadowDOM template. These placeholders can be used by simply placing an element inside your custom-element as a child in the DOM. The child element will then be placed inside the position where the <slot> element is placed.

But how do you know if a placeholder has been filled with an element?

Slot elements can listen to a unique event called slotchange. This will be fired whenever an element is (or multiple elements are) placed on the position of the slot element.

Inside the listener of the event you can access all of the element in the placeholder with the HTMLSlotElement.assignedNodes() or HTMLSlotElement.assignedElements() methods. These return an array with the elements placed in the slot.

Now you can wait for the children to be placed inside the slot and do something with the children that are present.

This way allows you to only manipulate the DOM and leave the ShadowDOM alone and let it do its work. Just like you would do with regular HTML elements.

Will the event wait for all child elements to be connected?

Yes, the slotchange event is fired after all connectedCallback methods of the custom elements have been called. This means no racing conditions or missing setup when listening to the event.

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);
<parent-element>
  <child-element></child-element>
  <child-element></child-element>
  <child-element></child-element>
</parent-element>

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...