首页 文章

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

提问于
浏览
11

我'm working on a single-page application (SPA) where we'使用HTML 5 history.pushState重新模拟多页面应用程序 . 它在视觉上看起来很好,但它可以在任何屏幕阅读器中工作,但画外音是我首先尝试的 . )

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

1.html

<!DOCTYPE html><html>
<body>This is page 1. <a href=2.html>Click here for page 2.</a></body>
</html>

2.html

<!DOCTYPE html><html>
<body>This is page 2. <a href=1.html>Click here for page 1.</a></body>
</html>

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

加载网页 . 这是第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>

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>

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();
};

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

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

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

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

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

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

3 回答

  • 1

    Jorgen's answer,基于another StackOverflow thread让我走上正轨 .

    实际修复不是将整个页面包装在 <div tabindex=-1> 中,而是围绕第一部分("This is page N")创建一个 <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();
    }
    

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

  • 0

    这是对Dan Fabulich's answer的回应的进一步改进 .

    问题是关于用 pushState() 更新页面,所以我将从包含要更新的主要内容区域的语义HTML结构开始 .

    <body>
        <header role="banner">
            <nav role="navigation">
                <a href="page1.html" onclick="updatePage(1, 'Page 1', this.href); return false;" aria-current="true">Page 1</a> |
                <a href="page2.html" onclick="updatePage(2, 'Page 2', this.href); return false;">Page 2</a>
            </nav>
        </header>
        <main role="main">
            <!-- This heading text is immediately after the nav, so it's a logical place to set focus. -->
            <h1 id="maintitle" tabindex="-1">Page 1</h1>
            <div id="maincontent">
    <p>Lorem ipsum</p>
            </div>
        </main>
        <footer role="contentinfo">&copy; 2017</footer>
    </body>
    

    JavaScript的:

    function updatePage(pageNumber, pageTitle, pagePath) {
        var mainContent;
        if (pageNumber == 1) {
            mainContent = '<p>Lorem ipsum</p>';
        } else if (pageNumber == 2) {
            mainContent = '<p>Dolor sit amet</p>';
        } else {
            return;
        }
        document.getElementById('maincontent').innerHTML = mainContent;
    
        // TODO: Update the address bar with pushState()
    
        // TODO: Update the nav links, so only one link has aria-current="true"
    
        // Keep the browser title bar in sync with the content.
        document.title = pageTitle + ' - My Example Site';
    
        // Set focus on the visible heading, so screen readers will announce it.
        var mainTitleElement = document.getElementById('maintitle');
        mainTitleElement.innerHTML = pageTitle;
        mainTitleElement.focus(); // TODO: Add a 0ms timer, to ensure the DOM is ready.
    }
    

    关于焦点轮廓(也称为焦点环):

    • 不要 blur() . 屏幕阅读器通常可以恢复,但他们可以_WCAG禁止 .

    • 比模糊更糟糕的是CSS span {outline:none} ,但这是有风险的 . 我只会在带有 tabindex=-1 的静态文本上使用它,并且对于有视力的键盘用户来说它会迷失方向 .

    • 我见过的最佳解决方案是Alice Boxhall's input modality prollyfill .

  • 1

    我没有iPhone,但另一个stackoverflow thread提供了解决方案 . 将内容包装在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 = '<div tabindex="-1" id="page' + page + '">This is page ' + page +
            '.\n<a href='spa'+otherPage+'.html'>Click here for page ' +
            otherPage + '.</a></div>';
    
        document.getElementById('page' + page).focus();
        attachHandler();
    }
    

相关问题