export interface ViewportOffsetResult { left: number; top: number; right: number; bottom: number; rightIncludeBody: number; bottomIncludeBody: number; } export function getBoundingClientRect(element: Element): DOMRect | number { if (!element || !element.getBoundingClientRect) { return 0; } return element.getBoundingClientRect(); } function trim(string: string) { return (string || '').replace(/^[\s\uFEFF]+|[\s\uFEFF]+$/g, ''); } /* istanbul ignore next */ export function hasClass(el: Element, cls: string) { if (!el || !cls) return false; if (cls.indexOf(' ') !== -1) throw new Error('className should not contain space.'); if (el.classList) { return el.classList.contains(cls); } else { return (' ' + el.className + ' ').indexOf(' ' + cls + ' ') > -1; } } /* istanbul ignore next */ export function addClass(el: Element, cls: string) { if (!el) return; let curClass = el.className; const classes = (cls || '').split(' '); for (let i = 0, j = classes.length; i < j; i++) { const clsName = classes[i]; if (!clsName) continue; if (el.classList) { el.classList.add(clsName); } else if (!hasClass(el, clsName)) { curClass += ' ' + clsName; } } if (!el.classList) { el.className = curClass; } } /* istanbul ignore next */ export function removeClass(el: Element, cls: string) { if (!el || !cls) return; const classes = cls.split(' '); let curClass = ' ' + el.className + ' '; for (let i = 0, j = classes.length; i < j; i++) { const clsName = classes[i]; if (!clsName) continue; if (el.classList) { el.classList.remove(clsName); } else if (hasClass(el, clsName)) { curClass = curClass.replace(' ' + clsName + ' ', ' '); } } if (!el.classList) { el.className = trim(curClass); } } /** 获取当前元素的左上偏移 * left:最左边的元素到文档左边的距离 * top:元素顶部到文档顶部的距离 * right:元素最右边到文档右边的距离 * bottom:元素底部到文档底部的距离 * rightIncludeBody:文档最左边的元素到右边的距离 * bottomIncludeBody:元素底部到文档底部的距离 * * @description: */ export function getViewportOffset(element: Element): ViewportOffsetResult { const doc = document.documentElement; const docScrollLeft = doc.scrollLeft; const docScrollTop = doc.scrollTop; const docClientLeft = doc.clientLeft; const docClientTop = doc.clientTop; const pageXOffset = window.pageXOffset; const pageYOffset = window.pageYOffset; const box = getBoundingClientRect(element); const { left: retLeft, top: rectTop, width: rectWidth, height: rectHeight } = box as DOMRect; const scrollLeft = (pageXOffset || docScrollLeft) - (docClientLeft || 0); const scrollTop = (pageYOffset || docScrollTop) - (docClientTop || 0); const offsetLeft = retLeft + pageXOffset; const offsetTop = rectTop + pageYOffset; const left = offsetLeft - scrollLeft; const top = offsetTop - scrollTop; const clientWidth = window.document.documentElement.clientWidth; const clientHeight = window.document.documentElement.clientHeight; return { left: left, top: top, right: clientWidth - rectWidth - left, bottom: clientHeight - rectHeight - top, rightIncludeBody: clientWidth - left, bottomIncludeBody: clientHeight - top, }; } /* istanbul ignore next */ export function on( element: Element | HTMLElement | Document | Window, event: string, handler: EventListenerOrEventListenerObject, ): void { if (element && event && handler) { element.addEventListener(event, handler, false); } } /* istanbul ignore next */ export function off( element: Element | HTMLElement | Document | Window, event: string, handler: Fn, ): void { if (element && event && handler) { element.removeEventListener(event, handler, false); } }