如何将焦点从 iframe 传递到主机页面中的下一个可聚焦元素?
How to pass focus from an iframe to next focusable element in host page?
背景
我有一个 iframe 小部件,它将嵌入到其他页面中 - 我事先不了解主机页面的结构和代码,并且 iframe 将是跨域的并且可能是沙盒。
我的目标是给 iframe 小部件一个单一的制表位 - 在关注它之后有一个专用的组合键来“进入”小部件,但正常的制表符应该跳到主机页面中的下一个可聚焦元素。
问题:在 iframe 获得焦点并检测到新的标签按键后,如何将焦点传递给宿主页面的 next 可聚焦元素?
简单示例:
<!-- host page -->
<button> first button </button>
<iframe src="myIframeWidget.com"/>
<button> second button </button>
在上面,如果第一个按钮获得焦点,那么预期的行为是在第一个选项卡上 iframe 将获得焦点,而在下一个选项卡上第二个按钮将获得焦点(跳过 iframe 内的所有可聚焦元素).
聚焦 iframe 并按下 Tab 键后,我需要将焦点从 iframe 传递到第二个按钮。
备注
- 由于我的 iframe DOM 相当大,我不想将
tabindex=-1
设置为所有内部可聚焦元素。
- 在撰写本文时,aria-hidden="true" 不会从焦点序列中删除元素或其后代。我不知道有任何其他 aria 属性会从焦点序列中删除整个子树。
- 纯 JS 解决方案优于依赖外部包的解决方案。
- 我还需要处理反向制表符,但我假设前向制表符的相同方法将适用 - 如果有任何重要差异,请尽可能在您的回答中提及。
- 有一种边缘情况,即在小部件之后没有任何可聚焦的东西 - 通常在这种情况下,浏览器将聚焦在地址栏按钮上 - 如果有办法处理这个问题,那将是很好的了解,但是这可能应该在一个单独的问题中处理。
谢谢!
比方说,这是主机页面的内容
<button>Main button 1</button>
<button>Main button 2</button>
<iframe id="frame1" name="frame1" src="iframe.html"></iframe>
<button>Main Button 3</button>
现在,当您在 iframe.html
的 iframe 上获得焦点时,在下一个 Tab 键按下时,您希望将焦点传递给 Main Button 3
按钮。您的 iframe.html
上可能有很多值得关注的内容,例如,
<button>I frame button 1</button>
<button>I frame button 2</button>
你想跳过。为此,您可以在 iframe.html
中写一个简单的 javascript 即,
let go_next = false;
document.body.addEventListener('keyup', (e)=> {
if( e.which == 9 ) {
if(go_next){
window.parent.document.getElementById('frame1').nextElementSibling.focus()
go_next=false
}else{
go_next =true;
}
}
});
代码说明
我们不希望焦点立即传递到下一个元素,因为用户也应该能够关注 iframe。因此,脚本假设用户通过声明变量 go_next = false
首次关注 iframe
注意:从 Main Button 2
到 iframe 的焦点计为 iframe.html 内的 Tab 键按下。这就是为什么我们必须忽略 iframe.html
中的第一个 tab 键
因此,就在用户关注 iframe 之后,我们正在创建变量 go_next = true
。现在在下一个 Tab 键按下时,我们可以继续将焦点传递到主机页面的下一个元素。
为此,我们使用 window.parent
抓取主机页面并使用 document.getElementById('frame1')
从主机页面中选择 iframe,使用 nextElementSibling
和聚焦选择 iframe 的下一个元素使用焦点函数的下一个元素。通过焦点后,我们将在下一次再次制作 go_next = false
。
我准备了一个小演示供大家测试here
跨域的替代解决方案
跨域通常会使用 内容脚本策略 阻止对父主机的访问。
为了克服这个问题,我们必须玩点小技巧。
我们将在 iframe.html
页面的末尾创建一个不可见的按钮。例如:
<button id="last_element" style="opacity:0;filter:alpha(opacity=0);">Hello</button>
还记得我们说过可以在 iframe.html 内部访问用于聚焦 iframe 的 Tab 键吗?
好吧,我们将利用这一优势并专注于 iframe.html
的最后一个按钮。为此,请在 iframe.html
的末尾添加此脚本
document.body.addEventListener('keyup', (e)=> {
if( e.which == 9 ) {
document.querySelector("#last_element").focus()
}
});
现在,当 iframe 的最后一个元素获得焦点时,下一个焦点将始终是宿主页面的下一个元素。
这是 codepen
上的演示
背景
我有一个 iframe 小部件,它将嵌入到其他页面中 - 我事先不了解主机页面的结构和代码,并且 iframe 将是跨域的并且可能是沙盒。
我的目标是给 iframe 小部件一个单一的制表位 - 在关注它之后有一个专用的组合键来“进入”小部件,但正常的制表符应该跳到主机页面中的下一个可聚焦元素。
问题:在 iframe 获得焦点并检测到新的标签按键后,如何将焦点传递给宿主页面的 next 可聚焦元素?
简单示例:
<!-- host page -->
<button> first button </button>
<iframe src="myIframeWidget.com"/>
<button> second button </button>
在上面,如果第一个按钮获得焦点,那么预期的行为是在第一个选项卡上 iframe 将获得焦点,而在下一个选项卡上第二个按钮将获得焦点(跳过 iframe 内的所有可聚焦元素).
聚焦 iframe 并按下 Tab 键后,我需要将焦点从 iframe 传递到第二个按钮。
备注
- 由于我的 iframe DOM 相当大,我不想将
tabindex=-1
设置为所有内部可聚焦元素。 - 在撰写本文时,aria-hidden="true" 不会从焦点序列中删除元素或其后代。我不知道有任何其他 aria 属性会从焦点序列中删除整个子树。
- 纯 JS 解决方案优于依赖外部包的解决方案。
- 我还需要处理反向制表符,但我假设前向制表符的相同方法将适用 - 如果有任何重要差异,请尽可能在您的回答中提及。
- 有一种边缘情况,即在小部件之后没有任何可聚焦的东西 - 通常在这种情况下,浏览器将聚焦在地址栏按钮上 - 如果有办法处理这个问题,那将是很好的了解,但是这可能应该在一个单独的问题中处理。
谢谢!
比方说,这是主机页面的内容
<button>Main button 1</button>
<button>Main button 2</button>
<iframe id="frame1" name="frame1" src="iframe.html"></iframe>
<button>Main Button 3</button>
现在,当您在 iframe.html
的 iframe 上获得焦点时,在下一个 Tab 键按下时,您希望将焦点传递给 Main Button 3
按钮。您的 iframe.html
上可能有很多值得关注的内容,例如,
<button>I frame button 1</button>
<button>I frame button 2</button>
你想跳过。为此,您可以在 iframe.html
中写一个简单的 javascript 即,
let go_next = false;
document.body.addEventListener('keyup', (e)=> {
if( e.which == 9 ) {
if(go_next){
window.parent.document.getElementById('frame1').nextElementSibling.focus()
go_next=false
}else{
go_next =true;
}
}
});
代码说明
我们不希望焦点立即传递到下一个元素,因为用户也应该能够关注 iframe。因此,脚本假设用户通过声明变量 go_next = false
注意:从 Main Button 2
到 iframe 的焦点计为 iframe.html 内的 Tab 键按下。这就是为什么我们必须忽略 iframe.html
因此,就在用户关注 iframe 之后,我们正在创建变量 go_next = true
。现在在下一个 Tab 键按下时,我们可以继续将焦点传递到主机页面的下一个元素。
为此,我们使用 window.parent
抓取主机页面并使用 document.getElementById('frame1')
从主机页面中选择 iframe,使用 nextElementSibling
和聚焦选择 iframe 的下一个元素使用焦点函数的下一个元素。通过焦点后,我们将在下一次再次制作 go_next = false
。
我准备了一个小演示供大家测试here
跨域的替代解决方案
跨域通常会使用 内容脚本策略 阻止对父主机的访问。
为了克服这个问题,我们必须玩点小技巧。
我们将在 iframe.html
页面的末尾创建一个不可见的按钮。例如:
<button id="last_element" style="opacity:0;filter:alpha(opacity=0);">Hello</button>
还记得我们说过可以在 iframe.html 内部访问用于聚焦 iframe 的 Tab 键吗?
好吧,我们将利用这一优势并专注于 iframe.html
的最后一个按钮。为此,请在 iframe.html
document.body.addEventListener('keyup', (e)=> {
if( e.which == 9 ) {
document.querySelector("#last_element").focus()
}
});
现在,当 iframe 的最后一个元素获得焦点时,下一个焦点将始终是宿主页面的下一个元素。 这是 codepen
上的演示