IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 【无障碍】自动朗读的弹窗和浮层实现 -> 正文阅读

[移动开发]【无障碍】自动朗读的弹窗和浮层实现

  • 单纯的aria-modal在ios上能不能行,看看aria-modal到底实现了什么(比如tabindex是不是能够不加)——如果不行的话底下的一些android的方法可能得挪出来
  • focus的事件捕获能不能行
  • 为啥外面加了tabindex之后,里面的不加就是一个整体了
  • aria-describedby 和 aria-labelledby为啥会让元素变成一个整体
  • Android的a链接按理说也不需要tabindex——focus需要,tanbindex是-1了才能focus
  • pushState 与 location.hash = 的有个区别会导致问题:pushState的第三个参数url 并不会马上就加载,而是可能在后面,比如页面重载的时候执行,这是导致弹窗没有自动朗读的大问题所在吧

使用Aria-modal属性

在弹窗元素上增加属性:

<section id="alert-dialog" role="dialog"  aria-modal="true" aria-label="xx对话框" tabindex="-1">
    <h1>标题内容</h1>
    <button>关闭</button>
</section>
  • role="dialog"——让浏览器告诉屏幕阅读器一个对话框打开了
  • aria-modal="true"——对话框以外的元素无法被聚焦(android上不生效
  • tabindex="-1——让对话框可以聚焦但无法被tab访问(android上必须加)。HTML dialog 元素,不能使用tabindex属性
  • aria-label="" 或 aria-labelledby="xx元素id"——指定元素朗读的内容
  • 这里按理说无需加tabindex属性,但实际实验中发现无tabindex不能朗读,可以加上试试
  • 弹窗div加上tabindex属性后,内部需要聚焦的第一个元素也要加上tabindex属性

Android不生效

解决焦点问题

document.activeElement可以获取到当前聚焦的元素

聚焦

使用a链接或直接改hash

原理是用锚点来指定位置。会导致在浏览器会话历史中新增一条记录,需要在关闭弹窗的时候history.back()或者history.go(-1)

	<a  @click.stop.prevent="showSelfAlert3"
		role="button"
		target="_self"
		href="#alert-dialog"
	>
		打开弹窗按钮
   </a>
   <div role="dialog" airia-live="polite" aria-label="xx对话框" id="alert-dialog" >弹窗内容</div>

或者使用js,直接修改hash值

showSelfAlert3(){
   this.showMask3 = true;
   this.$nextTick(() => {
		window.location.hash = 'alert-dialog';
		// 或者
		window.history.pushState( window.history.state || {}, document.title, location.href + '#alert-dialog');
	});
}

原理:#代表网页中的一个位置。其右面的字符,就是该位置的标识符
为网页位置指定标识符,有两个方法。一是使用锚点,比如<a name="print"></a>,二是使用id属性,比如<div id="print" >
单单改变#后的部分,浏览器只会滚动到相应位置,不会重新加载网页
pushState 与 location.hash = 的有个区别会导致问题:pushState的第三个参数url 并不会马上就加载,而是可能在后面,比如页面重载的时候执行,这是导致弹窗没有自动朗读的大问题所在吧
pushState()方法绝不会导致hashchange 事件被激活

使用focus实现

需要结合aria-live属性

showSelfAlert(){ 
   this.showMask3 = true;
   this.$nextTick(() => {
        // 或者在弹窗组件mounted的时候
		document.getElementById('aria-mask-dialog').focus(); 
	});
}
<div role="dialog" airia-live="polite" aria-label="xx对话框"  tabindex="-1">弹窗内容</div>

aria-live属性,默认值为off。变化了之后不会自己读出来。polite是系统会在用户空闲的时候朗读,assertive是立马打断
实验下来是使用polite,屏幕阅读器也会当即就读出来

限制焦点

让弹窗底下的元素无法访问,使用aria-hidden属性

// 在打开和关闭弹窗的位置
// 缺点是页面结构混乱的话会比较难处理
function switchCompAriaHidden(name, val) {
  	  let comp = document.querySelector(name);
	  comp?.setAttribute?.('aria-hidden', val);
}

// 捕获,监听focus事件
// 场景是公共的弹窗组件,希望把焦点管理包含在组件自身
document.addEventListener("focus", function(event) {
    var dialog = document.getElementById("my-dialog");
    if (dialogOpen && !dialog.contains(event.target)) {
        event.stopPropagation(); // 阻止捕获、目标、冒泡
        dialog.focus();
    }
}, true);

使用事件捕获(event capturing)侦听focus事件
事件处理程序的阶段:捕获 - 目标 - 冒泡

focus:当focusable元素获得焦点时,不支持冒泡;
focusin:和focus一样,只是此事件支持冒泡;
blur:当focusable元素失去焦点时,不支持冒泡;
focusout:和blur一样,只是此事件支持冒泡;

恢复焦点

关闭弹窗的时候,应该默认将焦点回退到弹窗显示之前相关的位置
实现:打开弹窗前记录最后一个焦点元素

let lastFocus = document.activeElement;
// 关闭弹窗时
lastFocus.focus()

特殊情况根据场景实现:
非常不希望用户再次唤起这个弹窗;
这个弹窗操作完成之后,需要用户进入到下一个流程

多个浮层/弹窗的管理

历史的h5已经实现了一部分的pushStatereturn popstateWatcher
在弹窗或浮层出现的时候,在浏览器记录里面push一下当前页面url。并对popstate事件addEventListener,回退时关闭弹窗/浮层
目的是为了解决用户手势操作(左滑)回退浏览器,而这种默认操作可能与用户预期不一致。比如打开弹窗的时候,用户左滑关闭弹窗,结果关闭了整个页面回退到上一个页面

问题点

因为是事件监听,多个弹窗时多个监听,回退一下会关闭多个

优化

使用数组的unshiftpop实现堆栈管理,保证每次监听的事件时最新的

let historyWatcherArr = [];

function stateWatcherStep() {
	if (historyWatcherArr && historyWatcherArr.length) {
		let listennerNow = historyWatcherArr.pop(historyWatcherArr[0]);
		window.removeEventListener('popstate', listennerNow);
	}
	historyWatcherArr.length && window.addEventListener('popstate', historyWatcherArr[0], false);
}

export default function pushHistory(callback, hashVal) {

	let historyState = window.history.state || {};

	historyState.url = location.href;
	if (hashVal) {
		historyState.hash = hashVal;
	}
	window.history.pushState(historyState, document.title, location.href + hashVal ? hashVal : '');

	let popstateWatcher = function (e) {
		popstateWatcher.clean();
		if (typeof callback === 'function') {
			callback();
			callback = null;
		}
	};

	popstateWatcher.remove = function () {
		popstateWatcher.clean();
		history.back();
	};

	popstateWatcher.clean = function () {
		window.removeEventListener('popstate', popstateWatcher);
		stateWatcherStep();
	};

	if (historyWatcherArr.length) {
		window.removeEventListener('popstate', historyWatcherArr[0]);
	}
	window.addEventListener('popstate', popstateWatcher, false);
	historyWatcherArr.unshift(popstateWatcher);
	return popstateWatcher;
}

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-11-20 18:30:36  更:2021-11-20 18:31:16 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 4:11:15-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码