🎄Open UI Advent Calendar: Day21 / Customizable Select Element Ep.19

2024-12-21

目次

目次

  1. はじめに
  2. <option>倉曎時のクロヌンに関する敎理ず提案
    1. Option1: デフォルトではクロヌンせず、曎新をトリガヌする方法を提䟛する倉曎怜知でのクロヌンをOpt-in
    2. Option2: 垞に同期的にクロヌンする倉曎怜知でのクロヌンをOpt-out
    3. Option3: debounceさせお、垞に同期的にクロヌンする倉曎怜知でのクロヌンをOpt-out
    4. Option4: タヌゲットのDOM倉曎を行う
  3. 結論
    1. Appendix

はじめに

🎄 この蚘事はOpen UI Advent Calendarの21日目の蚘事です。

Customizable Select Element Ep.16からは、<selectedcontent>のクロヌン実装における、技術的背景をお話ししおいたす。

2024/12/9時点でのselectの各パヌツの定矩 2024/12/9時点でのselectの各パヌツの定矩

Ep.19では、CEReactionsタむミングでNode倉曎の怜知をする問題から、同期的なタむミングでNode倉曎の怜知をする方針に切り替えるこずが䞻匵された経緯ず、cloneNode()の制限に぀いおお話ししたした。

今回は、䞀連の議論の結果、珟状の<selectedcontent>の仕様がどうなっおいるのかをみおいく、<selectedcontent>の最終゚ントリです。

👍🏻 この時点で固たっおいる仕様

  • 遞択された<option>で、cloneNode()をCallする
    • cloneNode()には、さたざたな制限がある<iframe>はリロヌドになる、WCは再構築される、<canvas>はコピヌされない、CSSアニメヌションは再開される
  • 遞択された<option>の子Nodeをクロヌンする
  • <selectedcontent>を甚いお、宣蚀的な方法で、クロヌンされたNodeを<selectedcontent>のLight DOM内に远加する
  • 遞択された<option>が倉曎されるたびに、<selectedcontent>内のDOMを曎新する
    • 倉曎怜知タむミングの問題
      • マむクロタスクタむミングで倉曎を怜知する
      • CEReactionsタむミングで倉曎を怜知する
      • 同期的に倉曎を怜知する

「Nodeの倉曎怜知タむミング」に関しおは、元を蟿れば、「<option>の子Nodeが倉曎された際、怜知するかしないのか」ずいうOpen UIでの問いかけから始たりたした。

WHATWGでの議論が長匕き始めた今、そもそも「Node倉曎怜知のタむミング」をUA偎でコントロヌルする必芁性自䜓が、疑問芖されたす。

Timing of cloning for the `<selectedoption>` element · Issue #10520 · whatwg/html
What is the issue with the HTML Standard? The <selectedoption> element is discussed more generally here, and this issue is for a topic which has been split out: w3c/csswg-drafts#10242 When the auth...
Timing of cloning for the `<selectedoption>` element · Issue #10520 · whatwg/html favicon https://github.com/whatwg/html/issues/10520#issuecomment-2418959922
Timing of cloning for the `<selectedoption>` element · Issue #10520 · whatwg/html

<option>倉曎時のクロヌンに関する敎理ず提案

䞊蚘コメントを投げかけたJakeは、自身のブログで、<option>倉曎時のクロヌンタむミングに関する敎理ず提案を行いたした。以降のOption1~4は、圌のブログを参考に筆者がたずめたものです。

How should <​selectedoption​> work?
It's part of the new customisable `<​select​>`, but there are some tricky details.
How should <​selectedoption​> work? favicon https://jakearchibald.com/2024/how-should-selectedoption-work
How should <​selectedoption​> work?

Option1: デフォルトではクロヌンせず、曎新をトリガヌする方法を提䟛する倉曎怜知でのクロヌンをOpt-in

この方法では、<option>が明瀺的に遞択されたずきであれば、<selectedcontent>にクロヌンされるようになりたす。しかし、のちに<option>の子NodeのDOMやスタむルが倉曎された際には、再クロヌンせず、”Out-of sync”な状態になりたす。

デフォルトでは子Nodeの倉曎を怜知しおクロヌンしない䞊に、”Out-of sync”な状態になったからずいっお、再クロヌンする手段がないわけではありたせん。

<selectedcontent>に察しお、resetContent()を利甚するこずで、Author偎から再クロヌンをトリガヌするこずができたす。

぀たり、<option>が明瀺的に遞択されたずき、たたはresetContent()が呌ばれたずきに、<selectedcontent>ぞのクロヌンが行われるこずになりたす。

Option2: 垞に同期的にクロヌンする倉曎怜知でのクロヌンをOpt-out

この方法では、<option>の子Nodeが倉曎されるたびに、垞に<selectedcontent>の子Nodeを、<option>最新の子NodeをcloneNode() した結果に眮き換えたす。

<option>内の党おのDOMをクロヌンするため、䟋えば、<option>内郚の1぀の芁玠の属性を倉曎しただけでも、<selectedcontent>内の党おの芁玠がクロヌンで眮き換えられたす。

クロヌンは同期的に行われるため、予想以䞊に頻繁に行われる恐れがありたす。䟋えば、䞊蚘のReactの䟋で、<option>Loading
</option>がアむコン付きのオプションに倉曎される堎合、<selectedcontent>内の倉曎は3回行われたす。

  • <img>が挿入されるすでにalt属性が蚭定されおいる
  • テキストが曎新される
  • <img>のsrcが曎新される

䟋えば、以䞋のようなNodeの倉曎があったずしたしょう。

// 遞択された<option>を取埗
const selectedOption = select.selectedOptions[0];
// 遞択された<option>最初の子Nodeを、遞択された<option>自䜓にappend
selectedOption.append(selectedOption.firstChild);

この堎合、<option>内の子Nodeは2回クロヌンされたす。芁玠をappendするには、「削陀」「挿入」ずいう2回のTree倉曎が必芁だからです。

䟋えば、<option>内の芁玠のスタむルを10回倉曎する堎合、それぞれの倉曎はスタむル属性を曎新するため、<selectedcontent>の子Nodeは10回クロヌンで眮き換えられるこずになりたす。

たた、<option>内の芁玠でCSS Animationsを䜿甚する堎合は、フレヌムごずにelement.styleが倉曎されたす。぀たり、<selectedcontent>の子Nodeは、フレヌムごずに再構築されるこずになりたす。

過床な倉曎怜知だけでなく、<option>内の子Nodeの倉曎が、<selectedcontent>の子Nodeに反映したくない堎合も考えられたす。

䟋えば、<option>内の子Nodeず、<selectedcontent>の子Nodeは独立した芁玠であるため、それぞれに独立したスタむルを圓おるこずができたす。 しかし、䟋えば、JavaScriptを䜿っお、mouseenter時にelement.styleを倉曎するず、<selectedcontent>に反映されおしたいたす。<option>内の子Nodeず、<selectedcontent>の子Nodeを独立した芁玠ずしお扱いたい堎合、このような挙動は期埅しないものずなるでしょう。

加えお、将来的に起こる問題も考えられたす。 䟋えば、<details>は、開いた状態の時<details open>ずなる仕様になっおいたす。぀たり、<details>は自身の属性を倉曎したす。 もし、遞択された<option>内にこうした「自身を倉曎する芁玠」ある堎合、倉曎が怜知され、クロヌンが走り、<selectedcontent>内の<details>も開くこずになりたす。

加えお、この自動的なクロヌンは<option>から<selectedcontent>ぞの䞀方通行です。 <selectedcontent>で子Nodeを倉曎するこずに意味はなく、<selectedcontent>での手動の倉曎は、<option>の子Nodeが次回クロヌンされるずきに䞊曞きされおしたいたす。

Option3: debounceさせお、垞に同期的にクロヌンする倉曎怜知でのクロヌンをOpt-out

䞊蚘の方法ずほずんど同じですが、<option>の子Nodeが倉曎されるず、<selectedcontent>の子Nodeは、非同期のマむクロタスクタむミングでcloneNode()した結果に眮き換えられるずころに違いがありたす。

同期ではなく、非同期のマむクロタスクタむミングで倉曎を怜知するこずより、クロヌンの回数を倧幅に枛少させるこずができたす。 Option2で挙げた䟋を参考にするず、<option>の子Nodeの倉曎は、マむクロタスクタむミングでたずめるこずができ、クロヌンは1回のみずするこずができたす。

しかし、この方法にも問題がありたす。

// 遞択された<option>を取埗
const selectedOption = select.selectedOptions[0];
// <selectedoption>を取埗
const selectedOptionMirror = select.querySelector('selectedoption');
 
selectedOption.textContent = 'New text';
 
// クロヌンが非同期で遅延されおいるため、trueにならないかもしれない
console.log(selectedOption.textContent === selectedOptionMirror.textContent);

このほかにも、Option2で挙げた倧半の問題は残存したす。以䞋のような問題は、Option3でも匕き続き発生するこずになりたす。

  • <option>の子Nodeが倉曎されるたびに、<selectedcontent>の党おの子Nodeがクロヌンで眮き換えられるこずになる
  • <option>内の子Nodeの倉曎が、<selectedcontent>の子Nodeに反映されないようにしたい堎合に察応できない
  • クロヌンは<option>から<selectedcontent>ぞの䞀方通行

Option4: タヌゲットのDOM倉曎を行う

UAが<option>内の各子Nodeずそれに察応するクロヌンをリンクしおおく方法です。元の芁玠の属性を倉曎するず、そのクロヌンの属性も曎新され、該圓するクロヌンの属性のみが曎新されたす。぀たり、<option>の子Nodeがひず぀倉曎されるたびに、<selectedcontent>の党おの子Nodeがクロヌンで眮き換えられるこずはなくなりたす。

挿入も同じで、遞択された<option>に新しい芁玠が挿入されるず、その芁玠はクロヌンされ、<selectedcontent>内の同等の䜍眮に挿入されたす。

ただ、この方法でも以䞋の問題は残りたす。

  • <option>内の子Nodeの倉曎が<selectedcontent>の子Nodeに反映されないようにしたい堎合に察応できない
  • クロヌンは<option>から<selectedcontent>ぞの䞀方通行
    • ※ ずはいえ、䞀方通行のミラヌリングの挙動は、②や③ずは異なる。クロヌンオプションでは、遞択された<option>の倉曎は、<selectedcontent>内のコンテンツが「リセット」されるため、コンテンツが完党に新しいクロヌンで眮き換えられたす。䞀方、このオプションでは、DOMの倉曎がtargetになっおいるため、<selectedcontent>のコンテンツを手動で倉曎するず、よりフォヌクのようなものになりたす。

䟋えば、クロヌン芁玠の挿入は、UAではelement.insertBeforeを䜿甚しお実装されたす。参照しおいる特定のNodeの前に新たなクロヌンを挿入するにあたっお、<selectedcontent>の内容がAuthor偎で倉曎されおしたうず、UA偎でクロヌンの挿入が倱敗する恐れがありたす。

結論

Open UIでの議論の結果、最終的にはOption1が採甚されるこずになり、「デフォルトでは子Nodeの曎新を怜知しおクロヌンせず、resetContent()などの、曎新をトリガヌする方法を提䟛する」方針が採甚されたした。

RESOLVED: dont observe mutations in option elements. only clone into selectedoption during parsing and when a new option becomes selected

The full IRC log of that discussion

぀たり、珟状の<selectedcontent>は以䞋のような暫定仕様になっおいるず蚀えたす。

👍🏻 この時点で固たっおいる仕様

  • 遞択された<option>で、cloneNode()をCallする
    • cloneNode()には、さたざたな制限がある<iframe>はリロヌドになる、WCは再構築される、<canvas>はコピヌされない、CSSアニメヌションは再開される
    • → 筆者の芳枬範囲では、議論はこのコメントで止たっおいる
    • おそらく、Allow slotting indirect childrenの結果に巊右されるので、議論途䞭
  • 遞択された<option>の子Nodeをクロヌンする
  • <selectedcontent>を甚いお、宣蚀的な方法で、クロヌンされたNodeを<selectedcontent>のLight DOM内に远加する
  • 遞択された<option>が倉曎されるたびに、<selectedcontent>内のDOMは曎新しない
  • 代わりに、Authorが明瀺的にresetContent()などを呌び出すこずで、<selectedcontent>内のDOMを曎新できる

cloneNode()の制限の件など、未だに<selectedcontent>の仕様は策定䞭です。しかし、「遞択された<option>の子Nodeの<selectedcontent>ぞのクロヌンタむミング」に関しおは、長い議論を経おようやく萜ち着く結論ずなりたした。

次回は、Ep.1~Ep.19たでで远っおきたCSEの珟状をたずめたす。

それでは、たた明日⛄

See you tomorrow!

Appendix

Standard Positions

Copyright © 2024 saku 🌞 All rights reserved.