# 封装函数集 **Repository Path**: Crivk/encapsulate-function-set ## Basic Information - **Project Name**: 封装函数集 - **Description**: 工具类函数的封装整理,便于后续代码中的使用 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2022-10-14 - **Last Updated**: 2022-11-30 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 保存封装的可拆箱快速使用函数 ## 1. DOM 操作相关的函数(dom-operation) ### 1.1 addEvent 给元素绑定事件处理函数 > 1. **对于 IE9 及以下的浏览器并不支持 `addEventListener` 这个给事件绑定处理函数的方法** > 2. **对于最新的浏览器而言不支持 `attachEvent` 绑定事件处理函数的方法, 且该方法存在 this 指向** > **并不会自动指向绑定事件处理函数的元素对象** > 3. **当两者都不存在时, 使用兼容性最好的句柄事件绑定** ```js /** * @description 给元素绑定事件处理函数 * @param elem {HTMLElement} 绑定事件处理函数的元素对象 * @param type {String} 所要绑定的事件名称 * @param handle {Function} 所绑定的事件处理函数 */ function addEvent(elem, type, handle) { if (elem.addEventListener) { elem.addEventListener(type, handle, false); } else if (elem.attachEvent) { elem.attachEvent('on' + type, handle.bind(elem)); } else { elem['on' + type] = handle; } } ``` ### 1.2 elemChildren 获取子元素节点 > 1. **Element 对象上的`children`属性在 IE9 及以下的版本并不兼容, 因此利用 Element 对象上的`nodeType`属性及`childNodes`属性封装出一个具有兼容性的获取子元素节点的方法** ```js /** * @description 获取元素下的元素子节点集合 * @param {HTMLElement} elem 需要获取子元素集合的目标父节点 * @returns { HTMLElement[] } 返回子元素集合 */ function elemChildren(elem) { var obj = { length: 0, splice: Array.prototype.splice, push: Array.prototype.push, }, children = elem.childNodes, len = children.length, item; for (var i = 0; i < len; i++) { item = children[i]; if (item.nodeType === 1) { obj.push(item); } } return obj; } ``` ### 1.3 insertAfter 在原型上封装向目标元素后添加新节点的方法 ```js Node.prototype.insertAfter = function (node, target) { var parentNode = this; if (parentNode.lastChild === target) { parentNode.appendChild(node); } else { parentNode.insertBefore(node, target.nextSibling); } }; ``` ### 1.4 getScrollOffset 封装获取滚动条滚动距离的兼容性方法 > 1. **在 IE9 某些版本/IE8 以下是不支持 window.pageXOffset 属性获取滚动条滚动距离的** > 2. **在 IE9/IE8 及以下中的浏览器`document.body.scrollLeft`与`document.documentElement`有且只会存在一个, 另一个值为 0** > 3. **window.scrollX/scrollY 的别名就是 window.pageXOffset** ```js /** * @description 封装获取滚动条滚动距离的兼容性方法 * @returns { {left: Number, top: Number}} 返回滚动条距离信息对象 */ function getScrollOffset() { if (window.pageXOffset) { return { left: window.pageXOffset, top: window.pageYOffset, }; } else { return { left: document.body.scrollLeft + document.documentElement.scrollLeft, top: document.body.scrollTop + document.documentElement.scrollTop, }; } } ``` ### 1.5 getViewSize 获取视图尺寸的兼容性方法 > 1. **在标准模式下获取视图(页面真实尺寸信息)通过`document.documentElement.clientWidth/clientHeight`获取** > 2. **在怪异模式下获取视图(页面真实尺寸信息)通过`document.body.clientWidth/clientHeight`获取** > 3. **HTML 的模式: 1. CSS1Compat 标准模式 2. BackCompat 怪异模式** ```js /** * @description 封装获取页面真实宽度/高度(可视区域)信息的方法 * @returns { { width: Number, height: Number} } 返回页面真实高宽信息对象 */ function getViewSize() { if (window.innerWidth) { return { width: window.innerWidth, height: window.innerHeight, }; } else { // HTML的模式: 1. CSS1Compat 标准模式 2. BackCompat 怪异模式 // 在标准模式下获取页面的可视尺寸信息得通过documentElement也就是html对象获取 // 而在怪异模式下, 则是通过body获取 return document.compatMode === 'BackCompat' ? { width: document.body.clientWidth, height: document.body.clientHeight, } : { width: document.documentElement.clientWidth, height: document.documentElement.clientHeight, }; } } ``` ### 1.6 getScrollSize 获取页面完整高度/高度的兼容性方法 > 1. **页面高度 = 滚动条 Y 轴滚动位置 + 页面真实高度** ```js /** * @description 获取页面完整高度/高度的方法(页面高度 = 滚动条Y轴滚动位置 + 页面真实高度) * @returns { { width: Number, height: Number} } 返回页面完整高度宽度对象信息 */ function getScrollSize() { if (document.body.scrollWidth) { return { width: document.body.scrollWidth, height: document.body.scrollHeight, }; } else { return { width: document.documentElement.scrollWidth, height: document.documentElement.scrollHeight, }; } } ``` ### 1.7 getElemDocPosition 获取元素距离页面最左侧、最顶端的距离 > 1. **如果向外寻找元素都不具有定位属性,最终会找到 body 元素** > 2. **如果有具有定位属性的定位元素,则返回这个定位元素** ```js /** * @param { HTMLElement } el 目标元素节点 * @returns { { left: Number, top: Number} } 返回元素距离文档左侧、顶部的距离信息 */ function getElemDocPosition(el) { // HTMLElement.prototype.offsetParent方法可以返回调用元素的外层具有定位属性的元素节点 // 直到找到body元素 var parent = el.offsetParent, offsetLeft = el.offsetLeft, offsetTop = el.offsetTop; // 迭代累加元素距离各父级之间的距离 while (parent) { offsetLeft += parent.offsetLeft; offsetTop += parent.offsetTop; parent = parent.offsetParent; } return { left: offsetLeft, top: offsetTop, }; } /** * @description 递归写法 */ function getElemDocPosition(el) { // HTMLElement.prototype.offsetParent方法可以返回调用元素的外层具有定位属性的元素节点 // 直到找到body元素 var parent = el.offsetParent, offsetLeft = el.offsetLeft, offsetTop = el.offsetTop; // 迭代累加元素距离各父级之间的距离 if (parent) { offsetLeft += getElemDocPosition(parent).left; offsetTop += getElemDocPosition(parent).top; } return { left: offsetLeft, top: offsetTop, }; } ``` ### 1.8 获取元素属性方法的兼容性方法 > 1. **window.getComputedStyle 方法在 IE8 及以下不支持** > 2. **getComputedStyle 方法会将相对单位转换为绝对单位,如 rem/em 换算为 px 单位,16 进制换算为 RGB** ```js /** * @description 获取元素属性方法的兼容性方法 * @param { HTMLElement } elem 目标元素 * @param { String } prop 获取的CSS属性名 */ function getStyles(elem, prop) { if (window.getComputedStyle) { return prop ? window.getComputedStyle(elem, null)[prop] : window.getComputedStyle(elem); } else { return prop ? elem.currentStyle[prop] : elem.currentStyle; } } ``` ### 1.9 获取元素向上 N 层所对应的父级元素节点 > 1. **因为只有元素节点可以使用 appendChild 方法, 因此在向上一层级查找时不需要判断父元素是否为元素节点** ```js function elemParent(elem, hierarchy) { var type = typeof hierarchy, parent = elem; if (type === 'undefined' || type !== 'number') { return parent.parentNode ? parent.parentNode : parent; } else if (hierarchy < 0) { return undefined; } else { while (hierarchy) { parent.parentNode && (parent = parent.parentNode); hierarchy--; } return parent; } } ``` ### 1.10 解除事件处理函数 ```js function removeEvent(elem, type, handle) { if (elem.removeEventListener) { elem.removeEventListener(type, handle, false); } else if (elem.detachEvent) { elem.detachEvent('on' + type, handle); } else { elem['on' + type] = null; } } ``` ### 1.11 取消冒泡事件函数 ```js function cancelBubble(e) { var e = e || window.e; if (e.stopPropagation) { e.stopPropagation(); } else { e.cancelBubble = true; } } ``` ### 1.12 取消默认事件函数 ```js function preventDefaultEvent(e) { var e = e || window.event; if (e.preventDefault) { e.preventDefault(); } else { e.returnValue = false; } } ``` ### 1.13 封装获取 pageX/Y 的兼容性方法 > 1. **pageX/Y 方法在 IE9 及以下不支持** > 2. **可以利用滚动条距离 + 可视范围元素坐标计算出 pageX/Y** ```js function pagePos(e) { var e = e || window.event, // 滚动条横向移动部分、竖向移动部分 sLeft = getScrollOffset().left, sTop = getScrollOffset().top, // 当前页面文档偏移量 cLeft = document.documentElement.clientLeft || 0, cTop = document.documentElement.clientTop || 0; return { x: sLeft + e.clientX - cLeft, y: sTop + e.clientY - cTop, }; } ``` ### 1.14 封装拖拽功能(基础版) ```js function elemDrag(elem) { var x, y, wWidth = getViewSize().width, wHeight = getViewSize().height, eWidth = parseInt(getStyles(elem, 'width')), eHeight = parseInt(getStyles(elem, 'height')); addEvent(elem, 'mousedown', function (e) { var e = e || window.e; x = pagePos(e).X - parseInt(getStyles(this, 'left')); y = pagePos(e).Y - parseInt(getStyles(this, 'top')); addEvent(document, 'mousemove', mouseMove); addEvent(document, 'mouseup', mouseUp); // 避免冒泡事件 cancelBubble(e); // 避免默认事件 preventDefaultEvent(e); }); function mouseMove(e) { var e = e || window.event, elemLeft = pagePos(e).X - x, elemTop = pagePos(e).Y - y; if (elemLeft < 0) { elemLeft = 0; } else if (elemLeft > wWidth - eWidth) { elemLeft = wWidth - eWidth - 1; } if (elemTop < 0) { elemTop = 0; } else if (elemTop > wHeight - eHeight) { elemTop = wHeight - eHeight - 1; } elem.style.left = elemLeft + 'px'; elem.style.top = elemTop + 'px'; } function mouseUp(e) { var e = e || window.event; removeEvent(document, 'mousemove', mouseMove); removeEvent(document, 'mouseup', mouseUp); } } ``` ## 2. 日期对象 ### 2.1 时间格式化 ```js Date.prototype.getDateTime = function () { var year = this.getFullYear(), month = this.getMonth() + 1, day = this.getDate(), hours = this.getHours(), minutes = this.getMinutes(), seconds = this.getSeconds(); return ( year + '-' + format(month) + '-' + format(day) + ' ' + format(hours) + ':' + format(minutes) + ':' + format(seconds) ); function format(time) { return time < 0 ? '0' + time : time; } }; ``` # 3. 节流防抖 ## 3.1 防抖 ```js export const debounce = function (fn, delay, now) { var t = null, res = null; var debounced = function () { var _self = this, args = arguments; if (t) { clearTimeout(t); } // 是否立即执行一次响应 if (now) { // 记录下是否是第一次进入防抖 var exec = !t; // 生成一个计时器用于判断是否第一次进入防抖 t = setTimeout(function () { // 当时间超过延迟时清除定时器以下次进入时再次立即执行 clearTimeout(t); t = null; }, delay); if (exec) { res = fn.apply(_self, args); } else { t = setTimeout(function () { fn.apply(_self, args); }, delay); } } else { t = setTimeout(function () { res = fn.apply(_self, args); }, delay); } return res; }; debounced.remove = function () { clearTimeout(t); t = null; }; return debounced; }; ``` ## 3.2 节流 ```js export const throttle = function (fn, delay) { var t = null, // 获取到节流函数绑定时的时间戳 begin = new Date().getTime(), res; var throttled = function () { var _self = this, args = arguments, now = new Date().getTime(); // 清除上一次绑定的延迟事件 // 本次触发会再次绑定 clearTimeout(t); if (now - begin > delay) { // 当前触发时间与上次触发时间之差大于延迟时间 // 直接响应事件 res = fn.apply(_self, args); begin = now; } else { t = setTimeout(function () { res = fn.apply(_self, args); }, delay); } return res; }; throttled.remove = function () { clearTimeout(t); t = null; }; return throttled; }; ```