对于一次调用,主要分为三个部分,分别是“入口”、“列表”和“出口”。每一个部分都会注册一些监听事件,最终被组合关联在一起,调用 .focus()
,完成“让焦点从入口进入,在列表移动,从出口退出”的任务。
例如,进行一个包含入口、列表和出口的最简单的调用:
focusFly(["#b1", "#b3"], {
onEscape: true,
entry: "#b0",
exit: "#b3",
});
上面的调用里,入口是元素“#b0”,出口是元素“#b3”,列表是一个范围,这个范围的头元素是“#b1”,尾元素是“#b3”。在调用 focusFly
的时候,实际上注册了一些事件:
- 入口,元素 #b0 注册点击(
click
)事件; - 列表,元素 #b1 和 #b3 共同的父元素,注册
click
、focusin
、focusout
、keydown
和mousedown
事件; - 出口,重复利用列表的
click
和focusout
事件。
下面解释每种事件的具体作用,部分内容会结合上面的代码块。
入口的点击事件:触发点击事件后,会对列表的第一个元素调用 .focus()
,结合上面代码块的列表,则是 b1.focus()
。
列表的 keydown
事件:事件会监听键盘按下的按键,让焦点在列表中循环,在上面的代码块里,事件会检查两个元素,分别是列表的头和尾,检测到当前元素是 #b3,并且同时按下了 Tab,将会执行 b1.focus()
;检测到当前元素是 #b1,并且同时按下了 Shift-Tab,将会执行 b3.focus()
。
列表的 mousedown
和 focusin
事件:这两个事件主要作用是焦点矫正,考虑一个场景,有一个列表包含 #b1、#b2、#b3 三个元素,点击元素 #b2 后,焦点聚焦 #b2,这时点击 #b3 之后的空白区域,#b2 会失焦,然后按下 Shift-Tab,由于焦点矫正的功能,焦点回到了 #b2,而不是空白区域的上一个元素 #b3。
出口的 focusout
事件:虽然该事件被注册在列表上,但它的作用是出口,让焦点回到入口,考虑一个场景,一个背景是灰色蒙层的弹窗,弹窗的内部元素是列表,要求点击灰色蒙层的时候,焦点回到“打开”按钮,点击蒙层后,focusout
中会执行延迟 0s 的定时器,在其中调用 document.activeElement
来获取当前聚焦元素,如果该元素不属于列表,则通过 .focus()
让焦点回到入口的“打开”按钮。
出口的 click
事件:该事件被注册在列表上,但作用是出口,让焦点回到入口,该事件会检测被点击的元素是否是出口元素,如果是,将对入口调用 .focus()
,结合上面的代码块,点击 #b3 之后,将调用 b0.focus()