現象
アイコンにマウスオンでPopoverを表示、マウスを外したときに非表示にしたかったので、このように実装した。
結果、高速でPopoverが無限に開閉し続け、表示されない状態になった。
補足:
WebComponentとして実装しており、親コンポーネント側にPopoverが出てスタイルが無効化されるのを防ぐため、div要素にrefを使用しています。
const MyComponent: React.FC = () => {
const popoverRef = useRef<HTMLDivElement>(null);
const [isOpen, setIsOpen] = useState<boolean>(false);
const handleClose = (): void => {
setIsOpen(false);
};
const handleOpen = (): void => {
setIsOpen(true);
};
return (
<div ref={popoverRef}>
<PsychologyIcon
onMouseOver={handleOpen}
onMouseLeave={handleClose}
/>
<Popover
open={isOpen}
anchorEl={popoverRef.current}
onClose={handleClose}
container={() => {
return popoverRef.current;
}}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left',
}}
transformOrigin={{
vertical: 'top',
horizontal: 'left',
}}>
<div style={{ padding: '5px' }}>Popoverに表示するテキスト</div>
</Popover>
</div>
)
原因と対応
1. フォーカス可能な要素にaria-hidden="true"を設定してはならない
コンソールを見ると、このようなエラーが出ていた。
Blocked aria-hidden on a <div> element because the element that just received focus must not be hidden from assistive technology users. Avoid using aria-hidden on a focused element or its ancestor. Consider using the inert attribute instead, which will also prevent focus. For more details, see the aria-hidden section of the WAI-ARIA specification at https://w3c.github.io/aria/#aria-hidden.
MDN Web docs にも警告が書いてあるとおり、aria-hidden="true"をフォーカス要素に使用することはできません。
Popoverコンポーネントが動作するときに自動でaria-hidden="true"が挿入されますが、今回、上述の事情によりdivの中に挿入していたので、このようなエラーが発生したようです。
解決策として、inert属性を使う方法もあるようですが、今回は、Popoverコンポーネントが表示されたときに自動的にフォーカスを取得しないように、disableAutoFocus
とdisableEnforceFocus
を追加しました。
<Popover
open={isOpen}
...
transformOrigin={{
vertical: 'top',
horizontal: 'left',
}}
disableAutoFocus // Popoverが表示される際にフォーカスを自動的に取得しないようにする
disableEnforceFocus // フォーカスの強制を無効にする
>
<div style={{ padding: '5px' }}>Popoverに表示するテキスト</div>
</Popover>
2. マウスイベントがPopover上で発生してしまっている
さて、1の対応でコンソールのエラーは表示されなくなりましたが、Popoverが高速で開閉してしまう問題は解決しませんでした。
マウスイベントがPopover上で発生しないように、sx={{ pointerEvents: 'none' }}
を追加したところ、正常に表示されるようになりました。
<Popover
open={isOpen}
...
transformOrigin={{
vertical: 'top',
horizontal: 'left',
}}
disableAutoFocus
disableEnforceFocus
sx={{ pointerEvents: 'none' }} // Popover上でマウスイベントが発生しないようにする
>
<div style={{ padding: '5px' }}>Popoverに表示するテキスト</div>
</Popover>