DIVでもInput要素のblur的なことがしたい!
InputElementは別のところを触るとBlurイベントを発火してフォーカスが外れます.
MDNのサイトで動作は確認できるかと思います.
これをDIV Elementでもやりたいなぁと言うのが今回の議題です.
今回はBlur的なことをしたくなったSelectorUIを用意しました.https://naporin0624.github.io/napoblog-assets/#/div-blur
ようなUIを考えます.
方針
セレクターUIはdivの中に存在するようになっていて,次のようなDOM構成になっています.
- ContainerDIV
- ButtonDIV
- MenuDIV
- OptionGroupDIV
- OptionDIV
- OptionDIV
- ...
- OptionGroupDIV
ButtonDIVもOptionGroupDIVもOptionDIV触っても親の要素は常にContainerDIVになるので
こちらのclosest
メソッドを使ってクリックされた要素の親にContainerがあるかどうかでフォーカスがはずれたかどうかを検知するようにしていきます!関係ないところをクリックしてもその親にはContainerはいないのでフォーカスは外れる仕組みです!
実装
こんな感じでできるかと思います!InputElementが自分でblurイベントを発火してくれるところを自分でclosestメソッドを使ってblurイベント相当のものを生成しています.
function Selector() { // closestにSelectorをわたすためにContainerのrefをとります const ref = useRef<HTMLDivElement>(null); useEffect(() => { const handler = (e: Event) => { // Blurを制御したい親Elementのselector(クラス)refからを作る const selector = ref.current?.className .split(" ") .map(s => `.${s}`) .join(); // eはクリックしたところから発火されるイベント // eをclosestで比較し,eの親にrefが含まれるか確認する const blur = !(e.target as HTMLElement).closest(selector || ""); // 親にrefが含まれていなければフォーカスを外す if (blur) setIsFocus(false); }; document.addEventListener("click", handler); return () => document.addEventListener("click", handler); }, []); return ( <Container ref={ref}> <Button></Button> <Menu> <OptionGroup> <Option>hoge</Option> <Option>huga</Option> <Option>nyan</Option> </OptionGroup> </Menu> </Container> ) }
ここなんですが
const selector = ref.current?.className .split(" ") .map(s => `.${s}`) .join();
const selector = [...ref.current.classList].map(s => `.${s}`).join()
みたいなことができたんですけど,TSだと怒られましたw
最後に
今回使ったプログラムはここに置いてあります!必要であれば使ってください. github.com
自作のUI(DIVで作ったやつ)でフォーカス制御してみたいなことがあって困っていろいろ模索した結果こんな感じの解にたどり着きました.MDNじろじろ見る癖が少しづつついてきたかなぁって感想と,CSS筋が前よりついてきた気がします.
もっとこうしたらいいよとかあれば@naporin24690に教えてください〜!