如何打印DOM对象的选择器

时间:2018-01-30 13:30:59

标签: javascript arrays combinations

给出这样的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])

不知道从哪里开始。

4 个答案:

答案 0 :(得分:1)

您可以使用this answer中的getPath方法映射页面上的所有元素。

最好在您自己的控制台中尝试此操作,因为代码段需要一些时间才能运行,而代码段的控制台似乎无法正确处理输出。

&#13;
&#13;
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;
&#13;
&#13;

这是一个没有jQuery的版本,如果可以的话:

&#13;
&#13;
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;
&#13;
&#13;

答案 1 :(得分:1)

一种可能的,非递归方法从顶部(根,准确)到底部:

&#13;
&#13;
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;
&#13;
&#13;

然而,最后一部分(do-while循环)在边缘有点粗糙;对任何进步持开放态度。

我已使用辅助属性(childElementCountfirstElementChildnextElementSibling)来跳过检查文本节点和内容。如果这不是一个选项(由于兼容性原因),很容易实现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>