const FOCUSABLE_ELEMENT_SELECTORS = `a[href],button:not([disabled]),button:not([hidden]),:not([tabindex="-1"]),input:not([disabled]),input:not([type="hidden"]),select:not([disabled]),textarea:not([disabled])`
- export const isVisible = (element: HTMLElement) => {
- if (process.env.NODE_ENV === 'test') return true
- const computed = getComputedStyle(element)
- // element.offsetParent won't work on fix positioned
- // WARNING: potential issue here, going to need some expert advices on this issue
- return computed.position === 'fixed' ? false : element.offsetParent !== null
- }
-
- /*
- getComputedStyle 获取到dom结构的样式对象集合
- offsetParent
- 元素自身有fixed定位,父元素不存在定位,则offsetParent的结果未null
- 元素自身无fixed定位,且父元素不存在定位,offsetParent为 body元素
- 元素自身无fixed定位,且父元素存在定位,offsetParent为离自身最近且经过定位的父元素
- body 元素的offsetParent是null
- */
getComputedStyle 获取指定dom元素的样式属性结合
返回的是一个对象 通过属性名称即可访问
如 getComputedStyle(document.getElementById("dom"))['width] 即可访问到dom元素的宽度
offsetParent 距离当前元素最近的进行过定位的父元素
素自身有fixed定位,父元素不存在定位,则offsetParent的结果未null
元素自身无fixed定位,且父元素不存在定位,offsetParent为 body元素
元素自身无fixed定位,且父元素存在定位,offsetParent为离自身最近且经过定位的父元素
body 元素的offsetParent是null
- export const obtainAllFocusableElements = (
- element: HTMLElement
- ): HTMLElement[] => {
- return Array.from(
- element.querySelectorAll<HTMLElement>(FOCUSABLE_ELEMENT_SELECTORS)
- ).filter((item: HTMLElement) => isFocusable(item) && isVisible(item))
- }
- /*
- obtainAllFocusableElements
- 入参
- HTMLElement 单个dom元素
- 返回值
- HTMLElement [] dom元素集合的 数组
- Array.from 将伪数组转换为真实的数组
- element.querySelectorAll 查找当前元素下的所有 满足条件的子元素 FOCUSABLE_ELEMENT_SELECTORS(子元素类名集合)
- filter 过滤
- isFocusable 和 isVisible 调用处理都返回true的
- */
obtainAllFocusableElements
入参
HTMLElement 单个dom元素
返回值
HTMLElement [] dom元素集合的 数组
Array.from 将伪数组转换为真实的数组
element.querySelectorAll 查找当前元素下的所有 满足条件的子元素 FOCUSABLE_ELEMENT_SELECTORS(子元素类名集合)
filter 过滤
isFocusable 和 isVisible 调用处理都返回true的
- export const isFocusable = (element: HTMLElement): boolean => {
- if (
- element.tabIndex > 0 ||
- (element.tabIndex === 0 && element.getAttribute('tabIndex') !== null)
- ) {
- return true
- }
- // HTMLButtonElement has disabled
- if ((element as HTMLButtonElement).disabled) {
- // HTMLButtonElement 表示按钮元素类型
- return false
- }
-
- switch (element.nodeName) {
- case 'A': {
- // casting current element to Specific HTMLElement in order to be more type precise
- return (
- !!(element as HTMLAnchorElement).href &&
- (element as HTMLAnchorElement).rel !== 'ignore'
- )
- /*
- !! (element as HTMLAnchorElement).href
- !! 双重取反
- !!a 表示 a != ""&&a != undefined && a != null
-
-
- a.rel 用于指定当前文档与被链接文档的关系
- HTMLAnchorElement 表示锚点链接类型
- */
- }
- case 'INPUT': {
- return !(
- (element as HTMLInputElement).type === 'hidden' ||
- (element as HTMLInputElement).type === 'file'
- )
- /*
- HTMLInputElement 输入元素类型
- as typescript 中的类型断言
- 所谓类型断言指的是我知道 某个值的详细信息
-
- */
- }
- case 'BUTTON':
- case 'SELECT':
- case 'TEXTAREA': {
- return true
- }
- default: {
- return false
- }
- }
- }
tabIndex 是全局属性,表示元素是否可以通过键盘导航 tab 选中
如果 tabIndex 为 负值 小于 0 则不可以通过 tab选中
tabIndex 设置为0 时 通常是为不可聚焦元素添加可聚焦属性
tabIndex 为正数 大于 0 时 可以调整 被访问的优先级
as 是断言 此处的作用是 认定了 element的类型为 HTMLButtonElement元素
此处表达的是 如果 button元素有disabled属性为true 即 终止代码 不可获取焦点
!!a 等价于 a != "" && a != null && a != undefined
a.rel 用于指定当前文档于被链接文档的关系
- export const attemptFocus = (element: HTMLElement): boolean => {
- if (!isFocusable(element)) {
- // 判断是否可以获取焦点 入宫不可获取焦点就返回false
- return false
- }
- // Remove the old try catch block since there will be no error to be thrown
- element.focus?.()
- // ?. 表示 问号之前的成立才执行问号之后的代码
- return document.activeElement === element
- // document.activeElement 当前页面中获取焦点的元素
- }
element.focus?.() 表示 入宫 element的focus属性存在就调用
相当于三元表达式 element.focus?element.focus():null
document.activeElement 表示当前页面中获取焦点的元素
document.activeElement = element 表示把当前的 element 设置为获取 焦点的元素
- export const triggerEvent = function (
- elm: HTMLElement,//当前dom元素
- name: string,//事件的名称
- ...opts: Array<boolean>//更多参数 opts,为一个数组, true 表示阻止事件冒泡 true 表示阻止默认行为
- ): HTMLElement {
- let eventName: string
-
- if (name.includes('mouse') || name.includes('click')) {
- // includes 表示是否包含 返回true或者false mouse 或者 click 都是鼠标事件 MouseEvents
- eventName = 'MouseEvents'
- } else if (name.includes('key')) {
- // key表示键盘事件
- eventName = 'KeyboardEvent'
- } else {
- // 既不是鼠标事件也不是键盘事件
- eventName = 'HTMLEvents'
- }
- const evt = document.createEvent(eventName)
- // 创建事件
- evt.initEvent(name, ...opts)
- // 初始化事件
- // opts 是数组 boolean 值 第一个表示是否阻止冒泡 是否阻止默认行为
- // document.createEvent dispatchEvent
- elm.dispatchEvent(evt)//触发事件
-
- /*
- 在其他地方可用document.addEventListener("事件名称") 来监听这些自定义事件
- */
-
- return elm
- }
表示入参 opts 是一个类型为boolean的数组
并将其解构出来
document.createEvent 创建自定义事件
并传入事件类型 eventName
鼠标事件
键盘事件
html事件 非 鼠标和键盘事件
let evt = document.createEvent(eventName) 创建自定义事件 并传入事件类型
evt.initEvent(name,...opts) 初始化事件 并传入参数 自定义事件名称 和 是否阻止冒泡 是否阻止默认事件行为 阻止参数
ele.dispatchEvent(evt) 触发自定义事件
可通过 document.addEventListener(name,()=>{}) 监听自定义事件的触发
-
- export const getSibling = (
- el: HTMLElement,//当前元素
- distance: number,//距离当前元素的位置
- elClass: string//元素的类名
- ) => {
- const { parentNode } = el//拿到当前元素的父元素
- if (!parentNode) return null//如果当前元素没有父元素就是 最顶级的元素 没有兄弟元素 就返回null
- const siblings = parentNode.querySelectorAll(elClass)//获取到所有兄弟元素的集合
- const index = Array.prototype.indexOf.call(siblings, el)// 获取到当前元素在所有兄弟元素集合中的位置 索引
- return siblings[index + distance] || null// 返回当前元素 距离兄弟元素指定位置的元素
- }
- 获取到当前元素的父元素
- 如果父元素不存在,就返回null
- 根据类名查找到所有的元素
- 拿到当前元素的位置索引
- 通过 当前元素的索引和传入的distance相加即得到指定坐标的元素
查找当前元素 el 在 siblings 的位置
由于sibilings是伪数组 所以需要调用 Array.prototype.indexof.call
等价于 Array.from(siblings).indexOf(el) Array.from 可以将伪数组转化为数组
- const FOCUSABLE_ELEMENT_SELECTORS = `a[href],button:not([disabled]),button:not([hidden]),:not([tabindex="-1"]),input:not([disabled]),input:not([type="hidden"]),select:not([disabled]),textarea:not([disabled])`
-
- /**
- * Determine if the testing element is visible on screen no matter if its on the viewport or not
- */
- export const isVisible = (element: HTMLElement) => {
- if (process.env.NODE_ENV === 'test') return true
- const computed = getComputedStyle(element)
- // element.offsetParent won't work on fix positioned
- // WARNING: potential issue here, going to need some expert advices on this issue
- return computed.position === 'fixed' ? false : element.offsetParent !== null
- }
-
- /*
- getComputedStyle 获取到dom结构的样式对象集合
- offsetParent
- 元素自身有fixed定位,父元素不存在定位,则offsetParent的结果未null
- 元素自身无fixed定位,且父元素不存在定位,offsetParent为 body元素
- 元素自身无fixed定位,且父元素存在定位,offsetParent为离自身最近且经过定位的父元素
- body 元素的offsetParent是null
- */
-
-
-
-
-
-
-
-
- export const obtainAllFocusableElements = (
- element: HTMLElement
- ): HTMLElement[] => {
- return Array.from(
- element.querySelectorAll<HTMLElement>(FOCUSABLE_ELEMENT_SELECTORS)
- ).filter((item: HTMLElement) => isFocusable(item) && isVisible(item))
- }
- /*
- obtainAllFocusableElements
- 入参
- HTMLElement 单个dom元素
- 返回值
- HTMLElement [] dom元素集合的 数组
- Array.from 将伪数组转换为真实的数组
- element.querySelectorAll 查找当前元素下的所有 满足条件的子元素 FOCUSABLE_ELEMENT_SELECTORS(子元素类名集合)
- filter 过滤
- isFocusable 和 isVisible 调用处理都返回true的
- */
-
- /**
- * @desc Determine if target element is focusable
- * @param element {HTMLElement}
- * @returns {Boolean} true if it is focusable
- */
- export const isFocusable = (element: HTMLElement): boolean => {
- if (
- element.tabIndex > 0 ||
- (element.tabIndex === 0 && element.getAttribute('tabIndex') !== null)
- ) {
- return true
- }
- // HTMLButtonElement has disabled
- if ((element as HTMLButtonElement).disabled) {
- // HTMLButtonElement 表示按钮元素类型
- return false
- }
-
- switch (element.nodeName) {
- case 'A': {
- // casting current element to Specific HTMLElement in order to be more type precise
- return (
- !!(element as HTMLAnchorElement).href &&
- (element as HTMLAnchorElement).rel !== 'ignore'
- )
- /*
- !! (element as HTMLAnchorElement).href
- !! 双重取反
- !!a 表示 a != ""&&a != undefined && a != null
-
-
- a.rel 用于指定当前文档与被链接文档的关系
- HTMLAnchorElement 表示锚点链接类型
-
-
- */
-
-
-
- }
- case 'INPUT': {
- return !(
- (element as HTMLInputElement).type === 'hidden' ||
- (element as HTMLInputElement).type === 'file'
- )
- /*
- HTMLInputElement 输入元素类型
- as typescript 中的类型断言
- 所谓类型断言指的是我知道 某个值的详细信息
-
- */
- }
- case 'BUTTON':
- case 'SELECT':
- case 'TEXTAREA': {
- return true
- }
- default: {
- return false
- }
- }
- }
-
- /**
- * @desc Set Attempt to set focus on the current node.
- * @param element
- * The node to attempt to focus on.
- * @returns
- * true if element is focused.
- */
- export const attemptFocus = (element: HTMLElement): boolean => {
- if (!isFocusable(element)) {
- // 判断是否可以获取焦点 入宫不可获取焦点就返回false
- return false
- }
- // Remove the old try catch block since there will be no error to be thrown
- element.focus?.()
- // ?. 表示 问号之前的成立才执行问号之后的代码
- return document.activeElement === element
- // document.activeElement 当前页面中获取焦点的元素
- }
-
- /**
- * Trigger an event
- * mouseenter, mouseleave, mouseover, keyup, change, click, etc.
- * @param {HTMLElement} elm
- * @param {String} name
- * @param {*} opts
- */
- export const = function (
- elm: HTMLElement,//当前dom元素
- name: string,//事件的名称
- ...opts: Array<boolean>//更多参数 opts,为一个数组, true 表示阻止事件冒泡 true 表示阻止默认行为
- ): HTMLElement {
- let eventName: string
-
- if (name.includes('mouse') || name.includes('click')) {
- // includes 表示是否包含 返回true或者false mouse 或者 click 都是鼠标事件 MouseEvents
- eventName = 'MouseEvents'
- } else if (name.includes('key')) {
- // key表示键盘事件
- eventName = 'KeyboardEvent'
- } else {
- // 既不是鼠标事件也不是键盘事件
- eventName = 'HTMLEvents'
- }
- const evt = document.createEvent(eventName)
- // 创建事件
- evt.initEvent(name, ...opts)
- // 初始化事件
- // opts 是数组 boolean 值 第一个表示是否阻止冒泡 是否阻止默认行为
- // document.createEvent dispatchEvent
- elm.dispatchEvent(evt)//触发事件
-
- /*
- 在其他地方可用document.addEventListener("事件名称") 来监听这些自定义事件
- */
-
- return elm
- }
-
- export const isLeaf = (el: HTMLElement) => !el.getAttribute('aria-owns')
-
- export const getSibling = (
- el: HTMLElement,//当前元素
- distance: number,//距离当前元素的位置
- elClass: string//元素的类名
- ) => {
- const { parentNode } = el//拿到当前元素的父元素
- if (!parentNode) return null//如果当前元素没有父元素就是 最顶级的元素 没有兄弟元素 就返回null
- const siblings = parentNode.querySelectorAll(elClass)//获取到所有兄弟元素的集合
- const index = Array.prototype.indexOf.call(siblings, el)// 获取到当前元素在所有兄弟元素集合中的位置 索引
- return siblings[index + distance] || null// 返回当前元素 距离兄弟元素指定位置的元素
- }
-
- export const focusNode = (el: HTMLElement) => {
- if (!el) return
- el.focus()
- !isLeaf(el) && el.click()
- }