├── .gitignore ├── dist ├── rollup.config.build.d.ts ├── rollup.config.dev.d.ts ├── demo │ └── index.d.ts ├── src │ ├── utils.d.ts │ ├── index.d.ts │ └── dom.d.ts └── index.js ├── demo ├── index.js └── index.html ├── rollup.config.dev.ts ├── rollup.config.build.ts ├── package.json ├── LICENSE ├── README.md └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | .DS_Store -------------------------------------------------------------------------------- /dist/rollup.config.build.d.ts: -------------------------------------------------------------------------------- 1 | import type { RollupOptions } from 'rollup'; 2 | declare const config: RollupOptions[]; 3 | export default config; 4 | -------------------------------------------------------------------------------- /dist/rollup.config.dev.d.ts: -------------------------------------------------------------------------------- 1 | import type { RollupOptions } from 'rollup'; 2 | declare const config: RollupOptions; 3 | export default config; 4 | -------------------------------------------------------------------------------- /dist/demo/index.d.ts: -------------------------------------------------------------------------------- 1 | import DomInspector from "../src/index"; 2 | declare global { 3 | interface Window { 4 | inspector: DomInspector; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /demo/index.js: -------------------------------------------------------------------------------- 1 | window.inspector = new DomInspector.default({ 2 | maxZIndex: 9999, 3 | onMoveSelect: (target) => { 4 | console.log(target); 5 | }, 6 | onDidSelect: (target) => { 7 | console.log(target); 8 | window.inspector.pause(); 9 | } 10 | }); 11 | -------------------------------------------------------------------------------- /rollup.config.dev.ts: -------------------------------------------------------------------------------- 1 | import type { RollupOptions } from 'rollup'; 2 | import typescript from '@rollup/plugin-typescript'; 3 | import serve from 'rollup-plugin-serve' 4 | import livereload from 'rollup-plugin-livereload'; 5 | import postcss from 'rollup-plugin-postcss'; 6 | 7 | const config: RollupOptions = { 8 | input: 'demo/index.ts', 9 | output: [ 10 | { 11 | dir: 'demo', 12 | format: 'umd', 13 | }, 14 | ], 15 | plugins: [ 16 | typescript(), 17 | serve(), 18 | postcss(), 19 | livereload(), 20 | ], 21 | }; 22 | 23 | export default config; -------------------------------------------------------------------------------- /dist/src/utils.d.ts: -------------------------------------------------------------------------------- 1 | export declare function mixin(target: { 2 | [key: string]: any; 3 | }, source: { 4 | [key: string]: any; 5 | }): { 6 | [key: string]: any; 7 | }; 8 | export declare function throttle(func: (...args: any) => any, wait?: number): (this: any, ...args: any) => void; 9 | export declare function isNull(obj: any): boolean; 10 | export declare const svgTags: string[]; 11 | export declare const ignoreTags: string[]; 12 | export declare const getTagName: (ele: HTMLElement) => string; 13 | export declare function getMax(arrOrObj: { 14 | [key: string | number]: any; 15 | }): number; 16 | export declare function judgeNums({ num, total, min, ratio, }: { 17 | num: number; 18 | total: number; 19 | min?: number; 20 | ratio?: number; 21 | }): boolean; 22 | export declare function touchAction(enable?: boolean): void; 23 | -------------------------------------------------------------------------------- /rollup.config.build.ts: -------------------------------------------------------------------------------- 1 | import type { RollupOptions } from 'rollup'; 2 | import typescript from '@rollup/plugin-typescript'; 3 | import postcss from 'rollup-plugin-postcss'; 4 | import { uglify } from 'rollup-plugin-uglify'; 5 | import cleaner from 'rollup-plugin-cleaner'; 6 | 7 | const config: RollupOptions[] = [ 8 | { 9 | input: 'src/index.ts', 10 | output: [ 11 | { 12 | dir: 'dist', 13 | format: 'umd', 14 | name: 'DomInspector', 15 | }, 16 | ], 17 | plugins: [ 18 | typescript({ 19 | compilerOptions: { 20 | declaration: true, 21 | outDir: 'dist', 22 | } 23 | }), 24 | postcss(), 25 | uglify(), 26 | cleaner({ 27 | targets: [ 28 | './dist/' 29 | ] 30 | }) 31 | ], 32 | }, 33 | { 34 | input: 'demo/index.ts', 35 | output: [ 36 | { 37 | dir: 'demo', 38 | format: 'umd', 39 | }, 40 | ], 41 | plugins: [ 42 | typescript(), 43 | postcss(), 44 | ], 45 | } 46 | ]; 47 | 48 | export default config; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dom-inspector-pro", 3 | "version": "1.0.3", 4 | "description": "", 5 | "main": "dist/index.js", 6 | "types": "dist/src/index.d.ts", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1", 9 | "dev": "rollup --config rollup.config.dev.ts --configPlugin typescript -w", 10 | "build": "rollup --config rollup.config.build.ts --configPlugin typescript" 11 | }, 12 | "files": [ 13 | "dist/*" 14 | ], 15 | "keywords": [], 16 | "author": "", 17 | "license": "ISC", 18 | "devDependencies": { 19 | "@rollup/plugin-babel": "^6.0.3", 20 | "@rollup/plugin-html": "^1.0.1", 21 | "@rollup/plugin-typescript": "^10.0.1", 22 | "postcss": "^8.4.20", 23 | "rollup": "^3.7.3", 24 | "rollup-plugin-browsersync": "^1.3.3", 25 | "rollup-plugin-cleaner": "^1.0.0", 26 | "rollup-plugin-dev": "^2.0.4", 27 | "rollup-plugin-livereload": "^2.0.5", 28 | "rollup-plugin-postcss": "^4.0.2", 29 | "rollup-plugin-serve": "^2.0.2", 30 | "rollup-plugin-uglify": "^6.0.4", 31 | "tslib": "^2.4.1", 32 | "typescript": "^4.9.4" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 xiaoboma 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /dist/src/index.d.ts: -------------------------------------------------------------------------------- 1 | import './style.css'; 2 | type Target = HTMLElement | EventTarget | '' | null; 3 | declare class DomInspector { 4 | theme: string; 5 | maxZIndex: number; 6 | mode: 'single' | 'multi'; 7 | env: 'pc' | 'mobile'; 8 | xpath: string | null; 9 | target: Target; 10 | status: 'enable' | 'disable' | 'pause'; 11 | private onMoveSelect; 12 | private onDidSelect; 13 | private assistEle; 14 | private _cachedTarget; 15 | private event; 16 | private overlay; 17 | private _throttleOnMove; 18 | constructor(options?: { 19 | theme?: string; 20 | maxZIndex?: number; 21 | mode?: 'single' | 'multi'; 22 | env?: 'pc' | 'mobile'; 23 | onMoveSelect?: (target: Target) => void; 24 | onDidSelect?: (target: Target) => void; 25 | }); 26 | private _addBodyClick; 27 | enable(mode?: 'single' | 'multi'): null; 28 | pause(): void; 29 | disable(): void; 30 | selectTarget(ele: HTMLElement): void; 31 | selectTargets(eles: HTMLElement[]): void; 32 | private _remove; 33 | private _onMove; 34 | private _onMoveEnd; 35 | } 36 | export default DomInspector; 37 | export * from './dom'; 38 | -------------------------------------------------------------------------------- /dist/src/dom.d.ts: -------------------------------------------------------------------------------- 1 | export declare function isDOM(obj: any): any; 2 | export declare function $(selector: string, parent?: HTMLElement): Element | null; 3 | export declare function addRule(selector: HTMLElement, cssObj: { 4 | [key: string]: any; 5 | }): void; 6 | export declare function findIndex(ele: Element | null, currentTag: string): number; 7 | export declare function getElementInfo(ele: HTMLElement): { 8 | [key: string]: any; 9 | }; 10 | export declare function getMaxZIndex(): number; 11 | export declare function createElement(tag: string, attr: { 12 | [key: string]: any; 13 | }, content?: string): HTMLElement; 14 | export declare function createSurroundEle(parent: HTMLElement, className?: string, content?: string): HTMLElement; 15 | export declare function addOverlay({ target, root, id, assistEle, theme, maxZIndex, }: { 16 | target: HTMLElement; 17 | root: HTMLElement; 18 | id?: string; 19 | assistEle: HTMLElement; 20 | theme?: string; 21 | maxZIndex?: number; 22 | }): (() => void) | null; 23 | export declare function detectList(ele: Element | null): false | { 24 | listEl: Element | ParentNode; 25 | ele: Element; 26 | preLevel: number; 27 | }; 28 | export declare function getTouchMouseTargetElement(e: TouchEvent | MouseEvent): EventTarget | null; 29 | export declare function getXpath(ele: HTMLElement, allId?: boolean): string | null; 30 | export declare function getXpathList(ele: HTMLElement, preLevel?: number): string | null; 31 | export declare function getEleByXpath(xpath: string): Node | null; 32 | export declare function getElesByXpath(xpath: string, max?: number): Node[]; 33 | export declare function getNearestNoSvgXpath(xpath: string): string; 34 | -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | test 7 | 8 | 9 | 10 | 23 | 24 | 25 | 26 |
支持 svg
27 | 28 | 29 | 30 |
这是个div
31 |
支持 transform
32 |
这是个transform: scale(0.5, 0.5) 的 div
33 |
支持 list 多选
34 |
35 |
36 |

list

37 |

name

38 |
39 |
40 |

title

41 |

name

42 |
43 |
44 |

title

45 |

name

46 |
47 |
48 |

title

49 |

name

50 |
51 |
52 |

title

53 |

name

54 |
55 |
56 |

title

57 |

name

58 |
59 |
60 |

title

61 |

name

62 |
63 |
64 |

title

65 |

name

66 |
67 |
68 |

title

69 |

name

70 |
71 |
72 |

title

73 |

name

74 |
75 |
76 |

title

77 |

name

78 |
79 |
80 |

title

81 |

name

82 |
83 |
84 |

title

85 |

name

86 |
87 |
88 | 89 |
90 | 91 | 92 |
93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 告所有人 2 | 3 | 很多原因,删掉了 src 下的文件, npm 包可以正常使用,但是源代码不再提供。仅提供压缩后的源码 4 | 5 | ## 原 readme 6 | 7 | dom-inspector-pro 8 | -------------------------------- 9 | 10 | Dom inspector like chrome dev tools。 11 | 12 | 可视化埋点的基础能力---元素圈选器 13 | 14 | 一部分代码参考自 [dom-inspector](https://github.com/luoye-fe/dom-inspector#readme), 但是对很多功能均进行了增强: 15 | 16 | 1. 增加了例如 onMoveSelect, onDidSelect 这样的回调函数,从而可以有更多的时机来完成元素圈选的逻辑相关逻辑 17 | 2. 增加了对 svg ,对 transform: scale 后的元素的支持 18 | 3. 增加了 selectTarget, selectTargets 这样的 api, 来给使用者自行完成元素圈选 19 | 4. 增加了 移动端选择的 能力 20 | 5. 增加了 列表元素的 判断及选择能力 21 | 22 | 故称为 dom-inspector-pro. 23 | 24 | 使用 demo 可以查看这个[例子](https://blog.maxiaobo.com.cn/dom-inspector-pro/demo/index.html)。演示了 多选、svg 元素、transform 等边界场景的支持。 25 | 26 | 移动端也可直接扫码体验: 27 | 28 | ![img](https://tva1.sinaimg.cn/large/008vxvgGly1h95hsdv1l9j307f07ejrs.jpg) 29 | 30 | ## 使用方法 31 | 32 | ### 安装 `dom-inspector-pro` 33 | 34 | ```bash 35 | npm install dom-inspector-pro --save 36 | ``` 37 | 38 | script 使用 39 | 40 | ```html 41 | 42 | ``` 43 | 44 | npm 包使用 45 | 46 | ```js 47 | const DomInspector = require('dom-inspector-pro'); 48 | ``` 49 | 50 | ```js 51 | import DomInspector from 'dom-inspector-pro'; 52 | ``` 53 | 54 | ### 实例化 55 | 56 | ```js 57 | const inspector = new DomInspector(); 58 | ``` 59 | 60 | ### 实例化选项 61 | 62 | ```js 63 | const inspector = new DomInspector({ 64 | theme: '', // 可选,主题,不建议更改,内部主题色参考 chrome 已经设置好 65 | onMoveSelect: (target) => { 66 | // 用户在移动过程中选择元素的回调 67 | console.log(target); 68 | }, // 可选 69 | onDidSelect: (target) => { 70 | // 用户在移动过程中,选择了元素,并进行了点击,此时会触发该回调 71 | // 实际的使用场景中,可以用来进行关闭 or 暂停选择器等操作 72 | console.log(target); 73 | inspector.pause(); 74 | }, // 可选 75 | maxZIndex: 9999, // 可选,max z index, if blank, will auto get document.all max z index 76 | env: 'pc' | 'mobile', // 可选, 如果不填,则内部会根据浏览器 ua 进行移动端 or pc 端的判断 77 | mode: 'single' | 'multi' // 可选, 开启多选 or 单选,如果是多选模式,则会在选中元素时,检测该元素是否处于列表中,如果是,则会全选多个元素 78 | }); 79 | ``` 80 | 81 | ### 启动、暂停与关闭 82 | 83 | ```js 84 | const inspector = new DomInspector(); 85 | 86 | inspector.enable(); 87 | 88 | inspector.disable(); 89 | 90 | inspector.pause(); 91 | ``` 92 | 93 | ### 内部属性及方法 94 | 95 | ```ts 96 | class DomInspector { 97 | theme: string; 98 | maxZIndex: number; 99 | mode: 'single' | 'multi'; 100 | env: 'pc' | 'mobile'; 101 | xpath: string | null; 102 | target: Target; 103 | status: 'enable' | 'disable' | 'pause'; 104 | constructor(options: { 105 | theme?: string; 106 | maxZIndex?: number; 107 | mode?: 'single' | 'multi'; 108 | env?: 'pc' | 'mobile'; 109 | onMoveSelect?: (target: Target) => void; 110 | onDidSelect?: (target: Target) => void; 111 | }); 112 | enable(mode?: 'single' | 'multi'): null; 113 | pause(): void; 114 | disable(): void; 115 | selectTarget(ele: HTMLElement): void; 116 | selectTargets(eles: HTMLElement[]): void; 117 | private _addBodyClick; 118 | private onMoveSelect; 119 | private onDidSelect; 120 | private assistEle; 121 | private _cachedTarget; 122 | private event; 123 | private overlay; 124 | private _throttleOnMove; 125 | private _remove; 126 | private _onMove; 127 | private _onMoveEnd; 128 | } 129 | ``` 130 | 131 | 实际使用中,对于期望在用户移动期间执行的 回调,可以通过 **onMoveSelect** 传入,对于期望用户在选中后并点击后执行的逻辑,可以通过 **onDidSelect** 传入,在可视化圈选中,一般在这个回调中,关闭圈选器,并唤起表单等供用户录入信息。 132 | 133 | 而如果想要直接选择元素而非借助用户移动鼠标,可以调用 **selectTarget** 及 **selectTargets** 方法去一次行选择单个 or 多个元素。 134 | 135 | 这几个 api 也是 dom-inspector 中被要求追加的功能,在可视化埋点中也是不可获取的功能,故 本工程实现了这些方法。 136 | 137 | ### 其他工具方法 138 | 139 | 在可视化埋点中,还有一些必备的工具方法,比如: 获取元素的 xpath,检测列表元素等等,本项目也对这些工具方法进行了抛出,可自行查看 ts 定义选择使用 140 | 141 | ### iframe 中使用 142 | 143 | 本库并不能直接在 iframe 中使用,不过可以作为一个环节来在 iframe 中使用,方案如下: 144 | 145 | iframe 中的页面 接入 inspector 脚本,同时注入一段 iframe 通信的脚本。 146 | 147 | 在主页面中,通过发送 指令,操控 inspector 来完成圈选, inspector 圈选结束后,向主页面发送消息,在主页面对 target 进行操控,从而完成 iframe 中使用的流程,具体可以参考文章 [可视化埋点(一):从0到1搭建可视化埋点平台](https://blog.maxiaobo.com.cn/2022/09/03/%E5%8F%AF%E8%A7%86%E5%8C%96%E5%9F%8B%E7%82%B9%EF%BC%88%E4%B8%80%EF%BC%89%EF%BC%9A%E4%BB%8E0%E5%88%B01%E6%90%AD%E5%BB%BA%E5%8F%AF%E8%A7%86%E5%8C%96%E5%9F%8B%E7%82%B9%E5%B9%B3%E5%8F%B0/) 148 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */ 4 | 5 | /* Projects */ 6 | // "incremental": true, /* Enable incremental compilation */ 7 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ 8 | // "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */ 9 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */ 10 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ 11 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ 12 | 13 | /* Language and Environment */ 14 | "target": "es2015", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ 15 | // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ 16 | // "jsx": "preserve", /* Specify what JSX code is generated. */ 17 | // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ 18 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ 19 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */ 20 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ 21 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */ 22 | // "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */ 23 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ 24 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ 25 | 26 | /* Modules */ 27 | "module": "esnext", /* Specify what module code is generated. */ 28 | // "rootDir": "./", /* Specify the root folder within your source files. */ 29 | "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ 30 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ 31 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ 32 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ 33 | // "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */ 34 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */ 35 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 36 | // "resolveJsonModule": true, /* Enable importing .json files */ 37 | // "noResolve": true, /* Disallow `import`s, `require`s or ``s from expanding the number of files TypeScript should add to a project. */ 38 | 39 | /* JavaScript Support */ 40 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */ 41 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ 42 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */ 43 | 44 | /* Emit */ 45 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ 46 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */ 47 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ 48 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ 49 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */ 50 | // "outDir": "./dist", /* Specify an output folder for all emitted files. */ 51 | // "removeComments": true, /* Disable emitting comments. */ 52 | // "noEmit": true, /* Disable emitting files from a compilation. */ 53 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ 54 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */ 55 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ 56 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ 57 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 58 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ 59 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ 60 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ 61 | // "newLine": "crlf", /* Set the newline character for emitting files. */ 62 | // "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */ 63 | // "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */ 64 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ 65 | // "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */ 66 | // "declarationDir": "./dist", /* Specify the output directory for generated declaration files. */ 67 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ 68 | 69 | /* Interop Constraints */ 70 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ 71 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ 72 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */ 73 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ 74 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ 75 | 76 | /* Type Checking */ 77 | "strict": true, /* Enable all strict type-checking options. */ 78 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */ 79 | // "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */ 80 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ 81 | // "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */ 82 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ 83 | // "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */ 84 | // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ 85 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ 86 | // "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */ 87 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */ 88 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ 89 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ 90 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ 91 | // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ 92 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ 93 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */ 94 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ 95 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ 96 | 97 | /* Completeness */ 98 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ 99 | "skipLibCheck": true /* Skip type checking all .d.ts files. */ 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /dist/index.js: -------------------------------------------------------------------------------- 1 | !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).DomInspector={})}(this,function(t){"use strict";var e,i,n,o;function r(t,e){const i=t;Object.keys(e).forEach(t=>{!{}.hasOwnProperty.call(e,t)||(i[t]=e[t])}),i}e=".dom-inspector {\n position: fixed;\n pointer-events: none;\n transform-origin: 0 0;\n}\n\n.dom-inspector > div {\n position: absolute;\n pointer-events: none;\n}\n\n.dom-inspector-wrapper .tips {\n max-width: 70%;\n background-color: #333740;\n font-size: 0;\n line-height: 18px;\n padding: 3px 10px;\n position: fixed;\n border-radius: 4px;\n display: none;\n pointer-events: none;\n}\n\n.dom-inspector-wrapper .tips.reverse {\n}\n\n.dom-inspector-wrapper .tips .triangle {\n width: 0;\n height: 0;\n position: absolute;\n border-top: 8px solid #333740;\n border-right: 8px solid transparent;\n border-bottom: 8px solid transparent;\n border-left: 8px solid transparent;\n left: 10px;\n bottom: -16px;\n}\n\n.dom-inspector-wrapper .tips.reverse .triangle {\n border-top: 8px solid transparent;\n border-right: 8px solid transparent;\n border-bottom: 8px solid #333740;\n border-left: 8px solid transparent;\n left: 10px;\n top: -16px;\n}\n\n.dom-inspector-wrapper .tips.reverse-r .triangle {\n left: auto;\n right: 10px;\n}\n\n.dom-inspector-wrapper .tips > div {\n display: inline-block;\n vertical-align: middle;\n font-size: 12px;\n font-family: Consolas, Menlo, Monaco, Courier, monospace;\n overflow: auto;\n}\n\n.dom-inspector-wrapper .tips .tag {\n color: #e776e0;\n}\n\n.dom-inspector-wrapper .tips .id {\n color: #eba062;\n}\n\n.dom-inspector-wrapper .tips .class {\n color: #8dd2fb;\n}\n\n.dom-inspector-wrapper .tips .line {\n color: #fff;\n}\n\n.dom-inspector-wrapper .tips .size {\n color: #fff;\n}\n\n.dom-inspector-theme-default .margin {\n background-color: rgb(246, 193, 139, 0.75);\n}\n\n.dom-inspector-theme-default .border {\n background-color: rgba(250, 215, 138, 0.75);\n}\n\n.dom-inspector-theme-default .padding {\n background-color: rgba(182, 200, 120, 0.75);\n}\n\n.dom-inspector-theme-default .content {\n background-color: rgba(81, 101, 255, 0.75);\n}\n",i=(i=void 0===i?{}:i).insertAt,e&&"undefined"!=typeof document&&(n=document.head||document.getElementsByTagName("head")[0],(o=document.createElement("style")).type="text/css","top"===i&&n.firstChild?n.insertBefore(o,n.firstChild):n.appendChild(o),o.styleSheet?o.styleSheet.cssText=e:o.appendChild(document.createTextNode(e)));const p=["svg","path","g","image","text","line","rect","polygon","circle","ellipse"],l=["html","body",...p],h=t=>{t=t.tagName.toLowerCase();return-1!==p.indexOf(t)?`*[name()='${t}']`:t};function f(e){let i=e,n=(e instanceof Object&&(i=Object.keys(e).map(t=>e[t])),0);for(let t=0;tn&&(n=i[t]);return n}function b({num:t,total:e,min:i=5,ratio:n=.7}){return!(t=n}const d=document.createElement("style");function a(t=!1){d.innerHTML=t?"":` 2 | * { 3 | touch-action: none; 4 | }`}function g(t){return t&&"object"==typeof t&&1===t.nodeType&&"object"==typeof t.style&&"object"==typeof t.ownerDocument}function w(t,e){return(void 0!==e&&g(e)?e:document).querySelector(t)}function y(e,i){Object.keys(i).forEach(t=>{e.style.setProperty(t,i[t])})}function c(t,e){let i=0;for(;t;)t.nodeName.toLowerCase()===e&&(i+=1),t=t.previousElementSibling;return i}function E(t){var e=t.getBoundingClientRect();return{scalex:Number((void 0===t.offsetWidth?1:t.offsetWidth/e.width).toFixed(1)),scaley:Number((void 0===t.offsetHeight?1:t.offsetHeight/e.height).toFixed(1))}}function L(t){const e={};const i=getComputedStyle(t);["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"].forEach(t=>{e[t]=parseFloat(i.getPropertyValue(t))||0});var n=t.getBoundingClientRect(),o=void 0===t.offsetWidth?n.width:t.offsetWidth-e["border-left-width"]-e["border-right-width"]-e["padding-left"]-e["padding-right"],n=void 0===t.offsetHeight?n.height:t.offsetHeight-e["border-top-width"]-e["border-bottom-width"]-e["padding-top"]-e["padding-bottom"];return r(e,{width:o,height:n}),r(e,function(t){var e=window.getComputedStyle(t),i=t.getBoundingClientRect(),{scalex:t,scaley:n}=E(t),o=i.left*t-parseFloat(e.getPropertyValue("margin-left"));return{top:i.top*n-parseFloat(e.getPropertyValue("margin-top")),left:o,right:i.right*t+parseFloat(e.getPropertyValue("margin-right")),scalex:t,scaley:n}}(t)),e}function s(){return[...document.querySelectorAll("*")].reduce((t,e)=>Math.max(t,+window.getComputedStyle(e).zIndex||0),0)}function M(t,e,i){const n=window.document.createElement(t);return Object.keys(e).forEach(t=>{n.setAttribute(t,e[t])}),i&&(n.innerHTML=i),n}function T(t,e,i){e=M("div",{class:e},i);return t.appendChild(e),e}function m({target:t,root:e,id:i="dom-inspector",assistEle:n,theme:o="dom-inspector-theme-default",maxZIndex:r=9999}){if(!t)return null;var d=M("div",{style:"z-index: "+r,class:"dom-inspector-wrapper"});const a=M("div",{id:i,class:"dom-inspector "+o,style:"z-index: "+r});d.appendChild(a);var i={parent:a,content:T(a,"content"),paddingTop:T(a,"padding padding-top"),paddingRight:T(a,"padding padding-right"),paddingBottom:T(a,"padding padding-bottom"),paddingLeft:T(a,"padding padding-left"),borderTop:T(a,"border border-top"),borderRight:T(a,"border border-right"),borderBottom:T(a,"border border-bottom"),borderLeft:T(a,"border border-left"),marginTop:T(a,"margin margin-top"),marginRight:T(a,"margin margin-right"),marginBottom:T(a,"margin margin-bottom"),marginLeft:T(a,"margin margin-left"),tips:T(d,"tips",'
 | 
')},{scalex:o,scaley:n}=E(n),{scalex:s,scaley:p}=E(t),l=L(t);const h=l.width,g=l.height,c=l["padding-left"]+h+l["padding-right"],m=l["padding-top"]+g+l["padding-bottom"],u=l["border-left-width"]+c+l["border-right-width"],f=l["border-top-width"]+m+l["border-bottom-width"],b=l["margin-left"]+u+l["margin-right"],v=l["margin-top"]+f+l["margin-bottom"];y(i.parent,{width:b+"px",height:v+"px",top:l.top/p*n+"px",left:l.left/s*o+"px"}),s===o&&p===n||y(i.parent,{transform:`scale(${o/s}, ${n/p})`}),y(i.content,{width:h+"px",height:g+"px",top:l["margin-top"]+l["border-top-width"]+l["padding-top"]+"px",left:l["margin-left"]+l["border-left-width"]+l["padding-left"]+"px"}),y(i.paddingTop,{width:c+"px",height:l["padding-top"]+"px",top:l["margin-top"]+l["border-top-width"]+"px",left:l["margin-left"]+l["border-left-width"]+"px"}),y(i.paddingRight,{width:l["padding-right"]+"px",height:m-l["padding-top"]+"px",top:l["padding-top"]+l["margin-top"]+l["border-top-width"]+"px",right:l["margin-right"]+l["border-right-width"]+"px"}),y(i.paddingBottom,{width:c-l["padding-right"]+"px",height:l["padding-bottom"]+"px",bottom:l["margin-bottom"]+l["border-bottom-width"]+"px",right:l["padding-right"]+l["margin-right"]+l["border-right-width"]+"px"}),y(i.paddingLeft,{width:l["padding-left"]+"px",height:m-l["padding-top"]-l["padding-bottom"]+"px",top:l["padding-top"]+l["margin-top"]+l["border-top-width"]+"px",left:l["margin-left"]+l["border-left-width"]+"px"}),y(i.borderTop,{width:u+"px",height:l["border-top-width"]+"px",top:l["margin-top"]+"px",left:l["margin-left"]+"px"}),y(i.borderRight,{width:l["border-right-width"]+"px",height:f-l["border-top-width"]+"px",top:l["margin-top"]+l["border-top-width"]+"px",right:l["margin-right"]+"px"}),y(i.borderBottom,{width:u-l["border-right-width"]+"px",height:l["border-bottom-width"]+"px",bottom:l["margin-bottom"]+"px",right:l["margin-right"]+l["border-right-width"]+"px"}),y(i.borderLeft,{width:l["border-left-width"]+"px",height:f-l["border-top-width"]-l["border-bottom-width"]+"px",top:l["margin-top"]+l["border-top-width"]+"px",left:l["margin-left"]+"px"}),y(i.marginTop,{width:b+"px",height:l["margin-top"]+"px",top:0,left:0}),y(i.marginRight,{width:l["margin-right"]+"px",height:v-l["margin-top"]+"px",top:l["margin-top"]+"px",right:0}),y(i.marginBottom,{width:b-l["margin-right"]+"px",height:l["margin-bottom"]+"px",bottom:0,right:l["margin-right"]+"px"}),y(i.marginLeft,{width:l["margin-left"]+"px",height:v-l["margin-top"]-l["margin-bottom"]+"px",top:l["margin-top"]+"px",left:0}),w(".tag",i.tips).innerHTML=t.tagName.toLowerCase(),w(".id",i.tips).innerHTML=t.id?"#"+t.id:"",w(".size",i.tips).innerHTML=b/s+"x"+v/p;let x=0;t=["top","left"],x=32<=l.top/p?(i.tips.classList.remove("reverse"),(l.top/p-24-8)*n):(i.tips.classList.add("reverse"),((v+l.top)/p+8)*n),t[0]="top",p={left:"auto",right:"auto"};return l.left/s*o>.7*document.body.clientWidth?(i.tips.classList.add("reverse-r"),t[1]="right",p.right=document.body.clientWidth-l.right/s*o+"px"):(i.tips.classList.remove("reverse-r"),p.left=l.left/s*o+"px",t[1]="left"),y(i.tips,Object.assign(Object.assign({top:x+"px"},p),{display:"block","z-index":r})),1===Number(o)&&1===Number(n)||y(i.tips,{transform:`scale(${o}, ${n})`,"transform-origin":t[0]+" "+t[1]}),e.appendChild(d),()=>{a.parentNode.removeChild(a)}}function u(i){if(i){let t=i,e=0;for(;t&&t.nodeType===Node.ELEMENT_NODE&&t!==document.body;){var n=t.parentNode.children,o={},r={};for(let t=0;t{},this.onDidSelect=()=>{},this.assistEle=M("div",{style:`pointer-events: none; 5 | visibility: hidden; 6 | width: 100px; 7 | height: 100px; 8 | position: absolute; 9 | top: -100px;`}),this._cachedTarget="",this.event="mousemove",this.overlay=M("div",{id:"dom-inspector-root",style:`z-index: ${this.maxZIndex};`}),void 0!==t.theme&&(this.theme=t.theme),void 0!==t.maxZIndex&&(this.maxZIndex=t.maxZIndex),void 0!==t.mode&&(this.mode=t.mode),void 0!==t.env&&(this.env=t.env),this.event="pc"===this.env?"mousemove":"touchmove",void 0!==t.onMoveSelect&&(this.onMoveSelect=t.onMoveSelect),void 0!==t.onDidSelect&&(this.onDidSelect=t.onDidSelect),this._throttleOnMove=function(n,o){let r,d,a=Date.now();return function(...t){const e=this;function i(){a=Date.now(),r=null,n.apply(e,t)}"number"==typeof r&&window.clearTimeout(r),(d=Date.now()-a)>o?i():r=window.setTimeout(i,o-d)}}(this._onMove.bind(this),100),this._onMoveEnd=this._onMoveEnd.bind(this),S&&S.appendChild(this.overlay),S&&S.appendChild(this.assistEle),this._addBodyClick()}_addBodyClick(){document.body.addEventListener("click",t=>{"enable"===this.status&&(t.preventDefault(),t.stopPropagation(),this.onDidSelect(this.target))},!0)}enable(t="single"){return this.status="enable",this.xpath=null,this.target="",this.mode=t,this.overlay.style.display="block",O&&O.addEventListener(this.event,this._throttleOnMove,{capture:!0,passive:"mobile"!==this.env}),"mobile"===this.env&&(O&&O.addEventListener("touchend",this._onMoveEnd,{capture:!0,passive:!1}),a(!1)),null}pause(){this.status="pause",O&&O.removeEventListener(this.event,this._throttleOnMove,!0),"mobile"===this.env&&(O&&O.removeEventListener("touchend",this._onMoveEnd,{capture:!0}),a(!0))}disable(){this.status="disable",this.xpath=null,this.target="",this.overlay.style.display="none",this.overlay.style.width="0",this.overlay.style.height="0",O&&O.removeEventListener(this.event,this._throttleOnMove,!0),"mobile"===this.env&&(O&&O.removeEventListener("touchend",this._onMoveEnd,{capture:!0}),a(!0))}selectTarget(t){t&&(this._remove(),m({target:t,root:this.overlay,assistEle:this.assistEle}))}selectTargets(e){if(e&&e.length){this._remove();var i=e.length;for(let t=0;te.length-5?e.slice(-9):e.slice(n-4,n+5));for(let t=0;t{i.dispatchEvent(e)},500)}},t.detectList=u,t.findIndex=c,t.getEleByXpath=function(t){var e=document;return e.evaluate(t,e).iterateNext()},t.getElementInfo=L,t.getElesByXpath=C,t.getMaxZIndex=s,t.getNearestNoSvgXpath=function(t){if(-1===t.indexOf("*[name()="))return t;var e=t.split("/"),i=[];for(let t=0;t