1- /* Select elements using xpath, IE is not supported.
1+ /* Select elements using xpath or get element xpath, IE is not supported.
2+ *
3+ * Select elements using xpath.
24 *
35 * |Name |Desc |
46 * |------|---------------|
57 * |xpath |Xpath |
68 * |return|Target elements|
9+ *
10+ * Get element xpath.
11+ *
12+ * |Name |Desc |
13+ * |---------|------------------------------------|
14+ * |el |Element |
15+ * |optimized|Optimize the xpath, default is false|
716 */
817
918/* example
10- * xpath('//html/body'); // -> [body]
19+ * xpath('/html/body'); // -> [body]
20+ * xpath(document.body); // -> /html/body
1121 */
1222
1323/* module
1727
1828/* typescript
1929 * export declare function xpath(xpath: string): HTMLElement[];
30+ * export declare function xpath(node: ChildNode, optimized?: boolean): string;
2031 */
2132
22- exports = function ( xpath ) {
33+ _ ( 'isStr Class' ) ;
34+
35+ exports = function ( xpath , optimized ) {
36+ if ( isStr ( xpath ) ) {
37+ return findEl ( xpath ) ;
38+ } else {
39+ return getXpath ( xpath , optimized ) ;
40+ }
41+ } ;
42+
43+ function findEl ( xpath ) {
2344 const ret = [ ] ;
2445
2546 const nodesSnapshot = document . evaluate (
@@ -34,4 +55,137 @@ exports = function(xpath) {
3455 }
3556
3657 return ret ;
37- } ;
58+ }
59+
60+ // https://github.com/ChromeDevTools/devtools-frontend/blob/main/front_end/panels/elements/DOMPath.ts
61+ function getXpath ( node , optimized = false ) {
62+ if ( node . nodeType === Node . DOCUMENT_NODE ) {
63+ return '/' ;
64+ }
65+
66+ const steps = [ ] ;
67+ let contextNode = node ;
68+ while ( contextNode ) {
69+ const step = xPathValue ( contextNode , optimized ) ;
70+ if ( ! step ) {
71+ break ;
72+ }
73+ steps . push ( step ) ;
74+ if ( step . optimized ) {
75+ break ;
76+ }
77+ contextNode = contextNode . parentNode ;
78+ }
79+
80+ steps . reverse ( ) ;
81+ return ( steps . length && steps [ 0 ] . optimized ? '' : '/' ) + steps . join ( '/' ) ;
82+ }
83+
84+ function xPathValue ( node , optimized ) {
85+ let ownValue ;
86+ const ownIndex = xPathIndex ( node ) ;
87+ if ( ownIndex === - 1 ) {
88+ return null ;
89+ }
90+
91+ switch ( node . nodeType ) {
92+ case Node . ELEMENT_NODE :
93+ if ( optimized && node . getAttribute ( 'id' ) ) {
94+ return new Step (
95+ '//*[@id="' + node . getAttribute ( 'id' ) + '"]' ,
96+ true
97+ ) ;
98+ }
99+ ownValue = node . localName ;
100+ break ;
101+ case Node . ATTRIBUTE_NODE :
102+ ownValue = '@' + node . nodeName ( ) ;
103+ break ;
104+ case Node . TEXT_NODE :
105+ case Node . CDATA_SECTION_NODE :
106+ ownValue = 'text()' ;
107+ break ;
108+ case Node . PROCESSING_INSTRUCTION_NODE :
109+ ownValue = 'processing-instruction()' ;
110+ break ;
111+ case Node . COMMENT_NODE :
112+ ownValue = 'comment()' ;
113+ break ;
114+ case Node . DOCUMENT_NODE :
115+ ownValue = '' ;
116+ break ;
117+ default :
118+ ownValue = '' ;
119+ break ;
120+ }
121+
122+ if ( ownIndex > 0 ) {
123+ ownValue += '[' + ownIndex + ']' ;
124+ }
125+
126+ return new Step ( ownValue , node . nodeType === Node . DOCUMENT_NODE ) ;
127+ }
128+
129+ function xPathIndex ( node ) {
130+ function areNodesSimilar ( left , right ) {
131+ if ( left === right ) {
132+ return true ;
133+ }
134+
135+ if (
136+ left . nodeType === Node . ELEMENT_NODE &&
137+ right . nodeType === Node . ELEMENT_NODE
138+ ) {
139+ return left . localName === right . localName ;
140+ }
141+
142+ if ( left . nodeType === right . nodeType ) {
143+ return true ;
144+ }
145+
146+ const leftType =
147+ left . nodeType === Node . CDATA_SECTION_NODE
148+ ? Node . TEXT_NODE
149+ : left . nodeType ;
150+ const rightType =
151+ right . nodeType === Node . CDATA_SECTION_NODE
152+ ? Node . TEXT_NODE
153+ : right . nodeType ;
154+ return leftType === rightType ;
155+ }
156+
157+ const siblings = node . parentNode ? node . parentNode . children : null ;
158+ if ( ! siblings ) {
159+ return 0 ;
160+ }
161+ let hasSameNamedElements ;
162+ for ( let i = 0 ; i < siblings . length ; ++ i ) {
163+ if ( areNodesSimilar ( node , siblings [ i ] ) && siblings [ i ] !== node ) {
164+ hasSameNamedElements = true ;
165+ break ;
166+ }
167+ }
168+ if ( ! hasSameNamedElements ) {
169+ return 0 ;
170+ }
171+ let ownIndex = 1 ;
172+ for ( let i = 0 ; i < siblings . length ; ++ i ) {
173+ if ( areNodesSimilar ( node , siblings [ i ] ) ) {
174+ if ( siblings [ i ] === node ) {
175+ return ownIndex ;
176+ }
177+ ++ ownIndex ;
178+ }
179+ }
180+ return - 1 ;
181+ }
182+
183+ const Step = Class ( {
184+ initialize ( value , optimized ) {
185+ this . value = value ;
186+ this . optimized = optimized || false ;
187+ } ,
188+ toString ( ) {
189+ return this . value ;
190+ }
191+ } ) ;
0 commit comments