UNPKG

chimee-helper-dom

Version:
713 lines (628 loc) 20.4 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } var _Object$assign = _interopDefault(require('babel-runtime/core-js/object/assign')); var _classCallCheck = _interopDefault(require('babel-runtime/helpers/classCallCheck')); var _createClass = _interopDefault(require('babel-runtime/helpers/createClass')); var _Array$from = _interopDefault(require('babel-runtime/core-js/array/from')); var toxicPredicateFunctions = require('toxic-predicate-functions'); var chimeeHelperEvents = require('chimee-helper-events'); var chimeeHelperUtils = require('chimee-helper-utils'); /** * @module dom * @author huzunjie * @description 一些常用的DOM判断及操作方法,可以使用dom.$('*')包装DOM,实现类jQuery的链式操作;当然这里的静态方法也可以直接使用。 */ var _divEl = chimeeHelperUtils.inBrowser ? document.createElement('div') : {}; var _textAttrName = 'innerText'; 'textContent' in _divEl && (_textAttrName = 'textContent'); var _arrPrototype = Array.prototype; /** * 读取HTML元素属性值 * @param {HTMLElement} el 目标元素 * @param {String} attrName 目标属性名称 * @return {String} */ function getAttr(el, attrName) { return el.getAttribute(attrName); } /** * 设置HTML元素属性值 * @param {HTMLElement} el 目标元素 * @param {String} attrName 目标属性名称 * @param {String} attrVal 目标属性值 */ function setAttr(el, attrName, attrVal) { if (attrVal === undefined) { el.removeAttribute(attrName); } else { el.setAttribute(attrName, attrVal); } } /** * 为HTML元素添加className * @param {HTMLElement} el 目标元素 * @param {String} cls 要添加的className(多个以空格分割) */ function addClassName(el, cls) { if (!cls || !(cls = cls.trim())) { return; } var clsArr = cls.split(/\s+/); if (el.classList) { clsArr.forEach(function (c) { return el.classList.add(c); }); } else { var curCls = ' ' + (el.className || '') + ' '; clsArr.forEach(function (c) { curCls.indexOf(' ' + c + ' ') === -1 && (curCls += ' ' + c); }); el.className = curCls.trim(); } } /** * 为HTML元素移除className * @param {HTMLElement} el 目标元素 * @param {String} cls 要移除的className(多个以空格分割) */ function removeClassName(el, cls) { if (!cls || !(cls = cls.trim())) { return; } var clsArr = cls.split(/\s+/); if (el.classList) { clsArr.forEach(function (c) { return el.classList.remove(c); }); } else { var curCls = ' ' + el.className + ' '; clsArr.forEach(function (c) { var tar = ' ' + c + ' '; while (curCls.indexOf(tar) !== -1) { curCls = curCls.replace(tar, ' '); } }); el.className = curCls.trim(); } } /** * 检查HTML元素是否已设置className * @param {HTMLElement} el 目标元素 * @param {String} className 要检查的className * @return {Boolean} */ function hasClassName(el, className) { return new RegExp('(?:^|\\s)' + className + '(?=\\s|$)').test(el.className); } /** * addEventListener 是否已支持 passive * @return {Boolean} */ exports.supportsPassive = false; try { var opts = Object.defineProperty({}, 'passive', { get: function get() { exports.supportsPassive = true; } }); if (chimeeHelperUtils.inBrowser) window.addEventListener('test', null, opts); } catch (e) { console.error(e); } /** * 为HTML元素移除事件监听 * @param {HTMLElement} el 目标元素 * @param {String} type 事件名称 * @param {Function} handler 处理函数 * @param {Boolean} once 是否只监听一次 * @param {Boolean} capture 是否在捕获阶段的监听 */ function removeEvent(el, type, handler) { var once = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; var capture = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false; if (capture !== undefined && !toxicPredicateFunctions.isBoolean(capture) && exports.supportsPassive) { capture = { passive: true }; } if (once) { /* 尝试从缓存中读取包装后的方法 */ var handlerWrap = chimeeHelperEvents.removeEventCache(el, type + '_once', handler); if (handlerWrap) { handler = handlerWrap; } } el.removeEventListener(type, handler, capture); } /** * 为HTML元素添加事件监听 * @param {HTMLElement} el 目标元素 * @param {String} type 事件名称 * @param {Function} handler 处理函数 * @param {Boolean} once 是否只监听一次 * @param {Boolean|Object} capture 是否在捕获阶段监听,这里也可以传入 { passive: true } 表示被动模式 */ function addEvent(el, type, handler) { var once = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; var capture = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false; if (capture !== undefined && !toxicPredicateFunctions.isBoolean(capture) && exports.supportsPassive) { capture = { passive: true }; } if (once) { var oldHandler = handler; handler = function () { return function () { for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } oldHandler.apply(this, args); removeEvent(el, type, handler, once, capture); }; }(); /* 将包装后的方法记录到缓存中 */ chimeeHelperEvents.addEventCache(el, type + '_once', oldHandler, handler); } el.addEventListener(type, handler, capture); } /** * 为HTML元素添加事件代理 * @param {HTMLElement} el 目标元素 * @param {String} selector 要被代理的元素 * @param {String} type 事件名称 * @param {Function} handler 处理函数 * @param {Boolean} capture 是否在捕获阶段监听 */ function addDelegate(el, selector, type, handler) { var capture = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false; if (capture !== undefined && !toxicPredicateFunctions.isBoolean(capture) && exports.supportsPassive) { capture = { passive: true }; } var handlerWrap = function handlerWrap(e) { var targetElsArr = findParents(e.target || e.srcElement, el, true); var targetElArr = query(selector, el, true); var retEl = void 0; if (targetElArr.find) { retEl = targetElArr.find(function (seEl) { return targetElsArr.find(function (tgEl) { return seEl === tgEl; }); }); } else { // Fixed IE11 Array.find not defined bug targetElArr.forEach(function (seEl) { return !retEl && targetElsArr.forEach(function (tgEl) { if (!retEl && seEl === tgEl) { retEl = tgEl; } }); }); } retEl && handler.apply(retEl, arguments); }; /* 将包装后的方法记录到缓存中 */ chimeeHelperEvents.addEventCache(el, type + '_delegate_' + selector, handler, handlerWrap); el.addEventListener(type, handlerWrap, capture); } /** * 为HTML元素移除事件代理 * @param {HTMLElement} el 目标元素 * @param {String} selector 要被代理的元素 * @param {String} type 事件名称 * @param {Function} handler 处理函数 * @param {Boolean} capture 是否在捕获阶段监听 */ function removeDelegate(el, selector, type, handler) { var capture = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false; if (capture !== undefined && !toxicPredicateFunctions.isBoolean(capture) && exports.supportsPassive) { capture = { passive: true }; } /* 尝试从缓存中读取包装后的方法 */ var handlerWrap = chimeeHelperEvents.removeEventCache(el, type + '_delegate_' + selector, handler); handlerWrap && el.removeEventListener(type, handlerWrap, capture); } /** * 读取HTML元素样式值 * @param {HTMLElement} el 目标元素 * @param {String} key 样式key * @return {String} */ function getStyle(el, key) { return (el.currentStyle || document.defaultView.getComputedStyle(el, null))[key] || el.style[key]; } /** * 设置HTML元素样式值 * @param {HTMLElement} el 目标元素 * @param {String} key 样式key * @param {String} val 样式值 */ function setStyle(el, key, val) { if (toxicPredicateFunctions.isObject(key)) { for (var k in key) { setStyle(el, k, key[k]); } } else { el.style[key] = val; } } /** * 根据选择器查询目标元素 * @param {String} selector 选择器,用于 querySelectorAll * @param {HTMLElement} container 父容器 * @param {Boolean} toArray 强制输出为数组 * @return {NodeList|Array} */ function query(selector) { var container = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : document; var toArray = arguments[2]; var retNodeList = container.querySelectorAll(selector); return toArray ? _Array$from(retNodeList) : retNodeList; } /** * 从DOM树中移除el * @param {HTMLElement} el 目标元素 */ function removeEl(el) { el.parentNode.removeChild(el); } /** * 查找元素的父节点们 * @param {HTMLElement} el 目标元素 * @param {HTMLElement} endEl 最大父容器(不指定则找到html) * @param {Boolean} haveEl 包含当前元素 * @param {Boolean} haveEndEl 包含设定的最大父容器 */ function findParents(el) { var endEl = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; var haveEl = arguments[2]; var haveEndEl = arguments[3]; var retEls = []; if (haveEl) { retEls.push(el); } while (el && el.parentNode !== endEl) { el = el.parentNode; el && retEls.push(el); } if (haveEndEl) { retEls.push(endEl); } return retEls; } /** * @class NodeWrap * @description * NodeWrap DOM包装器,用以实现基本的链式操作 * new dom.NodeWrap('*') 相当于 dom.$('*') * 这里面用于DOM操作的属性方法都是基于上面静态方法实现,有需要可以随时修改补充 * @param {String} selector 选择器(兼容 String||HTMLString||NodeList||NodeArray||HTMLElement) * @param {HTMLElement} container 父容器(默认为document) */ var NodeWrap = function () { function NodeWrap(selector) { var container = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : document; _classCallCheck(this, NodeWrap); var _this = this; _this.selector = selector; /* String||NodeList||HTMLElement 识别处理 */ var elsArr = void 0; if (selector && selector.constructor === NodeList) { /* 支持直接传入NodeList来构建包装器 */ elsArr = chimeeHelperUtils.makeArray(selector); } else if (toxicPredicateFunctions.isArray(selector)) { /* 支持直接传入Node数组来构建包装器 */ elsArr = selector; } else if (toxicPredicateFunctions.isString(selector)) { if (selector.indexOf('<') === 0) { /* 支持直接传入HTML字符串来新建DOM并构建包装器 */ _divEl.innerHTML = selector; elsArr = query('*', _divEl, true); } else { /* 支持直接传入字符串选择器来查找DOM并构建包装器 */ elsArr = query(selector, container, true); } } else { /* 其他任意对象直接构建包装器 */ elsArr = [selector]; } _Object$assign(_this, elsArr); /* NodeWrap本意可以 extends Array省略构造方法中下面这部分代码,但目前编译不支持 */ _this.length = elsArr.length; } /** * 循环遍历DOM集合 * @param {Function} fn 遍历函数 fn(item, i) * @return {Object} */ _createClass(NodeWrap, [{ key: 'each', value: function each() { for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { args[_key2] = arguments[_key2]; } _arrPrototype.forEach.apply(this, args); return this; } /** * 添加元素到DOM集合 * @param {HTMLElement} el 要加入的元素 * @return {this} */ }, { key: 'push', value: function push() { for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { args[_key3] = arguments[_key3]; } _arrPrototype.push.apply(this, args); return this; } /** * 截取DOM集合片段,并得到新的包装器splice * @param {Nubmer} start * @param {Nubmer} count * @return {NodeWrap} 新的DOM集合包装器 */ }, { key: 'splice', value: function splice() { for (var _len4 = arguments.length, args = Array(_len4), _key4 = 0; _key4 < _len4; _key4++) { args[_key4] = arguments[_key4]; } return $(_arrPrototype.splice.apply(this, args)); } /** * 查找子元素 * @param {String} selector 选择器 * @return {NodeWrap} 新的DOM集合包装器 */ }, { key: 'find', value: function find(selector) { var childs = []; this.each(function (el) { childs = childs.concat(query(selector, el, true)); }); var childsWrap = $(childs); childsWrap.parent = this; childsWrap.selector = selector; return childsWrap; } /** * 添加子元素 * @param {HTMLElement} childEls 要添加的HTML元素 * @return {this} */ }, { key: 'append', value: function append(childEls) { var childsWrap = $(childEls); var firstEl = this[0]; childsWrap.each(function (newEl) { return firstEl.appendChild(newEl); }); return this; } /** * 将元素集合添加到指定容器 * @param {HTMLElement} parentEl 要添加到父容器 * @return {this} */ }, { key: 'appendTo', value: function appendTo(parentEl) { $(parentEl).append(this); return this; } /** * DOM集合text内容读写操作 * @param {String} val 文本内容(如果有设置该参数则执行写操作,否则执行读操作) * @return {this} */ }, { key: 'text', value: function text(val) { if (arguments.length === 0) { return this[0][_textAttrName]; } return this.each(function (el) { el[_textAttrName] = val; }); } /** * DOM集合HTML内容读写操作 * @param {String} html html内容(如果有设置该参数则执行写操作,否则执行读操作) * @return {this} */ }, { key: 'html', value: function html(_html) { if (arguments.length === 0) { return this[0].innerHTML; } return this.each(function (el) { el.innerHTML = _html; }); } /** * DOM集合属性读写操作 * @param {String} name 属性名称 * @param {String} val 属性值(如果有设置该参数则执行写操作,否则执行读操作) * @return {this} */ }, { key: 'attr', value: function attr(name, val) { if (arguments.length === 1) { return getAttr(this[0], name); } return this.each(function (el) { return setAttr(el, name, val); }); } /** * DOM集合dataset读写操作 * @param {String} key 键名 * @param {Any} val 键值(如果有设置该参数则执行写操作,否则执行读操作) * @return {this} */ }, { key: 'data', value: function data(key, val) { if (arguments.length === 0) { return this[0].dataset || {}; } if (arguments.length === 1) { return (this[0].dataset || {})[key]; } return this.each(function (el) { (el.dataset || (el.dataset = {}))[key] = val; }); } /** * DOM集合样式读写操作 * @param {String} key 样式key * @param {String} val 样式值(如果有设置该参数则执行写操作,否则执行读操作) * @return {this} */ }, { key: 'css', value: function css(key, val) { if (arguments.length === 1 && !toxicPredicateFunctions.isObject(key)) { return getStyle(this[0], key); } return this.each(function (el) { return setStyle(el, key, val); }); } /** * 为DOM集合增加className * @param {String} cls 要增加的className * @return {this} */ }, { key: 'addClass', value: function addClass(cls) { return this.each(function (el) { return addClassName(el, cls); }); } /** * 移除当前DOM集合的className * @param {String} cls 要移除的className * @return {this} */ }, { key: 'removeClass', value: function removeClass(cls) { return this.each(function (el) { return removeClassName(el, cls); }); } /** * 检查索引0的DOM是否有className * @param {String} cls 要检查的className * @return {this} */ }, { key: 'hasClass', value: function hasClass(cls) { return hasClassName(this[0], cls); } /** * 为DOM集合添加事件监听 * @param {String} type 事件名称 * @param {Function} handler 处理函数 * @param {Boolean} once 是否只监听一次 * @param {Boolean} capture 是否在捕获阶段监听 * @return {this} */ }, { key: 'on', value: function on(type, handler) { var once = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; var capture = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; return this.each(function (el) { return addEvent(el, type, handler, once, capture); }); } /** * 为DOM集合解除事件监听 * @param {String} type 事件名称 * @param {Function} handler 处理函数 * @param {Boolean} once 是否只监听一次 * @param {Boolean} capture 是否在捕获阶段监听 * @return {this} */ }, { key: 'off', value: function off(type, handler) { var once = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; var capture = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; return this.each(function (el) { return removeEvent(el, type, handler, once, capture); }); } /** * 为DOM集合绑定事件代理 * @param {String} selector 目标子元素选择器 * @param {String} type 事件名称 * @param {Function} handler 处理函数 * @param {Boolean} capture 是否在捕获阶段监听 * @return {this} */ }, { key: 'delegate', value: function delegate(selector, type, handler) { var capture = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; return this.each(function (el) { return addDelegate(el, selector, type, handler, capture); }); } /** * 为DOM集合解绑事件代理 * @param {String} selector 目标子元素选择器 * @param {String} type 事件名称 * @param {Function} handler 处理函数 * @param {Boolean} capture 是否在捕获阶段监听 * @return {this} */ }, { key: 'undelegate', value: function undelegate(selector, type, handler) { var capture = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; return this.each(function (el) { return removeDelegate(el, selector, type, handler, capture); }); } /** * 从DOM树中移除 * @return {this} */ }, { key: 'remove', value: function remove() { return this.each(function (el) { return removeEl(el); }); } }]); return NodeWrap; }(); function $(selector, container) { return selector.constructor === NodeWrap ? selector : new NodeWrap(selector, container); } exports.getAttr = getAttr; exports.setAttr = setAttr; exports.addClassName = addClassName; exports.removeClassName = removeClassName; exports.hasClassName = hasClassName; exports.removeEvent = removeEvent; exports.addEvent = addEvent; exports.addDelegate = addDelegate; exports.removeDelegate = removeDelegate; exports.getStyle = getStyle; exports.setStyle = setStyle; exports.query = query; exports.removeEl = removeEl; exports.findParents = findParents; exports.NodeWrap = NodeWrap; exports.$ = $;