在开发应用程序时,前端供应商很少关注用户如何使用浏览器提供的键盘功能。 我也不例外,但是有一天,我通过按“ Tab”和“ Shift + Tab”获得了有关UX和过渡的一项任务。
该任务的本质是透明且干净的:有一个界面,其界面显示在下面。 从概念上讲,一页可以包含2种不同的形式,并且要求“使用Tabs运行不能从一种形式转换为另一种形式”。

如果浏览器知道如何“本地”阻止表单焦点,那么一切都会很好。 下图显示了一个示例,其中橙色的“ border” om标记当前元素,而灰色的则是前一个元素。


如您所见,“本机”行为不符合要求。 因此,让我们解决这个问题。 解决方案并不复杂,因此请考虑一下。
如果有一些“门”可以阻止焦点从最后一个带有
“ tabindex”的元素(带有“ Tab”)或第一个(带有“ Shift + Tab”)的元素
“跳出”,或者默认情况下支持焦点,那将是理想的。 因此,本质很简单:我们的“门”是隐藏的“输入元素”,它们在“ onFocus”事件期间作为参数接收“事件”事件,并将焦点返回到它来自的元素。 下图。

使用“事件”对象的“ relatedTarget”属性来获取上一个元素是可行的。 可视化以下解决方案。


这是代码本身。 值得注意的是,没有“ ES6 +”语法,因为其思想是在不连接Babel等不同“编译器”的情况下,使用不同的浏览器支持代码。
function getGateInput(handleTabOut) { var input = document.createElement("input");
没什么复杂的:创建“输入”,设置样式以“隐藏”我们的“门”。 此处,由于浏览器未将“制表符”集中在此类元素上,因此未使用“显示:无”。 由于此行为,需要使该元素透明并将其移出浏览器窗口。
function getTabOutHandler(element, GATES) { return function(event) { var relatedTarget = event.relatedTarget || event.fromElement; var target = event.target; var gatesTrapped = target === GATES[0] || target === GATES[1]; if (gatesTrapped && isChild(relatedTarget, element)) { event.preventDefault(); relatedTarget.focus(); } }; }
要将焦点返回到上一个项目,请使用getTabOutHandler。 这就是
HOC 。 它的第一个参数是我们的容器(在其周围设置“门”),第二个参数期望使用getGateInput创建的“门”数组。 该函数返回事件处理程序,该事件处理程序根据上述原理工作。
为了使焦点进入容器,我们需要打开和关闭“门”。 我们将通过设置
“ tabindex”属性来做到这一点。 (-1-不使用制表符聚焦,0-根据流聚焦)
function moveGates(open, GATES) { GATES[0].setAttribute("tabindex", open ? -1 : 0); GATES[1].setAttribute("tabindex", open ? -1 : 0); }
为了控制门,我们将设置一个处理程序,该处理程序将“监听” Tab(代码9),如果焦点元素(activeElement)在容器内,则关闭“门”,否则将其打开。
window.addEventListener("keydown", function(event) { if (event.keyCode === 9) { if (isChild(document.activeElement, element)) { moveGates(false, GATES); } else { moveGates(true, GATES); } } });
合计
考虑了一种将焦点锁定在表单中的方法,该方法包括将焦点返回到先前的焦点元素。 为了“捕获”焦点,我们使用了隐藏的“输入元素”,使用
“ tabindex”控制了焦点。 上面的代码是我为解决问题而编写的
tab-out-catcher库的一部分。 使用示例可以在
这里找到。 还有一个针对React应用程序的
解决方案 。