UNPKG

@alifd/next

Version:

A configurable component library for web built on React.

418 lines (363 loc) 11.4 kB
'use strict'; exports.__esModule = true; exports.matches = exports.hasDOM = undefined; var _typeof2 = require('babel-runtime/helpers/typeof'); var _typeof3 = _interopRequireDefault(_typeof2); exports.hasClass = hasClass; exports.addClass = addClass; exports.removeClass = removeClass; exports.toggleClass = toggleClass; exports.getNodeHozWhitespace = getNodeHozWhitespace; exports.getStyle = getStyle; exports.setStyle = setStyle; exports.scrollbar = scrollbar; exports.hasScroll = hasScroll; exports.getOffset = getOffset; exports.getPixels = getPixels; exports.getClosest = getClosest; exports.getMatches = getMatches; exports.saveRef = saveRef; var _string = require('./string'); var _object = require('./object'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /** * 是否能使用 DOM 方法 * @type {Boolean} */ var hasDOM = exports.hasDOM = typeof window !== 'undefined' && !!window.document && !!document.createElement; /** * 节点是否包含指定 className * @param {Element} node * @param {String} className * @return {Boolean} * * @example * dom.hasClass(document.body, 'foo'); */ 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'); */ 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'); */ 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'); */ 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 */ var matches = exports.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|top/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 }; function getNodeHozWhitespace(node) { var paddingLeft = getStyle(node, 'paddingLeft'); var paddingRight = getStyle(node, 'paddingRight'); var marginLeft = getStyle(node, 'marginLeft'); var marginRight = getStyle(node, 'marginRight'); return paddingLeft + paddingRight + marginLeft + marginRight; } /** * 获取元素计算后的样式 * @param {Element} node DOM 节点 * @param {String} name 属性名 * @return {Number|Object} */ function getStyle(node, name) { /* istanbul ignore if */ if (!hasDOM || !node) { return null; } var style = _getComputedStyle(node); // 如果不指定属性名,则返回全部值 if (arguments.length === 1) { return style; } // if style is {}(e.g. node isn't a element node), return null if ((0, _object.isPlainObject)(style)) { return null; } name = floatMap[name] ? 'cssFloat' in node.style ? 'cssFloat' : 'styleFloat' : name; return _getStyleValue(node, name, style.getPropertyValue((0, _string.hyphenate)(name)) || node.style[(0, _string.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 * }); */ function setStyle(node, name, value) { /* istanbul ignore if */ if (!hasDOM || !node) { return false; } // 批量设置多个值 if ((typeof name === 'undefined' ? 'undefined' : (0, _typeof3.default)(name)) === 'object' && arguments.length === 2) { (0, _object.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[(0, _string.camelcase)(name)] = value; // IE8 support } } var isScrollDisplay = function isScrollDisplay(element) { try { var scrollbarStyle = window.getComputedStyle(element, '::-webkit-scrollbar'); return !scrollbarStyle || scrollbarStyle.getPropertyValue('display') !== 'none'; } catch (e) { // ignore error for firefox } return true; }; /** * 获取默认的滚动条大小(通过创造一个滚动元素,读取滚动元素的滚动条信息) * @return {Object} width, height */ function scrollbar() { var scrollDiv = document.createElement('div'); scrollDiv.className += 'just-to-get-scrollbar-size'; setStyle(scrollDiv, { position: 'absolute', width: '100px', height: '100px', overflow: 'scroll', top: '-9999px' }); document.body && 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 }; } function hasScroll(containerNode) { // 当元素带有 overflow: hidden 一定没有滚动条 var overflow = getStyle(containerNode, 'overflow'); if (overflow === 'hidden') { return false; } var parentNode = containerNode.parentNode; return parentNode && parentNode.scrollHeight > parentNode.clientHeight && scrollbar().width > 0 && isScrollDisplay(parentNode) && isScrollDisplay(containerNode); } /** * 获取元素距离视口顶部和左边的偏移距离 * @return {Object} top, left */ 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 */ 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 */ 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 */ 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; } function saveRef(ref) { if (!ref) { return null; } return function (element) { if (typeof ref === 'string') { throw new Error('can not set ref string for ' + ref); } else if (typeof ref === 'function') { ref(element); } else if (Object.prototype.hasOwnProperty.call(ref, 'current')) { ref.current = element; } }; }