如何使用CSS或JavaScript为伪元素添加交互式语义



我有一个小转盘,它在页面加载时自动播放,使用HTML、CSS和JavaScript,绝对没有jQuery。

要添加暂停/播放选项,有一个带有role="checkbox"的span,后跟一个标签。

标签本身是隐藏的,没有任何内容。跨度有两个伪元素。在第一次显示时,伪元素显示⏸字符,由CSS::after类控制。当单击时,跨度具有类";被点击";添加,此时▶显示字符,由另一个::after级控制

它是可聚焦的,可以通过按下Enter键用键盘激活,但当我与Lighthouse核对时,我一直得到";可聚焦元素应该具有交互式语义";。

为什么会这样?

这是代码:

/* detect keyboard users */
function handleFirstTab(e) {
if (e.key === 'Tab') { // the 'I am a keyboard user' key
document.body.classList.add('user-is-tabbing');
window.removeEventListener('keydown', handleFirstTab);
}
}
let checkboxEl = document.getElementById('checkbox');
let labelEl = document.getElementById('checkboxLabel');
labelEl.onclick = function handleLabelClick() {
checkboxEl.focus();
toggleCheckbox();
}
function toggleCheckbox() {
let isChecked = checkboxEl.classList.contains('is-checked');
checkboxEl.classList.toggle('is-checked', !isChecked);
checkboxEl.setAttribute('aria-checked', !isChecked);
}
checkboxEl.onclick = function handleClick() { 
toggleCheckbox();
}
checkboxEl.onkeypress = function handleKeyPress(event) { 
let isEnterOrSpace = event.keyCode === 32 || event.keyCode === 13;
if(isEnterOrSpace) {
toggleCheckbox();
}
}
.link {
height: auto;
border: 1px solid #000;
margin-bottom: 1rem;
width: 80%;
display: block;
}
#carousel-checkbox {
margin-bottom: 1rem;
height: 50px;
width: 100px;
display: inline-block;
}
#carousel-checkbox input {
display: none;
}
#carousel-checkbox label {
display: inline-block;
vertical-align: middle;
}
#carousel-checkbox #checkbox {
position: relative;
top: 0;
left: 30px;
padding: 0.5rem 1rem;
background: rgba(255,255,255, 0.5);
}
#carousel-checkbox #checkbox:hover {
cursor: pointer;
}
#carousel-checkbox #checkbox:focus {
border: 1px dotted var(--medium-grey);
}
#carousel-checkbox #checkbox::after {
content: "⏸";
font-size: 1.5rem;
color: var(--theme-dark);
}
#carousel-checkbox #checkbox.is-checked::after {
content: "▶";
}
<div class="link">A bit of text with <a href="/">a dummy link</a> to demonstrate the keyboard tabbing navigation. </div>
<div id="carousel-checkbox"><span id="checkbox" tabindex="0" role="checkbox" aria-checked="false" aria-labelledby="checkboxLabel"></span><label id="checkboxLabel"></label></div>
<div class="link">Another link to <a href="/">another dummy link</a></div>

为什么会这样?是因为伪元素没有name属性还是类似的东西?

我尝试了一种不同的方法,删除伪元素,并尝试根据类"被单击"是否存在来更改跨度innerHTML,但尽管我可以在最初显示暂停字符,但当再次单击跨度时,它不会将innerHTML更改为播放字符。

简短回答

这是一个警告,而不是一个错误,它告诉你检查项目是否真的是交互式的。

现在您已经获得了元素的交互性,因此可以忽略该问题。

答案很长

为什么不使用<input type="checkbox">来节省大量额外的工作呢?

您可以使用可视隐藏类隐藏复选框。

这样就可以使用伪元素作为状态的视觉表示来执行相同的操作。

我对您的示例做了一些更改,这意味着您不必担心捕获按键等,只需使用click处理程序,这样您的JS就简单多了。

注意标签的技巧,我在其中添加了一些视觉上隐藏的文本,这样标签仍然可见(所以我们仍然可以使用psuedo元素!(。

然后我使用#checkbox1 ~ label来访问带有CSS的标签,这样我们就可以更改状态。

最后要注意的是我如何稍微更改了content属性。这是因为一些屏幕阅读器会尝试读出伪元素,所以我添加了空白的alt文本。支持率不高达70%,但对于支持它的浏览器来说,这是值得添加的

示例

下面希望展示一种通过复选框实现您想要的东西的方法。

可能有一些错误,因为我刚刚修改了你的代码,所以请不要只是复制和粘贴!

注意:复选框不应与输入一起使用,而应仅与

空格

let checkboxEl = document.getElementById('checkbox1');
let labelEl = document.querySelector('#checkboxLabel');
function toggleCheckbox() {
let isChecked = checkboxEl.classList.contains('is-checked');
checkboxEl.classList.toggle('is-checked', !isChecked);
checkboxEl.setAttribute('aria-checked', !isChecked);
}
checkboxEl.onclick = function handleClick() { 
toggleCheckbox();
}
.link {
height: auto;
border: 1px solid #000;
margin-bottom: 1rem;
width: 80%;
display: block;
}
#carousel-checkbox {
margin-bottom: 1rem;
height: 50px;
width: 100px;
display: inline-block;
}
.visually-hidden { 
border: 0;
padding: 0;
margin: 0;
position: absolute !important;
height: 1px; 
width: 1px;
overflow: hidden;
clip: rect(1px 1px 1px 1px); /* IE6, IE7 - a 0 height clip, off to the bottom right of the visible 1px box */
clip: rect(1px, 1px, 1px, 1px); /*maybe deprecated but we need to support legacy browsers */
clip-path: inset(50%); /*modern browsers, clip-path works inwards from each corner*/
white-space: nowrap; /* added line to stop words getting smushed together (as they go onto seperate lines and some screen readers do not understand line feeds as a space */
}
#carousel-checkbox label {
display: inline-block;
vertical-align: middle;
}
#carousel-checkbox #checkbox1 {
position: relative;
top: 0;
left: 30px;
padding: 0.5rem 1rem;
background: rgba(255,255,255, 0.5);
}
#carousel-checkbox #checkbox1 ~label:hover {
cursor: pointer;
}
#carousel-checkbox #checkbox1:focus ~ label {
border: 1px dotted #333;
}
#carousel-checkbox #checkbox1 ~label::after {
content: "⏸" / "";
font-size: 1.5rem;
color: #000;
}
#carousel-checkbox #checkbox1.is-checked ~label::after {
content: "▶" / "";
}
<div class="link">A bit of text with <a href="/">a dummy link</a> to demonstrate the keyboard tabbing navigation. </div>
<div id="carousel-checkbox">
<input type="checkbox" id="checkbox1" class="visually-hidden">
<label for="checkbox1" id="checkboxLabel">
<span class="visually-hidden">Pause animations</span>
</label>
</div>
<div class="link">Another link to <a href="/">another dummy link</a></div>

最后,由于iPad/iOS无法响应复选框事件,我放弃了使用复选框。虽然它在iOS上的codepen中工作,但在实际网站上不起作用。所以我换了一个按钮。

这是代码,它是完全可访问的,没有"交互式语义"警告,显示了一些伪幻灯片。动画是基于只有三张幻灯片。如果你想要更多或更少,那么时间就必须相应地调整。我现在只需要设置暂停按钮的样式。

let element = document.getElementById("pause");
function toggleButton() {
element.classList.toggle("paused");
if (element.innerHTML === "⏸") {
element.innerHTML = "▶";
} 
else {
element.innerHTML = "⏸";
}  
}
element.onclick = function handleClick() {
toggleButton();
}
#carousel {
height: auto;
max-width: 1040px;
position: relative;
margin: 4rem auto 0;
}
#carousel > * {
animation: 12s autoplay6 infinite linear;
position: absolute; 
top: 0;
left: 0;
opacity: 0.0;
}
#carousel .one {
position: relative;
}
.homeSlides {
height: 150px;
width: 400px;
background-color: #ff0000;
}
.homeSlides.two {
background-color: #0fff00;
}
.homeSlides.three {
background-color: #e7e7e7;
}
@keyframes autoplay6 {
0% {opacity: 0.0}
4% {opacity: 1.0}
33.33% {opacity: 1.0}
37.33% {opacity: 0.0}
100% {opacity: 0.0}
}
#carousel > *:nth-child(1) {
animation-delay: 0s;
}
#carousel > *:nth-child(2) {
animation-delay: 4s;
}
#carousel > *:nth-child(3) {
animation-delay: 8s;
}
#carousel-button {
position: relative;
height: 100%;
width: auto;
}
#carousel-button button {
position: absolute;
top: -3.5rem;
right: 5rem;
padding: 0 0.5rem 0.25rem;;
background: #fff;
z-index: 98;
font-size: 2rem;
cursor: pointer;
}
body.user-is-tabbing #carousel-button button:focus {
outline: 1px dotted #333;
}
body:not(.user-is-tabbing) #carousel-button button:focus {
outline: none;
}
#carousel-button button:hover {
cursor: pointer;
}
#carousel-button ~ #carousel * {
animation-play-state: running;
}
#carousel-button button.paused ~ #carousel * {
animation-play-state: paused;
}
<div id="carousel-button"><button id="pause" class="">⏸</button>
<div id="carousel">
<div class="homeSlides one">This is div one</div>
<div class="homeSlides two">This is div two</div>
<div class="homeSlides three">This is div three</div>
</div>
</div>

最新更新