├── .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 |
30 | 这是个div
31 | 支持 transform
32 | 这是个transform: scale(0.5, 0.5) 的 div
33 | 支持 list 多选
34 |
35 |
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 | 
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