简体   繁体   English

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

[英]Simulate Voiceover page load in single-page pushState web application

I'm working on a single-page application (SPA) where we're simulating a multi-page application with HTML 5 history.pushState . 我正在开发一个单页面应用程序(SPA),我们正在使用HTML 5 history.pushState模拟多页面应用程序。 It looks fine visually, but it's not behaving correctly in iOS Voiceover. 它在视觉上看起来很好,但在iOS Voiceover中表现不正常。 (I assume it wouldn't work in any screen reader, but Voiceover is what I'm trying first.) (我认为它不适用于任何屏幕阅读器,但是我首先尝试使用画外音。)

Here's an example of the behavior I'm trying to achieve. 这是我想要实现的行为的一个例子。 Here are two ordinary web pages: 这是两个普通的网页:

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>

Nice and simple. 很好,很简单。 Voiceover reads it like this: 画外音如下所示:

Web page loaded. 网页已加载。 This is page 1. 这是第1页。
[swipe right] Click here for page 2. Link. [向右滑动]单击此处查看第2页。链接。
[double tap] Web page loaded. [双击]加载网页。 This is page 2. 这是第2页。
[swipe right] Click here for page 1. Visited. [向右滑动]单击此处查看第1页。已访问。 Link. 链接。
[double tap] Web page loaded. [双击]加载网页。 This is page 1. 这是第1页。

Here it is again as a single-page application, using history manipulation to simulate actual page loads. 这里它再次作为单页面应用程序,使用历史记录操作来模拟实际的页面加载。

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

(Note that this sample doesn't work from the filesystem; you have to load it from a webserver.) (请注意,此示例不适用于文件系统;您必须从Web服务器加载它。)

This SPA example visually looks exactly like the simple multi-page example, except that page 2 "loads quicker" (because it's not really loading at all; it's all happening in JS). 这个SPA示例在视觉上看起来与简单的多页面示例完全相同,只是第2页“加载速度更快”(因为它根本没有真正加载;它在JS中都发生了)。

But in Voiceover, it doesn't do the right thing. 但在画外音方面,它并没有做正确的事情。

Web page loaded. 网页已加载。 This is page 1. 这是第1页。
[swipe right] Click here for page 2. Link. [向右滑动]单击此处查看第2页。链接。
[double tap] Click here for page 1. Visited. [双击]点击此处查看第1页。访问。 Link. 链接。
[The focus is on the link! [重点是链接! swipe left] This is page 2. 向左滑动]这是第2页。
[swipe right] Click here for page 1. Visited. [向右滑动]单击此处查看第1页。已访问。 Link. 链接。
[double tap] Web page loaded. [双击]加载网页。 This is page 1. 这是第1页。

The focus is on the link, when it should be at the top of the page. 重点是链接,它应位于页面顶部。

How do I tell Voiceover that the whole page has just updated and so the reader should resume reading from the top of the page? 如何告诉Voiceover整个页面刚刚更新,读者应该从页面顶部继续阅读?

Jorgen's answer , which is based on another StackOverflow thread got me on the right track. Jorgen的回答是基于另一个StackOverflow线程让我走上正轨。

The actual fix was not to wrap the entire page in a <div tabindex=-1> but instead to create a <span tabindex=-1> around just the first part ("This is page N") and focus that. 实际修复不是将整个页面包装在<div tabindex=-1> ,而是围绕第一部分(“这是页面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();
}

Note in this example we also blur the focus in a timeout; 请注意,在此示例中,我们还会在超时时模糊焦点; otherwise, non-screen-reader browsers will draw a visible blue focus rectangle around the span, which is not what we want. 否则,非屏幕阅读器浏览器会在跨度周围绘制一个可见的蓝色焦点矩形,这不是我们想要的。 Blurring the focus doesn't affect the focus of the iOS VO reader. 模糊焦点不会影响iOS VO阅读器的焦点。

Here's a further improvement, in response to Dan Fabulich's answer . 根据Dan Fabulich的回答 ,这是一个进一步的改进。

The question was about updating a page with pushState() , so I'll start with a semantic HTML structure containing a main content area to be updated. 问题是关于使用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: 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.
}

About the focus outline (also known as focus ring): 关于焦点轮廓(也称为焦点环):

  • Don't blur() . 不要blur() Screen readers can often recover, but they can't re-announce the current line, and it's a WCAG no-no. 屏幕阅读器通常可以恢复,但他们无法重新宣布当前的线路,而且它是WCAG禁忌。
  • Less bad than blurring is CSS span {outline:none} , but this is risky. 比模糊更糟糕的是CSS span {outline:none} ,但这是有风险的。 I would only use it on static text with tabindex=-1 and it's not ideal because it's disorienting for sighted keyboard users. 我只会在tabindex=-1静态文本上使用它,这并不理想,因为它让视力不佳的键盘用户迷失方向。
  • The best solution I've seen is Alice Boxhall's input modality prollyfill . 我见过的最好的解决方案是Alice Boxhall的输入模式prollyfill

I don't own a iPhone but a solution is provided in another stackoverflow thread . 我没有iPhone,但在另一个stackoverflow线程中提供了解决方案。 Wrap the content in a element with a tabindex of -1 and programmatically set the focus on that element. 将内容包装在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();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 单页JavaScript Web应用程序的体系结构? - Architecture of a single-page JavaScript web application? 链接单页Web应用程序的各个部分 - Linking parts of a single-page web application 单页应用程序和网页标题 - Single-page application and web-page title 在PushState单页应用程序中将URL片段(#)用于模式视图 - Using URL fragment (#) for modal views in a pushState single-page app 在单页网页设计上实现链接 - Implement links on single-page web design 具有实际URL的单页应用程序 - Single-Page Application with Real URLs 单页Web应用程序是否应该与服务器保持一个Web Socket连接? - Should a single-page web application keep one Web Socket connection to the server or several? 如何在单页应用程序上使用AMD以仅加载某些依赖项 - how to use AMD on a Single-page application to load only certain dependency 使用Amazon Cognito的授权代码授予无服务器/单页Web应用程序 - Using Amazon Cognito's Authorization Code Grant with Serverless/Single-Page Web Application 如果未从根目录访问单页​​Web应用程序(SPA),则保证HTML信息 - Guaranteed HTML information if single-page web application (SPA) is not accessed at the root
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM