label
要素のfor
属性で、対象となるinput
要素のid
属性を指定した時、その対象は後からDOMツリーに差し込まれたものでも有効になります。
こんなんで検証しました。
<!doctype html>
<button onclick="removeInput()">Remove input</button>
<button onclick="insertInput()">Insert input</button>
<label for="input">label</label>
<input id="input">
<script>
inputs = document.getElementsByTagName("input");
function removeInput() {
console.info(inputs.length, inputs);
for (var l = inputs.length, i = l - 1; i >= 0; i--) {
var input = inputs[i];
input.parentNode.removeChild(input);
}
}
function insertInput() {
console.info(inputs);
var input = document.createElement("input");
input.id = "input";
document.body.appendChild(input);
}
</script>
「Remove input」ボタンを押してinput
を削除した後に「Insert input」ボタンを押してDOMツリーに挿入しても、label
をクリックすると追加されたinput
にフォーカスが当たります。
でなんなの、便利でよかったね? という話に見えるかも知れませんが、**カスタムエレメントを作る時に気を付けることが増えます。**似たようなインターフェイスの要素を作る時は、(MutationObserver
とかで)DOMツリーを監視して、後から追加される要素にも対応できるようにしておいた方が、ユーザー(タグを書く人)の期待に沿うことが多いでしょう。
もちろん、こういう監視がたくさん生まれてるとパフォーマンスが心配になってきますね。だから、現実的には「後から追加された要素にも対応する」フラグの属性を定義して、デフォルトでオフにしておくのがいいかも知れません。
余談ですが、このような「後からの追加を監視するか否か」という状態を両方持つ物を僕達はよく知っています。DOMのNodeList
です。
Node.chideNodes
やDocument.getElementsByTagName()
なんかで取得したNodeList
は後からの追加を監視しているので、DOMツリーの変更に応じて動的に内容が変わります(なので削除しながら順に処理する場合なんかは最初じゃなくて最後から処理する必要があったりする)。
一方でDocument.querySelectorAll()
で返すNodeList
は監視しません。例えばvar divs = document.querySelector("div")
とNodeList
を取得した後に、div
を足したり追加したりしても、divs
変数の内容は変わりません。
そしてこの二つ、区別するAPIがありません……おっと愚痴になってしまいました。
のでまあ、後から監視しなくても「完全に期待に反している」わけではないのでまあいいかなと思います。
NodeList
の「後からのDOMツリーの変更への対応」はliveコレクションか否か、ということで例えばMDNに載っています:https://developer.mozilla.org/ja/docs/Web/API/NodeList