Skip to content

Commit 391bb00

Browse files
committed
release: v1.48.0
1 parent f3e6030 commit 391bb00

File tree

8 files changed

+205
-13
lines changed

8 files changed

+205
-13
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## v1.48.0 (27 Mar 2025)
2+
3+
* feat(xpath): get element xpath
4+
15
## v1.47.0 (11 Mar 2025)
26

37
* feat: add dataView

DOC.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14672,24 +14672,35 @@ wx.getStorage('test').then(res => {
1467214672

1467314673
## xpath
1467414674

14675-
Select elements using xpath, IE is not supported.
14675+
Select elements using xpath or get element xpath, IE is not supported.
1467614676

1467714677
<details>
1467814678
<summary>Type Definition</summary>
1467914679

1468014680
```typescript
1468114681
function xpath(xpath: string): HTMLElement[];
14682+
function xpath(node: ChildNode, optimized?: boolean): string;
1468214683
```
1468314684

1468414685
</details>
1468514686

14687+
Select elements using xpath.
14688+
1468614689
|Name |Desc |
1468714690
|------|---------------|
1468814691
|xpath |Xpath |
1468914692
|return|Target elements|
1469014693

14694+
Get element xpath.
14695+
14696+
|Name |Desc |
14697+
|---------|------------------------------------|
14698+
|el |Element |
14699+
|optimized|Optimize the xpath, default is false|
14700+
1469114701
```javascript
14692-
xpath('//html/body'); // -> [body]
14702+
xpath('/html/body'); // -> [body]
14703+
xpath(document.body); // -> /html/body
1469314704
```
1469414705

1469514706
## zip

DOC_CN.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14661,24 +14661,35 @@ wx.getStorage('test').then(res => {
1466114661

1466214662
## xpath
1466314663

14664-
使用 xpath 选择元素,不支持 IE。
14664+
使用 xpath 选择元素或获取元素 xpath,不支持 IE。
1466514665

1466614666
<details>
1466714667
<summary>类型定义</summary>
1466814668

1466914669
```typescript
1467014670
function xpath(xpath: string): HTMLElement[];
14671+
function xpath(node: ChildNode, optimized?: boolean): string;
1467114672
```
1467214673

1467314674
</details>
1467414675

14676+
使用 xpath 选择元素。
14677+
1467514678
|参数名|说明|
1467614679
|-----|---|
1467714680
|xpath|Xpath|
1467814681
|返回值|目标元素集|
1467914682

14683+
获取元素 xpath。
14684+
14685+
|参数名|说明|
14686+
|-----|---|
14687+
|el|元素|
14688+
|optimized|优化 xpath,默认不启用|
14689+
1468014690
```javascript
14681-
xpath('//html/body'); // -> [body]
14691+
xpath('/html/body'); // -> [body]
14692+
xpath(document.body); // -> /html/body
1468214693
```
1468314694

1468414695
## zip

i18n/xpath.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
11
## CN
22

3-
使用 xpath 选择元素,不支持 IE。
3+
使用 xpath 选择元素或获取元素 xpath,不支持 IE。
4+
5+
使用 xpath 选择元素。
46

57
|参数名|说明|
68
|-----|---|
79
|xpath|Xpath|
810
|返回值|目标元素集|
911

12+
获取元素 xpath。
13+
14+
|参数名|说明|
15+
|-----|---|
16+
|el|元素|
17+
|optimized|优化 xpath,默认不启用|

index.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7193,8 +7193,11 @@
71937193
"test": []
71947194
},
71957195
"xpath": {
7196-
"dependencies": [],
7197-
"description": "Select elements using xpath, IE is not supported.",
7196+
"dependencies": [
7197+
"isStr",
7198+
"Class"
7199+
],
7200+
"description": "Select elements using xpath or get element xpath, IE is not supported.",
71987201
"env": [
71997202
"browser"
72007203
],

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "licia",
3-
"version": "1.47.0",
3+
"version": "1.48.0",
44
"description": "Useful utility collection with zero dependencies",
55
"bin": {
66
"licia": "./bin/licia.js"

src/xpath.js

Lines changed: 158 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,23 @@
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
@@ -17,9 +27,20 @@
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+
});

test/xpath.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
expect(xpath('//html/body')).to.eql([document.body]);
1+
expect(xpath('/html/body')).to.eql([document.body]);
2+
expect(xpath(document.body)).to.equal('/html/body');

0 commit comments

Comments
 (0)