给出这样的DOM结构:
<div>
<div>
<span>
<img/>
<i>
<span></span>
<meter></meter>
</i>
<a><span></span></a>
</span>
</div>
<nav>
<form>
<input/>
<button></button>
</form>
</nav>
</div>
想知道如何接受它然后返回所有选择器的平面数组:
[
'div > div > span > img',
'div > div > span > i > span',
'div > div > span > i > meter',
'div > div > span > a > span',
'div > nav > form > input',
'div > nav > form > button'
]
我的尝试没有到达任何地方:
function outputSelectors(array, node) {
var tag = node.tagName
array.push(tag)
for (var i = 0, n = node.children.length; i < n; i++) {
var child = node.children[i]
outputSelectors(array, child)
}
}
outputSelectors([], document.body.children[0])
不知道从哪里开始。
答案 0 :(得分:1)
您可以使用this answer中的getPath
方法映射页面上的所有元素。
最好在您自己的控制台中尝试此操作,因为代码段需要一些时间才能运行,而代码段的控制台似乎无法正确处理输出。
jQuery.fn.extend({
getPath: function () {
var path, node = this;
while (node.length) {
var realNode = node[0], name = realNode.localName;
if (!name) break;
name = name.toLowerCase();
var parent = node.parent();
var sameTagSiblings = parent.children(name);
if (sameTagSiblings.length > 1) {
allSiblings = parent.children();
var index = allSiblings.index(realNode) + 1;
if (index > 1) {
name += ':nth-child(' + index + ')';
}
}
path = name + (path ? '>' + path : '');
node = parent;
}
return path;
}
});
const allElements = $("*");
const allPaths = allElements.map((_, e) => $(e).getPath());
console.log(allPaths);
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
&#13;
这是一个没有jQuery的版本,如果可以的话:
function getPath (node) {
var path;
while (node.parentElement) {
var name = node.localName;
if (!name) break;
name = name.toLowerCase();
var parent = node.parentElement;
var sameTagSiblings = [...parent.children].filter(e => e.localName === name);
if (sameTagSiblings.length > 1) {
allSiblings = parent.children;
var index = [...allSiblings].indexOf(node) + 1;
if (index > 1) {
name += ':nth-child(' + index + ')';
}
}
path = name + (path ? '>' + path : '');
node = parent;
}
return path;
};
const allElements = document.querySelectorAll("*");
const allPaths = [...allElements].map(e => getPath(e));
console.log(allPaths);
&#13;
答案 1 :(得分:1)
一种可能的,非递归方法从顶部(根,准确)到底部:
function collectLeafNodePathes(root) {
const paths = [];
const selectorParts = [];
let el = root;
while (el) {
const tagName = el.tagName.toLowerCase();
if (el.childElementCount) {
selectorParts.push(tagName);
el = el.firstElementChild;
continue;
}
paths.push(selectorParts.concat([tagName]).join(' > '));
do {
if (el.nextElementSibling) {
el = el.nextElementSibling;
break;
}
el = el.parentNode;
selectorParts.pop();
if (el === root) {
el = null;
}
} while (el);
}
return paths;
}
const selectors = collectLeafNodePathes(document.getElementById('xxx'));
console.log(selectors);
&#13;
<div id="xxx">
<div>
<span>
<img/>
<i>
<span></span>
<meter></meter>
</i>
<a><span></span></a>
</span>
</div>
<nav>
<form>
<input/>
<button></button>
</form>
</nav>
</div>
&#13;
然而,最后一部分(do-while循环)在边缘有点粗糙;对任何进步持开放态度。
我已使用辅助属性(childElementCount,firstElementChild,nextElementSibling)来跳过检查文本节点和内容。如果这不是一个选项(由于兼容性原因),很容易实现polyfill或只是倒带&#39;非元素节点上的循环。
答案 2 :(得分:0)
稍微修改this解决方案以获取路径,稍微修改this以获取叶节点。
function getPath(node)
{
var path;
while (node.parentNode )
{
name = node.nodeName;
if (!name) break;
var parent = node.parentNode;
path = name + (path ? ' > ' + path : '');
node = parent;
}
return path;
}
function getLeafNodes()
{
var allNodes = document.getElementsByTagName("*");
var leafNodes = Array.from( allNodes ).filter(function(elem) {
return !elem.hasChildNodes();
});
return leafNodes;
}
var leadNodes = getLeafNodes() ;
var output = leadNodes.map( s => getPath(s) );
console.log(output);
<div>
<div>
<span>
<img/>
<i>
<span></span>
<meter></meter>
</i>
<a><span></span></a>
</span>
</div>
<nav>
<form>
<input/>
<button></button>
</form>
</nav>
</div>
答案 3 :(得分:0)
您可以使用children()
方法创建递归函数并检查当前元素是否包含子项。
const result = []
const getTag = (el) => el.prop('tagName').toLowerCase()
function print(el, prev = '') {
prev = prev.length ? prev : getTag(el)
const children = el.children();
if(!children.length) result.push(prev)
else {
children.each(function() {
let tag = getTag($(this))
let str = prev + (prev.length ? ' > ' : '') + tag;
print($(this), str)
})
}
}
print($('#start'))
console.log(result)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="start">
<div>
<span>
<img/>
<i>
<span></span>
<meter></meter>
</i>
<a><span></span></a>
</span>
</div>
<nav>
<form>
<input/>
<button></button>
</form>
</nav>
</div>
要获取唯一选择器数组,您可以在最终结果上使用Set
来删除重复项。
let result = []
const getTag = (el) => el.prop('tagName').toLowerCase()
function print(el, prev = '') {
prev = prev.length ? prev : getTag(el)
const children = el.children();
if(!children.length) result.push(prev)
else {
children.each(function() {
let tag = getTag($(this))
let str = prev + (prev.length ? ' > ' : '') + tag;
print($(this), str)
})
}
}
print($('#start'))
result = [...new Set(result)]
console.log(result)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="start">
<div>
<span>
<img/>
<i>
<span></span>
<meter></meter>
</i>
<a><span></span></a>
<a><span></span></a>
</span>
</div>
<nav>
<form>
<input/>
<button></button>
</form>
</nav>
</div>