- 单纯的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 并不会马上就加载,而是可能在后面,比如页面重载的时候执行,这是导致弹窗没有自动朗读的大问题所在吧
在弹窗元素上增加属性:
标题内容
- role="dialog"——让浏览器告诉屏幕阅读器一个对话框打开了
- aria-modal="true"——对话框以外的元素无法被聚焦(android上不生效)
- tabindex="-1——让对话框可以聚焦但无法被tab访问(android上必须加)。HTML dialog 元素,不能使用tabindex属性
- aria-label="" 或 aria-labelledby="xx元素id"——指定元素朗读的内容
Android不生效 解决焦点问题
- 这里按理说无需加tabindex属性,但实际实验中发现无tabindex不能朗读,可以加上试试
- 弹窗div加上tabindex属性后,内部需要聚焦的第一个元素也要加上tabindex属性
document.activeElement可以获取到当前聚焦的元素
聚焦 使用a链接或直接改hash原理是用锚点来指定位置。会导致在浏览器会话历史中新增一条记录,需要在关闭弹窗的时候history.back()或者history.go(-1)
打开弹窗按钮弹窗内容
或者使用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');
});
}
使用focus实现原理:#代表网页中的一个位置。其右面的字符,就是该位置的标识符
为网页位置指定标识符,有两个方法。一是使用锚点,比如,二是使用id属性,比如
单单改变#后的部分,浏览器只会滚动到相应位置,不会重新加载网页
pushState 与 location.hash = 的有个区别会导致问题:pushState的第三个参数url 并不会马上就加载,而是可能在后面,比如页面重载的时候执行,这是导致弹窗没有自动朗读的大问题所在吧
pushState()方法绝不会导致hashchange 事件被激活
需要结合aria-live属性
showSelfalert(){
this.showMask3 = true;
this.$nextTick(() => {
// 或者在弹窗组件mounted的时候
document.getElementById('aria-mask-dialog').focus();
});
}
弹窗内容
限制焦点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已经实现了一部分的pushState,return popstateWatcher
在弹窗或浮层出现的时候,在浏览器记录里面push一下当前页面url。并对popstate事件addEventListener,回退时关闭弹窗/浮层
目的是为了解决用户手势操作(左滑)回退浏览器,而这种默认操作可能与用户预期不一致。比如打开弹窗的时候,用户左滑关闭弹窗,结果关闭了整个页面回退到上一个页面
因为是事件监听,多个弹窗时多个监听,回退一下会关闭多个
优化使用数组的unshift和pop实现堆栈管理,保证每次监听的事件时最新的
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;
}



