ray-template/src/utils/element.ts
2023-07-12 14:45:24 +08:00

291 lines
6.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { isValueType } from '@use-utils/hook'
import { APP_REGEX } from '@/appConfig/regexConfig'
import type {
EventListenerOrEventListenerObject,
PartialCSSStyleDeclaration,
ElementSelector,
} from '@/types/modules/utils'
/**
*
* @param element Target element dom
* @param event 绑定事件类型
* @param handler 事件触发方法
* @param useCapture 是否冒泡
*
* @remark 给元素绑定某个事件柄方法
*/
export const on = (
element: HTMLElement | Document | Window,
event: string,
handler: EventListenerOrEventListenerObject,
useCapture: boolean | AddEventListenerOptions = false,
) => {
if (element && event && handler) {
element.addEventListener(event, handler, useCapture)
}
}
/**
*
* @param element Target element dom
* @param event 卸载事件类型
* @param handler 所需卸载方法
* @param useCapture 是否冒泡
*
* @remark 卸载元素上某个事件柄方法
*/
export const off = (
element: HTMLElement | Document | Window,
event: string,
handler: EventListenerOrEventListenerObject,
useCapture: boolean | AddEventListenerOptions = false,
) => {
if (element && event && handler) {
element.removeEventListener(event, handler, useCapture)
}
}
/**
*
* @param element Target element dom
* @param className 所需添加className可: 'xxx xxx' | 'xxx' 格式添加(参考向元素绑定 css 语法)
*
* @remark 添加元素className(可: 'xxx xxx' | 'xxx'格式添加)
*/
export const addClass = (element: HTMLElement, className: string) => {
if (element) {
const classes = className.trim().split(' ')
classes.forEach((item) => {
if (item) {
element.classList.add(item)
}
})
}
}
/**
*
* @param element Target element dom
* @param className 所需删除className可: 'xxx xxx' | 'xxx' 格式删除(参考向元素绑定 css 语法)
*
* @remark 删除元素className(可: 'xxx xxx' | 'xxx'格式删除)
* @remark 如果输入值为 removeAllClass 则会删除该元素所有 class name
*/
export const removeClass = (
element: HTMLElement,
className: string | 'removeAllClass',
) => {
if (element) {
if (className === 'removeAllClass') {
const classList = element.classList
classList.forEach((curr) => classList.remove(curr))
} else {
const classes = className.trim().split(' ')
classes.forEach((item) => {
if (item) {
element.classList.remove(item)
}
})
}
}
}
/**
*
* @param element Target element dom
* @param className 查询元素是否含有此className可: 'xxx xxx' | 'xxx' 格式查询(参考向元素绑定 css 语法)
*
* @returns 返回boolean
*
* @remark 元素是否含有某个className(可: 'xxx xxx' | 'xxx' 格式查询)
*/
export const hasClass = (element: HTMLElement, className: string) => {
const elementClassName = element.className
const classes = className
.trim()
.split(' ')
.filter((item: string) => item !== '')
return elementClassName.includes(classes.join(' '))
}
/**
*
* @param el Target element dom
* @param styles 所需绑定样式(如果为字符串, 则必须以分号结尾每个行内样式描述)
*
*
* @example
* style of string
* ```
* const styles = 'width: 100px; height: 100px; background: red;'
*
* addStyle(styles)
* ```
* style of object
* ```
* const styles = {
* width: '100px',
* height: '100px',
* }
*
* addStyle(styles)
* ```
*/
export const addStyle = (
el: HTMLElement,
styles: PartialCSSStyleDeclaration | string,
) => {
if (!el) {
return
}
let styleObj: PartialCSSStyleDeclaration
if (isValueType<string>(styles, 'String')) {
styleObj = styles.split(';').reduce((pre, curr) => {
const [key, value] = curr.split(':').map((s) => s.trim())
if (key && value) {
pre[key] = value
}
return pre
}, {} as PartialCSSStyleDeclaration)
} else {
styleObj = styles
}
Object.keys(styleObj).forEach((key) => {
const value = styleObj[key]
if (key in el.style) {
el.style[key] = value
}
})
}
/**
*
* @param el Target element dom
* @param styles 所需卸载样式
*/
export const removeStyle = (
el: HTMLElement,
styles: (keyof CSSStyleDeclaration & string)[],
) => {
if (!el) {
return
}
styles.forEach((curr) => {
el.style.removeProperty(curr)
})
}
/**
*
* @param color 颜色格式
* @param alpha 透明度
* @returns 转换后的 rgba 颜色值
*
* @remark 将任意颜色值转为 rgba
*/
export const colorToRgba = (color: string, alpha = 1) => {
const hexPattern = /^#([0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$/i
const rgbPattern = /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/i
const rgbaPattern =
/^rgba\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3}),\s*(\d*(?:\.\d+)?)\)$/i
let result: string
if (hexPattern.test(color)) {
const hex = color.substring(1)
const rgb = [
parseInt(hex.substring(0, 2), 16),
parseInt(hex.substring(2, 4), 16),
parseInt(hex.substring(4, 6), 16),
]
result = 'rgb(' + rgb.join(', ') + ')'
} else if (rgbPattern.test(color)) {
result = color
} else if (rgbaPattern.test(color)) {
result = color
} else {
result = color
}
if (result && !result.startsWith('rgba')) {
result = result.replace('rgb', 'rgba').replace(')', `, ${alpha})`)
}
return result
}
/**
*
* @param element 需要匹配元素参数名称
* @returns 匹配元素列表
*
* @remark 使用 querySelectorAll 作为检索方法
* @remark 如果希望按照 attribute 匹配, 仅需要 'attr:xxx'传递参数即可
*
* @example
* class:
* const el = queryElements('.demo')
* id:
* const el = queryElements('#demo')
* attribute:
* const el = queryElements('attr:type=button')
* 或者可以这样写
* const el = queryElements('attr:type')
*/
export const queryElements = <T extends Element = Element>(
selector: ElementSelector,
) => {
if (!selector) {
return null
}
const queryParam = selector.startsWith('attr:')
? `[${selector.replace('attr:', '')}]`
: selector
try {
const elements = Array.from(document.querySelectorAll<T>(queryParam))
return elements
} catch (error) {
console.error(`Failed to get elements for selector '${selector}'`, error)
return null
}
}
/**
*
* @param size css size
* @param unit 自动填充 css 尺寸单位
*
* @remark 自动补全尺寸
*/
export const completeSize = (size: number | string, unit = 'px') => {
if (typeof size === 'number') {
return size.toString() + unit
} else if (
isValueType<string>(size, 'String') &&
APP_REGEX.validerCSSUnit.test(size)
) {
return size
} else {
return size + unit
}
}