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
_で定義されたメソッドとの対話が必要です。
ただし、connectedCallback
がcustomElement
に対して起動された時点では、_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();
}
}
_
el
はHTMLElement
であり、期待どおり_child-element
_ではないため、これは機能しません。
テンプレート内のすべての子カスタム要素が適切にアタッチされたら、_parent-element
_のコールバックが必要です。
この質問 の解決策は機能していないようです。 _this.parentElement
_は_child-element
_ connectedCallback()
内のnull
です。
イルミオント
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>
さらにいくつかの作業を行った後、ある種の解決策があります。
もちろん、_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構造全体が初期化されるときに、親に対して起動するコールバックが必要だと思います。
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);
}
}
カスタム要素(v1)のconnectedCallback
で子が使用できないという、非常に関連する問題が発生しました。
最初は、Google AMPチームでも使用されている非常に複雑なアプローチ(connectedCallback
とmutationObserver
のチェックの組み合わせ)でnextSibling
を修正しようとしましたが、最終的にはto https://github.com/WebReflection/html-parsed-element
これは残念ながらそれ自体に問題を引き起こしたので、常にアップグレードケースを強制することに戻りました(つまり、ページの最後にのみカスタム要素を登録するスクリプトを含めます)。