web-dev-qa-db-ja.com

すべての子カスタム要素が接続されたときに「connectedCallback」を作成する方法

Web Componentsv1を使用しています。

2つのカスタム要素を想定します。

parent-element.html

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

child-element.html

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

_parent-element_でconnectedCallbackを使用して、接続時に親/子DOM構造全体を初期化しようとしています。これには、_child-element_で定義されたメソッドとの対話が必要です。

ただし、connectedCallbackcustomElementに対して起動された時点では、_child-element_が適切に定義されていないようです。

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

elHTMLElementであり、期待どおり_child-element_ではないため、これは機能しません。

テンプレート内のすべての子カスタム要素が適切にアタッチされたら、_parent-element_のコールバックが必要です。

この質問 の解決策は機能していないようです。 _this.parentElement_は_child-element_ connectedCallback()内のnullです。

イルミオント

15
Ilmiont

connectedCallbackにはタイミングの問題があります。カスタム要素の子がアップグレードされる前に、初めて呼び出されます。 <child-element>は、connectedCallbackが呼び出されたときのHTMLElementのみです。

アップグレードされた子要素を取得するには、タイムアウトでそれを行う必要があります。

以下のコードを実行して、コンソールの出力を確認します。子のメソッドを呼び出そうとすると失敗します。繰り返しますが、これは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);
<parent-element></parent-element>
4
Intervalia

さらにいくつかの作業を行った後、ある種の解決策があります。

もちろん、_this.parentElement_は子要素では機能しません。それはシャドウDOMのルートにあります!

私の現在の解決策は、私の特定のシナリオでは問題ありませんが、次のとおりです。

parent-element.js

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

child-element.js

_connectedCallback() {
    this.getRootNode().Host.init();
}
_

したがって、子要素では、ルートノード(テンプレートシャドウDOM)を取得し、次にそのホストである親要素を取得し、init(...)を呼び出します。この時点で、親は子にアクセスでき、完全に定義されます。

このソリューションはいくつかの理由で理想的ではないため、承認済みとしてマークしていません。

1)待機する子が複数ある場合、またはより深いネストがある場合、コールバックを調整するのはより複雑になります。

2)_child-element_の影響が心配です。この要素をスタンドアロンで使用したい場合(つまり、_parent-element_にネストされているのとはまったく別の場所)、変更する必要があります。 getRootNode().Hostが_parent-element_のインスタンスであるかどうかを明示的にチェックします。

したがって、このソリューションは今のところ機能しますが、気分が悪く、シャドウDOMにネストされたカスタム要素を含むDOM構造全体が初期化されるときに、親に対して起動するコールバックが必要だと思います。

2
Ilmiont

SetTimeoutの遅延によって引き起こされる視覚的な不具合を回避したい場合は、 MutationObserver を使用できます。

class myWebComponent extends HTMLElement 
{
      connectedCallback() {

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

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

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

     }
}
1
Jordan Daigle

カスタム要素(v1)のconnectedCallbackで子が使用できないという、非常に関連する問題が発生しました。

最初は、Google AMPチームでも使用されている非常に複雑なアプローチ(connectedCallbackmutationObserverのチェックの組み合わせ)でnextSiblingを修正しようとしましたが、最終的にはto https://github.com/WebReflection/html-parsed-element

これは残念ながらそれ自体に問題を引き起こしたので、常にアップグレードケースを強制することに戻りました(つまり、ページの最後にのみカスタム要素を登録するスクリプトを含めます)。

0
connexo