Web Component内でのfocusイベントの扱い
bubblingしないはずのfocusイベントの話。
こういうHTMLの構造を持ったWeb Componentがあるとします。
<x-input>
#shadow-root
<input>Button</input>
</x-input>
Shadow DOMの要素をfocusした時のactiveElement
Shadow DOM内のinput
はbutton
でもselect
でも良いですが、ともかくこれはインタラクティブな要素なため、focusすることが可能です。
この時document.activeElement
は何を指すかと言うと、x-input
になります。
<x-input>
にtabindexは付いていないので、それ自体がインタラクティブではないはずですが、LightDOMから見たらアクティブなのはコンポーネントそのものっぽい。
誰が反応しているのか
面白いので他の方法でも試してみましょう。Custom ElementとShadow DOM内のボタン、両方にfocus
イベントの時に反応するリスナーを仕込んでみます。
<x-input onfocus="console.log('x!');">
#shadow-root
<input onfocus="console.log('b!');" />
</x-input>
Light DOMの上ではfocus
イベントはbubblingしません。よって、もしこれがLight DOM上での親子関係だった場合、通常はボタンにfocusした場合でも親要素は反応しません。
Web Componentでの結果は
x!
b!
両方反応しました。
次に、focus
した時のターゲットはどの要素ということになっているのでしょうか。
<x-input onfocus="(e) => console.log(e.target);">
#shadow-root
<input onfocus="(e) => console.log(e.target);" />
</x-input>
結果は
Focus Event {...}
target: x-input
Focus Event {...}
target: x-input
targetまで一緒やん。
ちなみにcssだとどうでしょうか。
:focus
に反応するcssを付与し、<x-input>
の中のinput
要素にフォーカスを合わせてみます。
:host(:focus) {
color: #fff;
}
input:focus {
background: #f00;
}
内部のinput
のみ反応しています。不思議ですね。
挙動まとめ
- Shadow DOM内のの要素をfocusした時、挙動としては要素とそれを提供するCustom Element両方が反応している
-
focus
イベントのevent.target
はShadow DOM側でもCustom Element側でもCustom Elementを指す - CSSは内部のみ反応する
複数あるfocusable elementを外部から特定できるのか
上記のことを踏まえると、同じShadow DOM内に複数のfocusable elementがあったとしても、document.activeElement
は常にCustom Elementを指します。これではどの要素にfocusが当たったか調べたい時に見分けが付きません。そんな需要ある?でも想定してみます。
import lit from 'lit';
...
onFocus(e) {
this.dispatchEvent(nwe CustomEvent('focus-shadow', { data: this.shadowRoot?.activeElement }));
}
render() {
return html`
<div>
<input id="one" @focus=${this.onFocus} />
<input id="two" @focus=${this.onFocus} />
<input id="three" @focus=${this.onFocus} />
</div>
`
}
...
こうすることで、外部にイベントが渡った時にevent.data
は直接Shadow DOM内のfocusされた要素を指しています。ちなみにfocus
イベントをそのままdispatchするとx-input
から2回focus
が発火することになるため、混線します。
Web Componentのfocus
の扱いについては以上になります。実際のところ他のイベントでも似たような挙動ではあるのですが、focus
は本来bubblingしないために混乱することがあるかもしれません。ご参考まで。
Discussion