最近,我需要为标题元素中的各个字符设置动画。我希望有一个类似于CSS的方便的CSS专用解决方案:nth-child(i)
,但不幸的是不存在。因此,我决定研究如何实现类似且可访问的内容。
的HTML
我的第一个想法是<span>
手动将每个字符包装在单独的元素中。
<h1>
<span>T</span>
<span>e</span>
<span>x</span>
<span>t</span>
</h1>
但是,此方法存在两个问题:
- 辅助功能:通过这样分割文本,屏幕阅读器将单独读取每个字符,这对依赖屏幕阅读器的人们来说是一种痛苦的体验。
- 可伸缩性:这样写出整个单词或句子是一个烦人的过程,每次都必须手动重复,并且不适用于动态加载的文本。
具有HTML和JavaScript的可访问且可扩展的解决方案
我在css-irl上找到了解决这些问题的解决方案,使用aria元素来实现可访问性,并使用javascript来自动执行文本拆分。它以您想要拆分的文本作为输入,并以如下方式返回它:
<h1 aria-label="Text">
<span aria-hidden="true">T</span>
<span aria-hidden="true">e</span>
<span aria-hidden="true">x</span>
<span aria-hidden="true">t</span>
</h1>
屏幕阅读器将阅读内部定义的文本,aria-label
但忽略标记为的元素aria-hidden="true"
。但是,当我在Mac上的VoiceOver上尝试此操作时,我发现还必须向role
父级添加一个元素才能使其正常工作。
<h1 aria-label="Text" role="heading"> ... </h1>
反应组件
由于我在React中做了很多工作,因此我决定在可重用组件中创建一个类似的解决方案。
从前面的示例中我们知道,我们至少有两个变量信息:必须显示的文本(this.props.copy
)和元素的角色(this.props.role
)。
基于此,我们可以从创建SplitText
可重用组件开始:
<SplitText copy="This is the text that will be split" role="heading" />
在SplitText
组件的render函数中,我们首先要使用aria-label={this.props.copy}
和渲染一个父元素role={this.props.role}
。这将使屏幕阅读器阅读原始文本。
然后,我们需要遍历副本,并使用返回返回包裹在span元素中的每个元素aria-hidden="true"
。这将在视觉上呈现字符串的每个字符,但屏幕阅读器将跳过它。我们可以使用.split("")
函数将文本变成数组来遍历文本。
render(){
return(
<span aria-label={this.props.copy} role={this.props.role}>
{this.props.copy.split("").map(function(char, index){
return <span aria-hidden="true" key={index}>{char}</span>;
})}
</span>
);
}
在此扩展
现在我们有了基础,我们还可以扩展此逻辑,并在其中添加更多功能SplitText
,例如自定义类名称或条件样式。我将编写第二个教程,在其中我们将更深入地介绍一些示例。