@coreui/vue-pro
Version:
UI Components Library for Vue.js
91 lines (75 loc) • 2.82 kB
text/typescript
/**
* Gets all focusable child elements within a container.
* Uses a comprehensive selector to find elements that can receive focus.
* @param element - The container element to search within
* @returns Array of focusable HTML elements
*/
export const focusableChildren = (element: HTMLElement): HTMLElement[] => {
const focusableSelectors = [
'a[href]',
'button:not([disabled])',
'input:not([disabled])',
'textarea:not([disabled])',
'select:not([disabled])',
'details',
'[tabindex]:not([tabindex="-1"])',
'[contenteditable="true"]',
].join(',')
const elements = [...element.querySelectorAll<HTMLElement>(focusableSelectors)] as HTMLElement[]
return elements.filter((el) => !isDisabled(el) && isVisible(el))
}
/**
* Checks if an element is disabled.
* Considers various ways an element can be disabled including CSS classes and attributes.
* @param element - The HTML element to check
* @returns True if the element is disabled, false otherwise
*/
export const isDisabled = (element: HTMLElement): boolean => {
if (!element || element.nodeType !== Node.ELEMENT_NODE) {
return true
}
if (element.classList.contains('disabled')) {
return true
}
if ('disabled' in element && typeof element.disabled === 'boolean') {
return element.disabled
}
return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false'
}
/**
* Type guard to check if an object is an Element.
* Handles edge cases including jQuery objects.
* @param object - The object to check
* @returns True if the object is an Element, false otherwise
*/
export const isElement = (object: unknown): object is Element => {
if (!object || typeof object !== 'object') {
return false
}
return 'nodeType' in object && typeof object.nodeType === 'number'
}
/**
* Checks if an element is visible in the DOM.
* Considers client rects and computed visibility styles, handling edge cases like details elements.
* @param element - The HTML element to check for visibility
* @returns True if the element is visible, false otherwise
*/
export const isVisible = (element: HTMLElement): boolean => {
if (!isElement(element) || element.getClientRects().length === 0) {
return false
}
const elementIsVisible = getComputedStyle(element).getPropertyValue('visibility') === 'visible'
// Handle `details` element as its content may falsely appear visible when it is closed
const closedDetails = element.closest('details:not([open])')
if (!closedDetails) {
return elementIsVisible
}
if (closedDetails !== element) {
const summary = element.closest('summary')
// Check if summary is a direct child of the closed details
if (summary?.parentNode !== closedDetails) {
return false
}
}
return elementIsVisible
}