添加内容时如何按 Tab 键顺序和屏幕 reader 光标跳过按钮?
How do I skip a button in tab order and in screen reader cursor when adding content?
我正在 HTML 创作互动小说。向用户呈现一个段落列表,然后是一系列选择,当他们 select 做出选择时,选择列表将被删除并替换为新内容。但是,selected 选择被保留下来作为提醒,并为用户提供一种方式 return 并做出不同的选择。
这是用户select选择之前的标记:
<div aria-live="polite">
<p>You enter the castle.</p>
<p><button>Go to throne room</button></p>
<p><button>Go back out</button><p>
</div>
这是由 jQuery 编辑的标记,在用户选择“前往王座室”后:
<div aria-live="polite">
<p>You enter the castle.</p>
<p class="reminder">Go to throne room (<a>click this to make a different choice</a>)</p>
<p>There is a king in the room!</p>
<p><button>Assassinate him</button></p>
<p><button>Talk to him</button></p>
</div>
现在,当我按 Tab 键移动到下一个元素时,link“单击此按钮做出不同的选择”得到 selected,这几乎永远不会有用。在最常见的情况下,用户希望在下一个选择的选项之间 select,所以我希望选择后的第一个选项卡 select“暗杀他”。
同样,选择后,NVDA首先开始阅读提示文字,我也想跳过,让它开始阅读“房间里有国王!”相反,因为用户已经知道他们做出了什么选择。
我不想从 Tab 键顺序和屏幕 reader 中完全删除文本和 link,创建内容时只需跳过一次即可。
这可以做到吗?
我会把注意力放在“暗杀他”按钮上。因此,NVDA 会说出所有标有 aria-live="polite"
的 div
,但您的用户会直接点击必要的按钮。
您使用 jQuery 中的 focus() 方法执行此操作,因此:
$("#assassinate").focus();
您可能希望以不同方式构建页面。
aria-live
应用于文档流之外的公告(例如提醒、保存确认、更新等)
出于您的目的,您不需要 aria-live
,而是希望实施焦点管理。
因此您的页面流将如下所示:-
- 显示初始问题和选项。
- 我选择一个选项,然后您将新信息追加到页面并聚焦新文本的第一部分。
- 同时更新旧文本以包含所做的选择并替换那里的按钮。
第二点是关键点,您可能想要聚焦通常无法聚焦的内容(并且您不希望它出现在文档聚焦顺序中。)。为此,您需要组合 tabindex="-1"
和 .focus()
.
tabindex="-1"
基本上允许某些东西获得编程焦点(使用你的 JavaScript .focus()
函数)但它不会被添加到页面的焦点顺序(你不能使用 Tab 聚焦它)。
我在下面整理了一个非常丑陋的演示来演示行为(我没有包括更改选项等,但你应该明白了):
var mainSection = document.querySelector('main');
var previousActions = [];
var latestSection = "";
var getCurrentActions = function(){
latestSection = document.querySelector('section:last-of-type');
var actions = latestSection.querySelectorAll('.actions button');
for(x = 0; x < actions.length; x++){
actions[x].addEventListener('click', performAction);
}
return actions;
}
var performAction = function(){
//grab the selected action to later change the actions section.
var selectedAction = this.innerHTML;
var lastSection = latestSection;
previousActions = getCurrentActions();
var thisAction = this.getAttribute('data-action');
if(thisAction == ""){
alert("you didn't think I was going to do a whole story did you?");
return;
}
var template = document.querySelector('template[data-template=' + thisAction + ']');
var templateHTML = template.innerHTML;
for(x = 0; x < previousActions.length; x++){
previousActions[x].removeEventListener('click', performAction);
}
mainSection.insertAdjacentHTML('beforeend', templateHTML);
getCurrentActions();
document.querySelector('#' + thisAction).focus();
var actionReminder = lastSection.querySelector('.actions');
actionReminder.innerHTML = '<p class="reminder">You chose:(<button class="changeChoice">Make a different choice</button>)</p>';
//from here you need to add event listeners to change the choice, remove sections you added if they change their choice etc. The above is purely for demonstration purposes and I would recommend using arrays of information to construct things etc.
}
getCurrentActions();
template{
display: none;
}
<main>
<section aria-labelledby="enteringCastle">
<h2 id="enteringCastle" tabindex="-1">You enter the castle.</h2>
<p>The castle is grand and filled with ornate paintings etc. etc. (extended description for immersion if needed)</p>
<div class="actions">
<ul>
<li><button data-action="throne">Go to throne room</button></li>
<li><button data-action="leave">Go back out</button></li>
</ul>
</div>
</section>
</main>
<template data-template="throne">
<section aria-labelledby="throne">
<h2 id="throne" tabindex="-1">You enter the throne room.</h2>
<p>There is a king in the room!</p>
<div class="actions">
<ul>
<li><button data-action="assassinate">Assassinate Him</button></li>
<li><button data-action="talk">Talk To Him</button></li>
</ul>
</div>
</section>
</template>
<template data-template="leave">
<section aria-labelledby="leave">
<h2 id="leave" tabindex="-1">You left the castle</h2>
<p>You find yourself in a beautiful field, full of daisies</p>
<div class="actions">
<ul>
<li><button data-action="pick">Pick a Daisy to display on your armor</button></li>
<li><button data-action="burn">You remembered that you hate daisies, burn them all!</button></li>
</ul>
</div>
</section>
</template>
<template data-template="assassinate">
<section aria-labelledby="assassinate">
<h2 id="assassinate" tabindex="-1">You try to assassinate the king</h2>
<p>Despite his age the king is fast, he parries your attack and holds a knife to your throat.</p>
<div class="actions">
<ul>
<li><button data-action="">Say you are really sorry</button></li>
<li><button data-action="">Deliberately soil yourself, hoping the smell will distract the king</button></li>
</ul>
</div>
</section>
</template>
<template data-template="talk">
<section aria-labelledby="talk">
<h2 id="assassinate" tabindex="-1">You talk to the king</h2>
<p>The king tells you of a magical cup that he would reward you handsomely for if you could get it</p>
<div class="actions">
<ul>
<li><button data-action="">Go find the cup!</button></li>
<li><button data-action="">Ask for more information, only an idiot would go after treasure without all the information</button></li>
</ul>
</div>
</section>
</template>
<template data-template="pick">
<section aria-labelledby="pick">
<h2 id="pick" tabindex="-1">You pick a Daisy</h2>
<p>You hear a booming voice "who the f**k do you think you are, leave my garden immediately"</p>
<div class="actions">
<ul>
<li><button data-action="">Draw your sword ready</button></li>
<li><button data-action="">Apologise immediately and then spin round</button></li>
</ul>
</div>
</section>
</template>
<template data-template="burn">
<section aria-labelledby="burn">
<h2 id="burn" tabindex="-1">You Start a fire</h2>
<p>You realise that this was probably a bad idea, a giant ogre is running straight for you screaming! You see a bucket near a stream</p>
<div class="actions">
<ul>
<li><button data-action="">Grab the bucket and fill it with sand, ready to attack the ogre</button></li>
<li><button data-action="">Grab the bucket and fill it with water, ready to help put the fire out!</button></li>
</ul>
</div>
</section>
</template>
我正在 HTML 创作互动小说。向用户呈现一个段落列表,然后是一系列选择,当他们 select 做出选择时,选择列表将被删除并替换为新内容。但是,selected 选择被保留下来作为提醒,并为用户提供一种方式 return 并做出不同的选择。
这是用户select选择之前的标记:
<div aria-live="polite">
<p>You enter the castle.</p>
<p><button>Go to throne room</button></p>
<p><button>Go back out</button><p>
</div>
这是由 jQuery 编辑的标记,在用户选择“前往王座室”后:
<div aria-live="polite">
<p>You enter the castle.</p>
<p class="reminder">Go to throne room (<a>click this to make a different choice</a>)</p>
<p>There is a king in the room!</p>
<p><button>Assassinate him</button></p>
<p><button>Talk to him</button></p>
</div>
现在,当我按 Tab 键移动到下一个元素时,link“单击此按钮做出不同的选择”得到 selected,这几乎永远不会有用。在最常见的情况下,用户希望在下一个选择的选项之间 select,所以我希望选择后的第一个选项卡 select“暗杀他”。
同样,选择后,NVDA首先开始阅读提示文字,我也想跳过,让它开始阅读“房间里有国王!”相反,因为用户已经知道他们做出了什么选择。
我不想从 Tab 键顺序和屏幕 reader 中完全删除文本和 link,创建内容时只需跳过一次即可。
这可以做到吗?
我会把注意力放在“暗杀他”按钮上。因此,NVDA 会说出所有标有 aria-live="polite"
的 div
,但您的用户会直接点击必要的按钮。
您使用 jQuery 中的 focus() 方法执行此操作,因此:
$("#assassinate").focus();
您可能希望以不同方式构建页面。
aria-live
应用于文档流之外的公告(例如提醒、保存确认、更新等)
出于您的目的,您不需要 aria-live
,而是希望实施焦点管理。
因此您的页面流将如下所示:-
- 显示初始问题和选项。
- 我选择一个选项,然后您将新信息追加到页面并聚焦新文本的第一部分。
- 同时更新旧文本以包含所做的选择并替换那里的按钮。
第二点是关键点,您可能想要聚焦通常无法聚焦的内容(并且您不希望它出现在文档聚焦顺序中。)。为此,您需要组合 tabindex="-1"
和 .focus()
.
tabindex="-1"
基本上允许某些东西获得编程焦点(使用你的 JavaScript .focus()
函数)但它不会被添加到页面的焦点顺序(你不能使用 Tab 聚焦它)。
我在下面整理了一个非常丑陋的演示来演示行为(我没有包括更改选项等,但你应该明白了):
var mainSection = document.querySelector('main');
var previousActions = [];
var latestSection = "";
var getCurrentActions = function(){
latestSection = document.querySelector('section:last-of-type');
var actions = latestSection.querySelectorAll('.actions button');
for(x = 0; x < actions.length; x++){
actions[x].addEventListener('click', performAction);
}
return actions;
}
var performAction = function(){
//grab the selected action to later change the actions section.
var selectedAction = this.innerHTML;
var lastSection = latestSection;
previousActions = getCurrentActions();
var thisAction = this.getAttribute('data-action');
if(thisAction == ""){
alert("you didn't think I was going to do a whole story did you?");
return;
}
var template = document.querySelector('template[data-template=' + thisAction + ']');
var templateHTML = template.innerHTML;
for(x = 0; x < previousActions.length; x++){
previousActions[x].removeEventListener('click', performAction);
}
mainSection.insertAdjacentHTML('beforeend', templateHTML);
getCurrentActions();
document.querySelector('#' + thisAction).focus();
var actionReminder = lastSection.querySelector('.actions');
actionReminder.innerHTML = '<p class="reminder">You chose:(<button class="changeChoice">Make a different choice</button>)</p>';
//from here you need to add event listeners to change the choice, remove sections you added if they change their choice etc. The above is purely for demonstration purposes and I would recommend using arrays of information to construct things etc.
}
getCurrentActions();
template{
display: none;
}
<main>
<section aria-labelledby="enteringCastle">
<h2 id="enteringCastle" tabindex="-1">You enter the castle.</h2>
<p>The castle is grand and filled with ornate paintings etc. etc. (extended description for immersion if needed)</p>
<div class="actions">
<ul>
<li><button data-action="throne">Go to throne room</button></li>
<li><button data-action="leave">Go back out</button></li>
</ul>
</div>
</section>
</main>
<template data-template="throne">
<section aria-labelledby="throne">
<h2 id="throne" tabindex="-1">You enter the throne room.</h2>
<p>There is a king in the room!</p>
<div class="actions">
<ul>
<li><button data-action="assassinate">Assassinate Him</button></li>
<li><button data-action="talk">Talk To Him</button></li>
</ul>
</div>
</section>
</template>
<template data-template="leave">
<section aria-labelledby="leave">
<h2 id="leave" tabindex="-1">You left the castle</h2>
<p>You find yourself in a beautiful field, full of daisies</p>
<div class="actions">
<ul>
<li><button data-action="pick">Pick a Daisy to display on your armor</button></li>
<li><button data-action="burn">You remembered that you hate daisies, burn them all!</button></li>
</ul>
</div>
</section>
</template>
<template data-template="assassinate">
<section aria-labelledby="assassinate">
<h2 id="assassinate" tabindex="-1">You try to assassinate the king</h2>
<p>Despite his age the king is fast, he parries your attack and holds a knife to your throat.</p>
<div class="actions">
<ul>
<li><button data-action="">Say you are really sorry</button></li>
<li><button data-action="">Deliberately soil yourself, hoping the smell will distract the king</button></li>
</ul>
</div>
</section>
</template>
<template data-template="talk">
<section aria-labelledby="talk">
<h2 id="assassinate" tabindex="-1">You talk to the king</h2>
<p>The king tells you of a magical cup that he would reward you handsomely for if you could get it</p>
<div class="actions">
<ul>
<li><button data-action="">Go find the cup!</button></li>
<li><button data-action="">Ask for more information, only an idiot would go after treasure without all the information</button></li>
</ul>
</div>
</section>
</template>
<template data-template="pick">
<section aria-labelledby="pick">
<h2 id="pick" tabindex="-1">You pick a Daisy</h2>
<p>You hear a booming voice "who the f**k do you think you are, leave my garden immediately"</p>
<div class="actions">
<ul>
<li><button data-action="">Draw your sword ready</button></li>
<li><button data-action="">Apologise immediately and then spin round</button></li>
</ul>
</div>
</section>
</template>
<template data-template="burn">
<section aria-labelledby="burn">
<h2 id="burn" tabindex="-1">You Start a fire</h2>
<p>You realise that this was probably a bad idea, a giant ogre is running straight for you screaming! You see a bucket near a stream</p>
<div class="actions">
<ul>
<li><button data-action="">Grab the bucket and fill it with sand, ready to attack the ogre</button></li>
<li><button data-action="">Grab the bucket and fill it with water, ready to help put the fire out!</button></li>
</ul>
</div>
</section>
</template>