跳到主要内容

focus-trap

tokens.ts

  • FOCUS_AFTER_TRAPPED
  • FOCUS_AFTER_RELEASED
  • FOCUSOUT_PREVENTED
  • FOCUS_AFTER_TRAPPED_OPTS
  • FOCUSOUT_PREVENTED_OPTS
  • ON_TRAP_FOCUS_EVT
  • ON_RELEASE_FOCUS_EVT

utils.ts

  • obtainAllFocusableElements: 通过document.createTreeWalkernode.tabIndex >= 0获取列表
  • getVisibleElement: 找到列表第一个visible element
  • getEdges: obtainAllFocusableElements -> getVisibleElement
  • tryFocus: 如果能focus则调用element.focus({ preventScroll: true }), element.select()
  • focusFirstDescendant:挨个尝试,直到document.activeElement改变
  • focusableStack
  • isFocusCausedByUserEvent
  • useFocusReason: focusReason = ref<'pointer' | 'keyboard'>()
  • createFocusOutPreventedEvent

useFocusReason

      document.addEventListener('mousedown', notifyFocusReasonPointer)
document.addEventListener('touchstart', notifyFocusReasonPointer)
document.addEventListener('keydown', notifyFocusReasonKeydown)

focus-trap.vue

props

  props: {
loop: Boolean,
trapped: Boolean,
focusTrapEl: Object as PropType<HTMLElement>,
focusStartEl: {
type: [Object, String] as PropType<'container' | 'first' | HTMLElement>,
default: 'first',
},
},

emits

  emits: [
ON_TRAP_FOCUS_EVT,
ON_RELEASE_FOCUS_EVT,
'focusin',
'focusout',
'focusout-prevented',
'release-requested',
],

component setup

  • escape key: !focusLayer.paused也就是最上层focusLayer,emit('release-requested', event)
  • onKeydown: 当按键tab且正常应该focus离开当前container时,,限制focus只在内部变化,并发送emit('focusout-prevented', focusoutPreventedEvent)
  • onFocusOut: 如果props.trapped为真则, 发送emit('focusout-prevented', focusoutPreventedEvent), tryFocus(lastFocusAfterTrapped, true)
  • startTrap: 如果当前focus在外部,则尝试focus props.focusStartEl,如果失败则尝试tryFocus(trapContainer)
    useEscapeKeydown((event) => {
if (props.trapped && !focusLayer.paused) {
emit('release-requested', event)
}
})

const onKeydown = (e: KeyboardEvent) => {....

provide(FOCUS_TRAP_INJECTION_KEY, {
focusTrapRef: forwardRef,
onKeydown,
})

watch([forwardRef], ([forwardRef], [oldForwardRef]) => {
if (forwardRef) {
forwardRef.addEventListener('keydown', onKeydown)
forwardRef.addEventListener('focusin', onFocusIn)
forwardRef.addEventListener('focusout', onFocusOut)
}