如何将焦点从 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 传递到第二个按钮。

备注

  1. 由于我的 iframe DOM 相当大,我不想将 tabindex=-1 设置为所有内部可聚焦元素。
  2. 在撰写本文时,aria-hidden="true" 不会从焦点序列中删除元素或其后代。我不知道有任何其他 aria 属性会从焦点序列中删除整个子树。
  3. 纯 JS 解决方案优于依赖外部包的解决方案。
  4. 我还需要处理反向制表符,但我假设前向制表符的相同方法将适用 - 如果有任何重要差异,请尽可能在您的回答中提及。
  5. 有一种边缘情况,即在小部件之后没有任何可聚焦的东西 - 通常在这种情况下,浏览器将聚焦在地址栏按钮上 - 如果有办法处理这个问题,那将是很好的了解,但是这可能应该在一个单独的问题中处理。

谢谢!

比方说,这是主机页面的内容

<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

上的演示