实现XPath
题目描述
实现一个函数,生成某个DOM元素的xpath,主要包含两部分:标签层级和兄弟元素中的顺序。
比如:
<body>
<ul>
<li>
<span>1</span>
</li>
<li>
<span>2</span>
<span>3</span>
<span id="span_3">4</span>
</li>
</ul>
</body>
<body>
<ul>
<li>
<span>1</span>
</li>
<li>
<span>2</span>
<span>3</span>
<span id="span_3">4</span>
</li>
</ul>
</body>
如果传入id = "span_3" 的元素,那么生成的xpath是body>ul[0]>li[1]>span[2]
思路
我们的参数是目标节点,我们的目标是冒泡到body,然后记录中间的节点即可。
如图所示:
整个过程是:
- 我们通过target先找到了parentNode,即li。
- 我们通过li找到了parentNode,即ul。
- 我们通过ult找到了parentNode,即body。
- 结束
另外 DOM Node 数据结构大概是:
js
{
tagName: 'BODY',
children: [
{
tagName: 'UL',
children: [
{
tagName: 'LI',
children: [{
tagName: 'SPAN'
}]
}
]
}
]
}
{
tagName: 'BODY',
children: [
{
tagName: 'UL',
children: [
{
tagName: 'LI',
children: [{
tagName: 'SPAN'
}]
}
]
}
]
}
可以看出DOM Node是一种递归的数据结构,因此用递归来实现会非常直观和简洁。 如果不是特别严格的场景,通常也不会有严重性能问题。
关键点
- DFS
- 回溯
- parentNode 获取父节点, children 获取子节点
代码
js
function helper(node, path) {
if (node === document.body) return `body ${path}`;
const i = Array.prototype.findIndex.call(node.parentNode.children, el => el === node)
return helper(node.parentNode, `${path} > ${node.tagName.toLowerCase()}[${i}]`);
}
function XPath(node) {
return helper(node, '');
}
function helper(node, path) {
if (node === document.body) return `body ${path}`;
const i = Array.prototype.findIndex.call(node.parentNode.children, el => el === node)
return helper(node.parentNode, `${path} > ${node.tagName.toLowerCase()}[${i}]`);
}
function XPath(node) {
return helper(node, '');
}