Closed10

connectedCallbackの呼び出される順番を見てみる

suzuki-navisuzuki-navi

Web Componentsの仕組みを深堀したくて、試してみる。

試してみたブラウザはChrome 114。

suzuki-navisuzuki-navi

以下のようなHTMLファイルを用意して、ブラウザで開いてみる。

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>sample</title>
<script>
function log(message) {
  document.getElementById("log").innerHTML += message + "<br/>";
}

class SampleElement extends HTMLElement {
  connectedCallback () {
    log(`connected: ${this.id}`);
  }
}

customElements.define("sample-elem", SampleElement);
</script>
</head>
<body>
  <div id="log"></div>
  <sample-elem id="sample-1"></sample-elem>
  <script>
    log("last of body");
  </script>
</body>
</html>

ログをコピペしやすいように、開発者ツールのコンソールではなく <div id="log"></div> にログ出力するようにしている。

suzuki-navisuzuki-navi

上のHTMLだと、以下のような表示になる。

connected: sample-1
last of body

HTMLに直接書いたElementは、そのHTMLコード断片を解析したそばから connectedCallback が呼び出される。

suzuki-navisuzuki-navi

クラス定義を <script defer> にしてみる。

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>sample</title>
<script>
function log(message) {
  document.getElementById("log").innerHTML += message + "<br/>";
}
</script>

<script defer>
class SampleElement extends HTMLElement {
  connectedCallback () {
    log(`connected: ${this.id}`);
  }
}

customElements.define("sample-elem", SampleElement);
</script>
</head>
<body>
  <div id="log"></div>
  <sample-elem id="sample-1"></sample-elem>
  <script>
    log("last of body");
  </script>
</body>
</html>
connected: sample-1
last of body

実行順序が変わらなかった。

https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script

This attribute must not be used if the src attribute is absent (i.e. for inline scripts), in this case it would have no effect.

srcがないdeferは無意味らしい。

suzuki-navisuzuki-navi

クラス定義を <script type="module"> にしてみる。

last of body
connected: sample-1

type="module" ならば src がなくてもdeferの扱いになるようだ。

customElements.define がDOMの解析が終わった後でも、 connectedCallback が後から呼び出される。

suzuki-navisuzuki-navi
  <sample-elem id="sample-1">
    <sample-elem id="sample-2"></sample-elem>
  </sample-elem>

要素を入れ子に2つにすると、こうなる。

last of body
connected: sample-1
connected: sample-2

connectedCallback は親要素から順に呼ばれるようだ。

suzuki-navisuzuki-navi

要素をHTMLに静的に書くのではなく、JavaScriptから動的に挿入してみる。

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>sample</title>
<script>
function log(message) {
  document.getElementById("log").innerHTML += message + "<br/>";
}
</script>

<script type="module">
class SampleElement extends HTMLElement {
  connectedCallback () {
    log(`connected: ${this.id}`);
  }
}

customElements.define("sample-elem", SampleElement);

log("before createElement");
const sampleElement1 = document.createElement("sample-elem");
sampleElement1.id = "sample-1";
log("before append");
document.querySelector("body").append(sampleElement1);
log("after append");

</script>
</head>
<body>
  <div id="log"></div>
  <script>
    log("last of body");
  </script>
</body>
</html>
last of body
before createElement
before append
connected: sample-1
after append

connectedCallbackは、createElementよりも後で、appendの直前に呼び出される。

suzuki-navisuzuki-navi
const sampleElement1 = document.createElement("sample-elem");
sampleElement1.id = "sample-1";
document.querySelector("body").appendChild(sampleElement1);

const sampleElement2 = document.createElement("sample-elem");
sampleElement2.id = "sample-2";
document.querySelector("#sample-1").appendChild(sampleElement2);
last of body
connected: sample-1
connected: sample-2

これは想定通り。

suzuki-navisuzuki-navi

子要素を入れ子にしてから、親要素ごとまとめて一度だけdocumentにappendした場合にどうなるか。

const sampleElement1 = document.createElement("sample-elem");
sampleElement1.id = "sample-1";

const sampleElement2 = document.createElement("sample-elem");
sampleElement2.id = "sample-2";
log("before append sample-2");
sampleElement1.append(sampleElement2);
log("after append sample-2");

log("before append sample-1");
document.querySelector("body").append(sampleElement1);
log("after append sample-1");
last of body
before append sample-2
after append sample-2
before append sample-1
connected: sample-1
connected: sample-2
after append sample-1

documentにappendしたタイミングで、親要素から順にconnectedCallbackが呼び出される。createElementした順番は関係ないし、まだconnectされていない要素にappendしてもconnectedCallbackは呼び出されない。

このスクラップは2023/07/18にクローズされました