Angular 学习笔记 (cdk focus monitor 和一些 focus tabindex 的基础)
更新: 2021-01-13
场上只能有一个 active 的 focus track, 这个概念之前应该是没有的.
今天遇到一个 bug, 在 menu 里面打开一个 message tip
由于 message tip 打开时并不会马上 focus 进去, 但是我里面又做了 focus trap 这就导致了 menu 的 focus trap 失效了.
正确的做法是,等要进入 message tip 的时候才开启 message tip 的 focus trap.
更新: 2020-06-08
今天遇到一个鸟事儿.
FocusTrapFactory 快废弃了,请用 ConfigurableFocusTrapFactory
你以为 ng 像传说那样向后兼容,你以为只是换一个 class 就好了.
不不不,你太天真了.
首先这个 ConfigurableFocusTrapFactory 多了一个体验.
从前的 focus trap 只是无法 tab 出去, 但是可以 mouse click 出去.
现在呢,不可以了, 当你 mouse click 外面,它会 focus back to focus trap 里面。
接口没有完全一致,只是改了体验而已. 牛吧.
然后呢, 目前你用 cdkFocusTrap 指令的话,却没有这个体验...
为什么呢,因为 cdkFocusTrap 还在用旧的 FocusTrapFactory. 哎哟,建议我们用新的,自己却还用着旧的...
看看代码
监听了全局的 focus 然后判断 -> 然后 focus 回 focusTrap 内
我换了一个多月都没有发现体验不一样了. 不可能嘛.
哦,原来它对 overlay 有特别处理啊~~这操作这牛啊!如果 focus 到一个 overlay 里面就不会被拉回去....
我今天遇到的问题就是,我用了一个 editor plugin, 它也是会打开 modal 但是这个 plugin 不是 ng 的, 所以也没有使用 overlay module.
这样 focus trap 就认为它不是 overlay 所以就 focus 回去了. 我的体验就 break 了. 吐血.
不是很清楚这个体验对不对, 虽然名字上看 focus trap 就是确保 focus 在里面, 但我觉得 tab 就够了丫.
目前的定案就是把这个策略给关掉. 注意哦,factory 是 root 所以策略要 override 的话,也要在 app module 去 provide
更新 : 2019-12-22
基础知识 :
focus, tabindex
div by default 是不可以被 focus 的.
button by default 就可以被 focus
要想 focus div, 我们可以写 tabindex = -1
这个表示可以 focus 但是不可以 tab
如果要可以 tab 那么是 tabindex = 0
tabindex 的号码可以用来表示 focusable 和 tabable
还有顺序功能. 游览器会从 1 开始 tab to next 然后越来越大, 数字重复就用 parent -> child -> sibling 的顺序 (所以对于一个区块只要分配一个值就可以了) . 最后才去 tabindex 0.
所以修改后的顺序就是
1,1,2,2,2,2,3,4,5,0,0,0.... 这种
click 会有 focus 的, 但是没有 tab effect 框
click focus 后然后监听 tab prevent default 跳去我们要的地方, 如果是 <a> 的话 那么是没有 tab effect 框的, button 就有
display none 和 visibility: hidden 是不可以 focus 的. 然后和 element 被 remove 一样 focus 也是会跳去 body
当从 a element focus to b element 的时候, a 的 blur 会触发, 而这个时候 document.activeElement 是 body 哦.
document.click() 这种模拟 click 的话, 是不会 focus 的哦, 可以再调用 .focus() 就可以了
button vs div, keyup enter
当 button focus 的时候如果 enter or space 那么会触发 click 事件 (只有 click 哦,mousedown,up, touch 这些都不会触发)
当 div focus + enter 时, 啥也不会触发. 需要自己写监听 enter event 模拟一下. (这里要留意哦, 如果这个 div 里有 input, 那么是会冒泡上来的)
onblur to body
当一个被 focus 的 element 被删除后, 游览器会把 focus 移去 body. 不是parent 也不是 next 哦。
cdk focus monitor
ng 是用监听 focus 和 blur capture 来实现的, 不是 bubble 哦
然后触发时是在 outside angular zone 处理的事件监听.
所以经常会一种 bug 就是当一个 detech change cycle 开始后, 我们修改 state, dom 渲染时移除 dom
刚巧 dom 是被 focus 着的, 游览器就移动去 body 同时 blur 事件触发.
monitor 马上响应 (如果没有 outside zone,事件会排队然后触发下一次的 cycle)
马上响应,如果我们在监听里去修改 state, 那么就会导致一个 detech change cycle 进入渲染阶段后又修改 state, 然后就会报错了.
解决方法就是监听处理前先 delay 一下. Promise.resolve().then...
Overlay 经常破坏 focus monitor
overlay 会把内容丢去 body 如果我们 focus 里面的内容,很自然就去到外面了,monitor 会发现 blur 了。
但我们通常不希望这样. 解决方法就是自己做记入判断
比如当打开 overlay 后做一个 overlayOpened = true, 判断的时候就 blur = origin === null && !overlayOpened
这样才行.