chimee-helper-dom
Version:
dom hanlders of chimee
713 lines (628 loc) • 20.4 kB
JavaScript
;
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.$ = $;