【问题标题】:How to pass focus from an iframe to next focusable element in host page?如何将焦点从 iframe 传递到主机页面中的下一个可聚焦元素?
【发布时间】:2022-08-11 21:37:48
【问题描述】:

背景

我有一个 iframe 小部件,它将嵌入到其他页面中 - 我对主机页面的结构和代码一无所知,并且 iframe 将是跨域的并且可能是沙盒化的。

我的目标是为 iframe 小部件提供一个制表位 - 关注它之后,有一个专用的组合键可以“输入”小部件,但正常的制表符应该向前跳到主机页面中的下一个可聚焦元素。

问题:在 iframe 获得焦点并检测到新的 tab 按键后,如何将焦点传递给主机页面的下一个可聚焦元素?

简单的例子:

<!-- host page -->
<button> first button </button>
<iframe src=\"myIframeWidget.com\"/>
<button> second button </button>

在上面,如果第一个按钮获得焦点,那么预期的行为是在第一个选项卡上 iframe 将获得焦点,在下一个选项卡上第二个按钮将获得焦点(跳过 iframe 内的所有可聚焦元素)。

一旦 iframe 获得焦点并按下选项卡,我需要将焦点从 iframe 传递到第二个按钮。

笔记

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

    谢谢!

  • 由于浏览器在使用 TAB / SHIFT + TAB 时需要确定这一点,因此很奇怪钩子没有通过浏览器 API 公开

标签: javascript iframe focus keyboard-focus


【解决方案1】:

假设这是主机页面的内容

<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 上获得焦点时,即iframe.html,在下一次按 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 内的制表符按下。这就是为什么我们必须忽略 iframe.html 中的第一个选项卡按下

因此,就在用户关注 iframe 之后,我们正在创建变量 go_next = true。现在在下一个标签按下时,我们可以继续将焦点传递给主机页面的下一个元素。

为此,我们使用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 的 tab press 可以在 iframe.html 中访问吗? 好吧,我们将利用这一优势,专注于iframe.html 的最后一个按钮。为此,请将此脚本添加到 iframe.html 的末尾

document.body.addEventListener('keyup', (e)=> {
    if( e.which == 9 ) {
        document.querySelector("#last_element").focus()
    }
});

现在,由于 iframe 的最后一个元素被聚焦,下一个焦点将始终是主机页面的下一个元素。 这是codepen上的演示

【讨论】:

  • 谢谢 - 跨域的替代方案是我正在寻找的。为了使这个答案完美(即值得赏金),您能否也解决以下两个问题? (1) 最后的不可见元素在 iframe 小部件内应该是不可聚焦的(设置 tabindex=-1 等) - 对吧? (2) 处理后退制表符 (shift+tab) - 我认为这将需要一个对称的解决方案(开始时的不可见按钮)?除非有更简单的方法...
  • 是的,如果整个 iframe 设置为 tabindex=-1,那么 iframe 将不会被聚焦,也不会是不可见的按钮。事实上,整个小部件将被跳过。但是,如果 iframe 通过 tab 键获得焦点,那么隐形按钮肯定也会获得焦点。对于第二个问题,是的,因为我们无法真正预测用户在关注 iframe 之后接下来会做什么,所以最好的解决方案是在开始时设置一个不可见的按钮。
  • 感谢您的确认。关于第一个问题 - 我的意思是,在用户按下专用组合键“进入”小部件之前,不可见按钮应该是可聚焦的。当用户与小部件的内容进行交互时,不可见的按钮应该变得无法聚焦(这样用户就不会无意中点击它)。
  • 好吧,我猜,如果用户至少通过单击与 iframe 交互,您希望用户能够使用选项卡在 iframe 内导航,对吗?如果是这种情况,我们可以在 iframe 内容中监听点击事件,如果有点击事件发生,将不可见按钮的tabindex 属性设置为-1,以确保用户再次按 Tab 时,不可见按钮没有获得焦点。
【解决方案2】:

这种围绕 iframe 内容移动焦点的方法本质上会将仅使用键盘的用户困在 iframe 的一侧,从而导致 WCAG 违规(2.1.1 和 2.1.2)。从可访问性的角度来看,应该避免这种方法。

如果您想为用户提供绕过 iframe 的方法,理想情况下,您应在 iframe 前后提供“跳过”链接,以允许用户跳过 iframe 内容。您希望使用跳过链接“预订” iframe,以便仅使用键盘的用户可以使用 TAB 或 SHIFT-TAB 导航。

【讨论】:

    猜你喜欢
    • 2016-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-04
    • 1970-01-01
    • 2015-04-30
    • 1970-01-01
    相关资源
    最近更新 更多