mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-03-02 15:27:18 +00:00
340 lines
8.7 KiB
JavaScript
340 lines
8.7 KiB
JavaScript
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;
|
||
} |