340 lines
8.7 KiB
JavaScript
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 _typeof from 'babel-runtime/helpers/typeof';
import { camelcase, hyphenate } from './string';
import { each } from './object';
/**
* 是否能使用 DOM 方法
* @type {Boolean}
*/
export var hasDOM = typeof window !== 'undefined' && !!window.document && !!document.createElement;
/**
* 节点是否包含指定 className
* @param {Element} node
* @param {String} className
* @return {Boolean}
*
* @example
* dom.hasClass(document.body, 'foo');
*/
export function hasClass(node, className) {
/* istanbul ignore if */
if (!hasDOM || !node) {
return false;
}
if (node.classList) {
return node.classList.contains(className);
} else {
return node.className.indexOf(className) > -1;
}
}
/**
* 添加 className
* @param {Element} node
* @param {String} className
*
* @example
* dom.addClass(document.body, 'foo');
*/
export function addClass(node, className, _force) {
/* istanbul ignore if */
if (!hasDOM || !node) {
return;
}
if (node.classList) {
node.classList.add(className);
} else if (_force === true || !hasClass(node, className)) {
node.className += ' ' + className;
}
}
/**
* 移除 className
* @param {Element} node
* @param {String} className
*
* @example
* dom.removeClass(document.body, 'foo');
*/
export function removeClass(node, className, _force) {
/* istanbul ignore if */
if (!hasDOM || !node) {
return;
}
if (node.classList) {
node.classList.remove(className);
} else if (_force === true || hasClass(node, className)) {
node.className = node.className.replace(className, '').replace(/\s+/g, ' ').trim();
}
}
/**
* 切换 className
* @param {Element} node
* @param {String} className
* @return {Boolean} 执行后节点上是否还有此 className
*
* @example
* dom.toggleClass(document.body, 'foo');
*/
export function toggleClass(node, className) {
/* istanbul ignore if */
if (!hasDOM || !node) {
return false;
}
if (node.classList) {
return node.classList.toggle(className);
} else {
var flag = hasClass(node, className);
flag ? removeClass(node, className, true) : addClass(node, className, true);
return !flag;
}
}
/**
* 元素是否匹配 CSS 选择器
* @param {Element} node DOM 节点
* @param {String} selector CSS 选择器
* @return {Boolean}
*
* @example
* dom.matches(mountNode, '.container'); // boolean
*/
export var matches = function () {
var matchesFn = null;
/* istanbul ignore else */
if (hasDOM) {
var _body = document.body || document.head;
matchesFn = _body.matches ? 'matches' : _body.webkitMatchesSelector ? 'webkitMatchesSelector' : _body.msMatchesSelector ? 'msMatchesSelector' : _body.mozMatchesSelector ? 'mozMatchesSelector' : null;
}
return function (node, selector) {
if (!hasDOM || !node) {
return false;
}
return matchesFn ? node[matchesFn](selector) : false;
};
}();
/**
* 获取元素计算后的样式
* @private
* @param {Element} node
* @return {Object}
*/
function _getComputedStyle(node) {
return node && node.nodeType === 1 ? window.getComputedStyle(node, null) : {};
}
var PIXEL_PATTERN = /margin|padding|width|height|max|min|offset|size/i;
var removePixel = { left: 1, top: 1, right: 1, bottom: 1 };
/**
* 校验并修正元素的样式属性值
* @private
* @param {Element} node
* @param {String} type
* @param {Number} value
*/
function _getStyleValue(node, type, value) {
type = type.toLowerCase();
if (value === 'auto') {
if (type === 'height') {
return node.offsetHeight || 0;
}
if (type === 'width') {
return node.offsetWidth || 0;
}
}
if (!(type in removePixel)) {
// 属性值是否需要去掉 px 单位,这里假定此类的属性值都是 px 为单位的
removePixel[type] = PIXEL_PATTERN.test(type);
}
return removePixel[type] ? parseFloat(value) || 0 : value;
}
var floatMap = { cssFloat: 1, styleFloat: 1, float: 1 };
/**
* 获取元素计算后的样式
* @param {Element} node DOM 节点
* @param {String} name 属性名
* @return {Number|Object}
*/
export function getStyle(node, name) {
/* istanbul ignore if */
if (!hasDOM || !node) {
return null;
}
var style = _getComputedStyle(node);
// 如果不指定属性名,则返回全部值
if (arguments.length === 1) {
return style;
}
name = floatMap[name] ? 'cssFloat' in node.style ? 'cssFloat' : 'styleFloat' : name;
return _getStyleValue(node, name, style.getPropertyValue(hyphenate(name)) || node.style[camelcase(name)]);
}
/**
* 设置元素的样式
* @param {Element} node DOM 节点
* @param {Object|String} name 属性名,或者是一个对象,包含多个属性
* @param {Number|String} value 属性值
*
* @example
* // 设置单个属性值
* dom.setStyle(mountNode, 'width', 100);
* // 设置多条属性值
* dom.setStyle(mountNode, {
* width: 100,
* height: 200
* });
*/
export function setStyle(node, name, value) {
/* istanbul ignore if */
if (!hasDOM || !node) {
return false;
}
// 批量设置多个值
if ((typeof name === 'undefined' ? 'undefined' : _typeof(name)) === 'object' && arguments.length === 2) {
each(name, function (val, key) {
return setStyle(node, key, val);
});
} else {
name = floatMap[name] ? 'cssFloat' in node.style ? 'cssFloat' : 'styleFloat' : name;
if (typeof value === 'number' && PIXEL_PATTERN.test(name)) {
value = value + 'px';
}
node.style[camelcase(name)] = value; // IE8 support
}
}
/**
* 获取默认的滚动条大小
* @return {Object} width, height
*/
export function scrollbar() {
var scrollDiv = document.createElement('div');
setStyle(scrollDiv, {
position: 'absolute',
width: '100px',
height: '100px',
overflow: 'scroll',
top: '-9999px'
});
document.body.appendChild(scrollDiv);
var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;
var scrollbarHeight = scrollDiv.offsetHeight - scrollDiv.clientHeight;
document.body.removeChild(scrollDiv);
return {
width: scrollbarWidth,
height: scrollbarHeight
};
}
/**
* 获取元素距离视口顶部和左边的偏移距离
* @return {Object} top, left
*/
export function getOffset(node) {
var rect = node.getBoundingClientRect();
var win = node.ownerDocument.defaultView;
return {
top: rect.top + win.pageYOffset,
left: rect.left + win.pageXOffset
};
}
/**
* 获取不同单位转为 number 的长度
* @param {string|number} len 传入的长度
* @return {number} pixels
*/
export function getPixels(len) {
var win = document.defaultView;
if (typeof +len === 'number' && !isNaN(+len)) {
return +len;
}
if (typeof len === 'string') {
var PX_REG = /(\d+)px/;
var VH_REG = /(\d+)vh/;
if (Array.isArray(len.match(PX_REG))) {
return +len.match(PX_REG)[1] || 0;
}
if (Array.isArray(len.match(VH_REG))) {
var _1vh = win.innerHeight / 100;
return +(len.match(VH_REG)[1] * _1vh) || 0;
}
}
return 0;
}
/**
* 匹配特定选择器且离当前元素最近的祖先元素(也可以是当前元素本身),如果匹配不到,则返回 null
* @param {element} dom 待匹配的元素
* @param {string} selecotr 选择器
* @return {element} parent
*/
export function getClosest(dom, selector) {
/* istanbul ignore if */
if (!hasDOM || !dom) {
return null;
}
// ie9
/* istanbul ignore if */
if (!Element.prototype.closest) {
if (!document.documentElement.contains(dom)) return null;
do {
if (getMatches(dom, selector)) return dom;
dom = dom.parentElement;
} while (dom !== null);
} else {
return dom.closest(selector);
}
return null;
}
/**
* 如果元素被指定的选择器字符串选择getMatches() 方法返回true; 否则返回false
* @param {element} dom 待匹配的元素
* @param {string} selecotr 选择器
* @return {element} parent
*/
export function getMatches(dom, selector) {
/* istanbul ignore if */
if (!hasDOM || !dom) {
return null;
}
/* istanbul ignore if */
if (Element.prototype.matches) {
return dom.matches(selector);
} else if (Element.prototype.msMatchesSelector) {
return dom.msMatchesSelector(selector);
} else if (Element.prototype.webkitMatchesSelector) {
return dom.webkitMatchesSelector(selector);
}
return null;
}