在单页pushState Web应用程序中模拟画外音页面加载

Dan*_*ich 11 javascript ios voiceover wai-aria pushstate

我正在开发一个单页面应用程序(SPA),我们正在使用HTML 5模拟多页面应用程序history.pushState.它在视觉上看起来很好,但在iOS Voiceover中表现不正常.(我认为它不适用于任何屏幕阅读器,但是我首先尝试使用画外音.)

这是我想要实现的行为的一个例子.这是两个普通的网页:

1.html

<!DOCTYPE html><html>
<body>This is page 1. <a href=2.html>Click here for page 2.</a></body>
</html>
Run Code Online (Sandbox Code Playgroud)

2.html

<!DOCTYPE html><html>
<body>This is page 2. <a href=1.html>Click here for page 1.</a></body>
</html>
Run Code Online (Sandbox Code Playgroud)

很好,很简单.画外音如下所示:

网页已加载.这是第1页.
[向右滑动]单击此处查看第2页.链接.
[双击]加载网页.这是第2页.
[向右滑动]单击此处查看第1页.访问过.链接.
[双击]加载网页.这是第1页.

这里它再次作为单页面应用程序,使用历史记录操作来模拟实际的页面加载.

spa1.html

<!DOCTYPE html><html>
<body>This is page 1.
<a href='spa2.html'>Click here for page 2.</a></body>
<script src="switchPage.js"></script>
</html>
Run Code Online (Sandbox Code Playgroud)

spa2.html

<!DOCTYPE html><html>
<body>This is page 2.
<a href='spa1.html'>Click here for page 1.</a></body>
<script src="switchPage.js"></script>
</html>
Run Code Online (Sandbox Code Playgroud)

switchPage.js

console.log("load");
attachHandler();

function attachHandler() {
    document.querySelector('a').onclick = function(event) {
        event.preventDefault();
        history.pushState(null, null, this.href);
        drawPage();
    }
}

function drawPage() {
    var page, otherPage;
    // in real life, we'd do an AJAX call here or something
    if (/spa1.html$/.test(window.location.pathname)) {
        page = 1;
        otherPage = 2;
    } else {
        page = 2;
        otherPage = 1;
    }
    document.body.innerHTML = "This is page " + page +
        ".\n<a href='spa"+otherPage+".html'>Click here for page " +
        otherPage + ".</a>";
    attachHandler();
}

window.onpopstate = function() {
    drawPage();
};
Run Code Online (Sandbox Code Playgroud)

(请注意,此示例不适用于文件系统;您必须从Web服务器加载它.)

这个SPA示例在视觉上看起来与简单的多页面示例完全相同,只是第2页"加载速度更快"(因为它根本没有真正加载;它在JS中都发生了).

但在画外音方面,它并没有做正确的事情.

网页已加载.这是第1页.
[向右滑动]单击此处查看第2页.链接.
[双击]点击此处查看第1页.访问.链接.
[重点是链接!向左滑动]这是第2页.
[向右滑动]单击此处查看第1页.访问.链接.
[双击]加载网页.这是第1页.

重点是链接,它应位于页面顶部.

如何告诉Voiceover整个页面刚刚更新,读者应该从页面顶部继续阅读?

Dan*_*ich 1

Jorgen 的答案基于 另一个 StackOverflow 线程,让我走上了正确的道路。

实际的修复方法不是将整个页面包装在 a 中,而是仅围绕第一部分(“这是第 N 页”)<div tabindex=-1>创建 a并将其聚焦。<span tabindex=-1>

function drawPage() {
    var page, otherPage;
    // in real life, we'd do an AJAX call here or something
    if (/spa1.html$/.test(window.location.pathname)) {
        page = 1;
        otherPage = 2;
    } else {
        page = 2;
        otherPage = 1;
    }
    document.body.innerHTML = '<span tabindex="-1" id="page' + page + '">This is page ' + page +
        '.</span>\n<a href="spa'+otherPage+'.html">Click here for page ' +
        otherPage + '.</a>';

    document.getElementById('page' + page).focus();
    setTimeout(function() {
        document.getElementById('page' + page).blur();
    }, 0);
    attachHandler();
}
Run Code Online (Sandbox Code Playgroud)

请注意,在此示例中,我们还在超时中模糊了焦点;否则,非屏幕阅读器浏览器将在跨度周围绘制可见的蓝色焦点矩形,这不是我们想要的。模糊焦点不会影响 iOS VO 阅读器的焦点。