UNPKG

dom-inspector

Version:
543 lines (428 loc) 20.5 kB
/* * DomInspector v1.2.4-beta.0 * (c) 2020 luoye <luoyefe@gmail.com> */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global.DomInspector = factory()); }(this, (function () { 'use strict'; function __$styleInject (css, returnValue) { if (typeof document === 'undefined') { return returnValue; } css = css || ''; var head = document.head || document.getElementsByTagName('head')[0]; var style = document.createElement('style'); style.type = 'text/css'; if (style.styleSheet){ style.styleSheet.cssText = css; } else { style.appendChild(document.createTextNode(css)); } head.appendChild(style); return returnValue; } __$styleInject(".dom-inspector {\n position: fixed;\n pointer-events: none;\n}\n\n.dom-inspector>div {\n\tposition: absolute;\n}\n\n.dom-inspector .tips {\n\tbackground-color: #333740;\n\tfont-size: 0;\n\tline-height: 18px;\n\tpadding: 3px 10px;\n\tposition: fixed;\n\tborder-radius: 4px;\n\tdisplay: none;\n}\n\n.dom-inspector .tips.reverse{\n\n}\n\n.dom-inspector .tips .triangle {\n\twidth: 0;\n\theight: 0;\n\tposition: absolute;\n\tborder-top: 8px solid #333740;\n\tborder-right: 8px solid transparent;\n\tborder-bottom: 8px solid transparent;\n\tborder-left: 8px solid transparent;\n\tleft: 10px;\n\ttop: 24px;\n}\n\n.dom-inspector .tips.reverse .triangle {\n\tborder-top: 8px solid transparent;\n\tborder-right: 8px solid transparent;\n\tborder-bottom: 8px solid #333740;\n\tborder-left: 8px solid transparent;\n\tleft: 10px;\n\ttop: -16px;\n}\n\n.dom-inspector .tips>div {\n\tdisplay: inline-block;\n\tvertical-align: middle;\n\tfont-size: 12px;\n\tfont-family: Consolas, Menlo, Monaco, Courier, monospace;\n\toverflow: auto;\n}\n\n.dom-inspector .tips .tag {\n\tcolor: #e776e0;\n}\n\n.dom-inspector .tips .id {\n\tcolor: #eba062;\n}\n\n.dom-inspector .tips .class {\n\tcolor: #8dd2fb;\n}\n\n.dom-inspector .tips .line {\n\tcolor: #fff;\n}\n\n.dom-inspector .tips .size {\n\tcolor: #fff;\n}\n\n.dom-inspector-theme-default {\n\n}\n\n.dom-inspector-theme-default .margin {\n\tbackground-color: rgba(255, 81, 81, 0.75);\n}\n\n.dom-inspector-theme-default .border {\n\tbackground-color: rgba(255, 241, 81, 0.75);\n}\n\n.dom-inspector-theme-default .padding {\n\tbackground-color: rgba(81, 255, 126, 0.75);\n}\n\n.dom-inspector-theme-default .content {\n\tbackground-color: rgba(81, 101, 255, 0.75);\n}\n", undefined); function mixin(target, source) { var targetCopy = target; Object.keys(source).forEach(function (item) { if ({}.hasOwnProperty.call(source, item)) { targetCopy[item] = source[item]; } }); return targetCopy; } function throttle(func) { var wait = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 100; var timeout = void 0; var elapsed = void 0; var lastRunTime = Date.now(); // 上次运行时间 return function none() { for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } var _this = this; clearTimeout(timeout); elapsed = Date.now() - lastRunTime; function later() { lastRunTime = Date.now(); timeout = null; func.apply(_this, args); } if (elapsed > wait) { later(); } else { timeout = setTimeout(later, wait - elapsed); } }; } function isNull(obj) { return Object.prototype.toString.call(obj).replace(/\[object[\s]/, '').replace(']', '').toLowerCase() === 'null'; } var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; var classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; var createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; var set = function set(object, property, value, receiver) { var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent !== null) { set(parent, property, value, receiver); } } else if ("value" in desc && desc.writable) { desc.value = value; } else { var setter = desc.set; if (setter !== undefined) { setter.call(receiver, value); } } return value; }; var toConsumableArray = function (arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; return arr2; } else { return Array.from(arr); } }; /* eslint-disable eqeqeq */ /* eslint-disable max-len */ function isDOM() { var obj = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; return (typeof obj === 'undefined' ? 'undefined' : _typeof(obj)) === 'object' && obj.nodeType === 1 && _typeof(obj.style) === 'object' && _typeof(obj.ownerDocument) === 'object'; } function $(selector, parent) { if (!parent) return document.querySelector(selector); if (isDOM(parent)) return parent.querySelector(selector); return document.querySelector(selector); } function addRule(selector, cssObj) { Object.keys(cssObj).forEach(function (item) { selector.style[item] = cssObj[item]; }); } function findIndex(ele, currentTag) { var nth = 0; while (ele) { if (ele.nodeName.toLowerCase() === currentTag) nth += 1; ele = ele.previousElementSibling; } return nth; } function findPos(ele) { var computedStyle = getComputedStyle(ele); var _x = ele.getBoundingClientRect().left - parseFloat(computedStyle['margin-left']); var _y = ele.getBoundingClientRect().top - parseFloat(computedStyle['margin-top']); var el = ele.parent; while (el) { computedStyle = getComputedStyle(el); _x += el.frameElement.getBoundingClientRect().left - parseFloat(computedStyle['margin-left']); _y += el.frameElement.getBoundingClientRect().top - parseFloat(computedStyle['margin-top']); el = el.parent; } return { top: _y, left: _x }; } /** * @param { Dom Element } * @return { Object } */ function getElementInfo$1(ele) { var result = {}; var requiredValue = ['border-top-width', 'border-right-width', 'border-bottom-width', 'border-left-width', 'margin-top', 'margin-right', 'margin-bottom', 'margin-left', 'padding-top', 'padding-right', 'padding-bottom', 'padding-left', 'z-index']; var computedStyle = getComputedStyle(ele); requiredValue.forEach(function (item) { result[item] = parseFloat(computedStyle[item]) || 0; }); mixin(result, { width: ele.offsetWidth - result['border-left-width'] - result['border-right-width'] - result['padding-left'] - result['padding-right'], height: ele.offsetHeight - result['border-top-width'] - result['border-bottom-width'] - result['padding-top'] - result['padding-bottom'] }); mixin(result, findPos(ele)); return result; } function getMaxZIndex() { return [].concat(toConsumableArray(document.querySelectorAll('*'))).reduce(function (r, e) { return Math.max(r, +window.getComputedStyle(e).zIndex || 0); }, 0); } function isParent(obj, parentObj) { while (obj !== undefined && obj !== null && obj.tagName.toUpperCase() !== 'BODY') { if (obj == parentObj) return true; obj = obj.parentNode; } return false; } var sep = 'DomInspector: '; var proxy = ['log', 'warn', 'error']; var exportObj = {}; proxy.forEach(function (item) { exportObj[item] = function funcName() { return console[item].call(this, sep + (arguments.length <= 0 ? undefined : arguments[0]), (arguments.length <= 1 ? undefined : arguments[1]) || ''); }; }); var DomInspector = function () { function DomInspector() { var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; classCallCheck(this, DomInspector); this._doc = window.document; this.root = options.root ? isDOM(options.root) ? options.root : $(options.root) : $('body'); if (isNull(this.root)) { exportObj.warn('Root element is null. Auto select body as root'); this.root = $('body'); } this.theme = options.theme || 'dom-inspector-theme-default'; this.exclude = this._formatExcludeOption(options.exclude || []); this.overlay = {}; this.overlayId = ''; this.target = ''; this.destroyed = false; this.maxZIndex = options.maxZIndex || getMaxZIndex() + 1; this._cachedTarget = ''; this._throttleOnMove = throttle(this._onMove.bind(this), 100); this._init(); } createClass(DomInspector, [{ key: 'enable', value: function enable() { if (this.destroyed) return exportObj.warn('Inspector instance has been destroyed! Please redeclare it.'); this.overlay.parent.style.display = 'block'; this.root.addEventListener('mousemove', this._throttleOnMove); } }, { key: 'pause', value: function pause() { this.root.removeEventListener('mousemove', this._throttleOnMove); } }, { key: 'disable', value: function disable() { this.overlay.parent.style.display = 'none'; this.overlay.parent.style.width = 0; this.overlay.parent.style.height = 0; this.target = null; this.root.removeEventListener('mousemove', this._throttleOnMove); } }, { key: 'destroy', value: function destroy() { this.destroyed = true; this.disable(); this.overlay = {}; } }, { key: 'getXPath', value: function getXPath(ele) { if (!isDOM(ele) && !this.target) return exportObj.warn('Target element is not found. Warning function name:%c getXPath', 'color: #ff5151'); if (!ele) ele = this.target; if (ele.hasAttribute('id')) { return '//' + ele.tagName.toLowerCase() + '[@id="' + ele.id + '"]'; } if (ele.hasAttribute('class')) { return '//' + ele.tagName.toLowerCase() + '[@class="' + ele.getAttribute('class') + '"]'; } var path = []; while (ele.nodeType === Node.ELEMENT_NODE) { var currentTag = ele.nodeName.toLowerCase(); var nth = findIndex(ele, currentTag); path.push('' + ele.tagName.toLowerCase() + (nth === 1 ? '' : '[' + nth + ']')); ele = ele.parentNode; } return '/' + path.reverse().join('/'); } }, { key: 'getSelector', value: function getSelector(ele) { if (!isDOM(ele) && !this.target) return exportObj.warn('Target element is not found. Warning function name:%c getCssPath', 'color: #ff5151'); if (!ele) ele = this.target; var path = []; while (ele.nodeType === Node.ELEMENT_NODE) { var currentSelector = ele.nodeName.toLowerCase(); if (ele.hasAttribute('id')) { currentSelector += '#' + ele.id; } else if (ele.hasAttribute('class')) { currentSelector += '.' + ele.className.replace(/\s+/g, ' ').split(' ').join('.'); } else { var nth = findIndex(ele, currentSelector); if (nth !== 1) currentSelector += ':nth-of-type(' + nth + ')'; } path.unshift(currentSelector); ele = ele.parentNode; } return path.join('>'); } }, { key: 'getElementInfo', value: function getElementInfo(ele) { if (!isDOM(ele) && !this.target) return exportObj.warn('Target element is not found. Warning function name:%c getElementInfo', 'color: #ff5151'); return getElementInfo$1(ele || this.target); } }, { key: '_init', value: function _init() { this.overlayId = 'dom-inspector-' + Date.now(); var parent = this._createElement('div', { id: this.overlayId, class: 'dom-inspector ' + this.theme, style: 'z-index: ' + this.maxZIndex }); this.overlay = { parent: parent, content: this._createSurroundEle(parent, 'content'), paddingTop: this._createSurroundEle(parent, 'padding padding-top'), paddingRight: this._createSurroundEle(parent, 'padding padding-right'), paddingBottom: this._createSurroundEle(parent, 'padding padding-bottom'), paddingLeft: this._createSurroundEle(parent, 'padding padding-left'), borderTop: this._createSurroundEle(parent, 'border border-top'), borderRight: this._createSurroundEle(parent, 'border border-right'), borderBottom: this._createSurroundEle(parent, 'border border-bottom'), borderLeft: this._createSurroundEle(parent, 'border border-left'), marginTop: this._createSurroundEle(parent, 'margin margin-top'), marginRight: this._createSurroundEle(parent, 'margin margin-right'), marginBottom: this._createSurroundEle(parent, 'margin margin-bottom'), marginLeft: this._createSurroundEle(parent, 'margin margin-left'), tips: this._createSurroundEle(parent, 'tips', '<div class="tag"></div><div class="id"></div><div class="class"></div><div class="line">&nbsp;|&nbsp;</div><div class="size"></div><div class="triangle"></div>') }; this.root.appendChild(parent); } }, { key: '_createElement', value: function _createElement(tag, attr, content) { var ele = this._doc.createElement(tag); Object.keys(attr).forEach(function (item) { ele.setAttribute(item, attr[item]); }); if (content) ele.innerHTML = content; return ele; } }, { key: '_createSurroundEle', value: function _createSurroundEle(parent, className, content) { var ele = this._createElement('div', { class: className }, content); parent.appendChild(ele); return ele; } }, { key: '_onMove', value: function _onMove(e) { for (var i = 0; i < this.exclude.length; i += 1) { var cur = this.exclude[i]; if (cur.isEqualNode(e.target) || isParent(e.target, cur)) return; } this.target = e.target; if (this.target === this._cachedTarget) return null; this._cachedTarget = this.target; var elementInfo = getElementInfo$1(e.target); var contentLevel = { width: elementInfo.width, height: elementInfo.height }; var paddingLevel = { width: elementInfo['padding-left'] + contentLevel.width + elementInfo['padding-right'], height: elementInfo['padding-top'] + contentLevel.height + elementInfo['padding-bottom'] }; var borderLevel = { width: elementInfo['border-left-width'] + paddingLevel.width + elementInfo['border-right-width'], height: elementInfo['border-top-width'] + paddingLevel.height + elementInfo['border-bottom-width'] }; var marginLevel = { width: elementInfo['margin-left'] + borderLevel.width + elementInfo['margin-right'], height: elementInfo['margin-top'] + borderLevel.height + elementInfo['margin-bottom'] }; // so crazy addRule(this.overlay.parent, { width: marginLevel.width + 'px', height: marginLevel.height + 'px', top: elementInfo.top + 'px', left: elementInfo.left + 'px' }); addRule(this.overlay.content, { width: contentLevel.width + 'px', height: contentLevel.height + 'px', top: elementInfo['margin-top'] + elementInfo['border-top-width'] + elementInfo['padding-top'] + 'px', left: elementInfo['margin-left'] + elementInfo['border-left-width'] + elementInfo['padding-left'] + 'px' }); addRule(this.overlay.paddingTop, { width: paddingLevel.width + 'px', height: elementInfo['padding-top'] + 'px', top: elementInfo['margin-top'] + elementInfo['border-top-width'] + 'px', left: elementInfo['margin-left'] + elementInfo['border-left-width'] + 'px' }); addRule(this.overlay.paddingRight, { width: elementInfo['padding-right'] + 'px', height: paddingLevel.height - elementInfo['padding-top'] + 'px', top: elementInfo['padding-top'] + elementInfo['margin-top'] + elementInfo['border-top-width'] + 'px', right: elementInfo['margin-right'] + elementInfo['border-right-width'] + 'px' }); addRule(this.overlay.paddingBottom, { width: paddingLevel.width - elementInfo['padding-right'] + 'px', height: elementInfo['padding-bottom'] + 'px', bottom: elementInfo['margin-bottom'] + elementInfo['border-bottom-width'] + 'px', right: elementInfo['padding-right'] + elementInfo['margin-right'] + elementInfo['border-right-width'] + 'px' }); addRule(this.overlay.paddingLeft, { width: elementInfo['padding-left'] + 'px', height: paddingLevel.height - elementInfo['padding-top'] - elementInfo['padding-bottom'] + 'px', top: elementInfo['padding-top'] + elementInfo['margin-top'] + elementInfo['border-top-width'] + 'px', left: elementInfo['margin-left'] + elementInfo['border-left-width'] + 'px' }); addRule(this.overlay.borderTop, { width: borderLevel.width + 'px', height: elementInfo['border-top-width'] + 'px', top: elementInfo['margin-top'] + 'px', left: elementInfo['margin-left'] + 'px' }); addRule(this.overlay.borderRight, { width: elementInfo['border-right-width'] + 'px', height: borderLevel.height - elementInfo['border-top-width'] + 'px', top: elementInfo['margin-top'] + elementInfo['border-top-width'] + 'px', right: elementInfo['margin-right'] + 'px' }); addRule(this.overlay.borderBottom, { width: borderLevel.width - elementInfo['border-right-width'] + 'px', height: elementInfo['border-bottom-width'] + 'px', bottom: elementInfo['margin-bottom'] + 'px', right: elementInfo['margin-right'] + elementInfo['border-right-width'] + 'px' }); addRule(this.overlay.borderLeft, { width: elementInfo['border-left-width'] + 'px', height: borderLevel.height - elementInfo['border-top-width'] - elementInfo['border-bottom-width'] + 'px', top: elementInfo['margin-top'] + elementInfo['border-top-width'] + 'px', left: elementInfo['margin-left'] + 'px' }); addRule(this.overlay.marginTop, { width: marginLevel.width + 'px', height: elementInfo['margin-top'] + 'px', top: 0, left: 0 }); addRule(this.overlay.marginRight, { width: elementInfo['margin-right'] + 'px', height: marginLevel.height - elementInfo['margin-top'] + 'px', top: elementInfo['margin-top'] + 'px', right: 0 }); addRule(this.overlay.marginBottom, { width: marginLevel.width - elementInfo['margin-right'] + 'px', height: elementInfo['margin-bottom'] + 'px', bottom: 0, right: elementInfo['margin-right'] + 'px' }); addRule(this.overlay.marginLeft, { width: elementInfo['margin-left'] + 'px', height: marginLevel.height - elementInfo['margin-top'] - elementInfo['margin-bottom'] + 'px', top: elementInfo['margin-top'] + 'px', left: 0 }); $('.tag', this.overlay.tips).innerHTML = this.target.tagName.toLowerCase(); $('.id', this.overlay.tips).innerHTML = this.target.id ? '#' + this.target.id : ''; $('.class', this.overlay.tips).innerHTML = [].concat(toConsumableArray(this.target.classList)).map(function (item) { return '.' + item; }).join(''); $('.size', this.overlay.tips).innerHTML = marginLevel.width + 'x' + marginLevel.height; var tipsTop = 0; if (elementInfo.top >= 24 + 8) { this.overlay.tips.classList.remove('reverse'); tipsTop = elementInfo.top - 24 - 8; } else { this.overlay.tips.classList.add('reverse'); tipsTop = marginLevel.height + elementInfo.top + 8; } addRule(this.overlay.tips, { top: tipsTop + 'px', left: elementInfo.left + 'px', display: 'block' }); } }, { key: '_formatExcludeOption', value: function _formatExcludeOption() { var excludeArray = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; var result = []; excludeArray.forEach(function (item) { if (typeof item === 'string') return result.push($(item)); if (isDOM(item)) return result.push(item); }); return result; } }]); return DomInspector; }(); return DomInspector; })));