获取元素的CSS选择器(当它没有id时)

35

我正在尝试通过JavaScript/CSS修改页面(就像Stylish或Greasemonkey一样)。这是一个非常复杂的页面(我没有构建它,也无法在预渲染之前修改),这使得构建CSS选择器变得困难(手动查看文档结构)。我该如何实现这个目标?


抱歉,你是如何确定元素的?还是说你想构建完整的 DOM 树? - Shadow The Spring Wizard
我的最终目的是为该对象创建一个CSS选择器。我可以查看页面代码并从文档中推断出路径(来自gmail.com的示例选择器为“div.nH.T4.pp + div.pp + div.nH.pp.ps.TZ”),我希望有一种方法来帮助我构建选择器。 - rcphq
嘿,rcphq,你找到这个问题的解决方案了吗?我也在寻找同样的东西。 - C J
为什么不使用它的id(如果有),或者如果它没有id就分配一个新的id? - Stijn de Witt
这是一个比看起来更复杂的问题,有好的库可以解决它。此外,它是获取元素的jQuery选择器的重复问题。 - Dan Dascalescu
6个回答

68
function fullPath(el){
  var names = [];
  while (el.parentNode){
    if (el.id){
      names.unshift('#'+el.id);
      break;
    }else{
      if (el==el.ownerDocument.documentElement) names.unshift(el.tagName);
      else{
        for (var c=1,e=el;e.previousElementSibling;e=e.previousElementSibling,c++);
        names.unshift(el.tagName+":nth-child("+c+")");
      }
      el=el.parentNode;
    }
  }
  return names.join(" > ");
}

console.log(  fullPath( $('input')[0] ) );
// "#search > DIV:nth-child(1) > INPUT:nth-child(1)"

这似乎是您所要求的,但您可能会意识到这不能保证只唯一地标识一个元素。(对于上面的示例,所有兄弟输入框也将匹配。)

编辑:更改了代码,使用nth-child而不是CSS类来正确消除单个子项的歧义。


2
不错的简洁纯js函数 :) 只是需要稍微注意一下,当应用于HTML标记时,:nth-child将无法正常工作...或者至少我无法在Firefox或Chrome中让它正常工作...我想这可能是因为<html>没有父元素的缘故。 - Pebbl
没问题,这也不是我会想到的 :) 我已经提交了一个微小的编辑,将你新版本中的“HTML”更改为“html”...因为要使用 .toLowerCase() 方法。 - Pebbl
2
请注意,由于CSS选择器不允许选择DOM文本节点,因此在这种情况下,该代码将产生错误的结果(el.tagName将返回未定义)。 - Grzegorz Luczywo
不错... 可能想要将其打包成一个库,或者改进其中的一个链接,或者改进其中的一个链接,这些链接中有10多个可以完成相同任务的工具。 - Dan Dascalescu
这个方法可以很好地工作,直到他们添加了另一个子元素,这就是使用nth-child()选择器的固有问题。我会使用特异性来派生一个选择器。 - thdoan
谢谢提供代码。我的建议是,如果您正在使用较新的Javascript版本,则应该使用“let”而不是“var”。在这种情况下,您将需要在“for”之前添加“let c, e;”,并且在“for”内部删除“var”。如果您不这样做,'c'将在'for'后面的行中变为“未定义”。请参见https://dev59.com/pXRA5IYBdhLWcg3w9y50#11444416了解原因。 - Almir Campos

23

我发现实际上可以使用来自Chrome devtools源代码的这段代码来解决这个问题,而不需要太多修改。

在将相关方法从WebInspector.DOMPresentationUtils添加到新命名空间并修复一些差异后,我只需这样调用它:

> UTILS.cssPath(node)

实现示例请参见css_path.js


6
这是我找到的迄今为止最佳的解决方案。为了方便起见,我已将其发布在npm上:https://www.npmjs.com/package/cssman。 - Macks
1
@Macks:你是否愿意在GitHub上发布源代码(https://github.com/maximilianschmitt/iniquest/issues/1)? - Dan Dascalescu
3
实际上,Chromium代码生成了许多非唯一选择器 - Dan Dascalescu
如果您所说的非唯一性是指生成的选择器应该返回1个且仅1个元素,那么这从未被操作者明确要求。但是,修改代码以检查兄弟节点并获取索引或其他不同属性应该相当容易。 - tutuDajuju
1
为什么会产生非唯一的结果?我以为它是做n-th child的事情? - Tarwin Stroh-Spijer

20

使用已安装FireBug的FireFox浏览器。

  • 右键单击任何元素
  • 选择“检查元素”
  • 在HTML树中右键单击该元素
  • 选择“复制XPath”或“复制CSS路径”

输出此答案的永久链接(XPath):

/html/body/div[4]/div[2]/div[2]/div[2]/div[3]/table/tbody/tr/td[2]/table/tbody/tr/td/div/a

CSS路径:

html body.question-page div.container div#content div#mainbar div#answers div#answer-4588287.answer table tbody tr td table.fw tbody tr td.vt div.post-menu a


但是关于这个评论:

我的最终目的是创建一个对象的CSS选择器...

如果您的目的是如此,可能有一种更简单的方法是通过JavaScript实现:

var uniquePrefix = 'isThisUniqueEnough_';
var counterIndex = 0;
function addCssToElement(elem, cssText){
    var domId;
    if(elem.id)domId=elem.id;
    else{
        domId = uniquePrefix + (++counterIndex);
        elem.id = domId;
    }
    document.styleSheets[0].insertRule("#"+domId+"{"+cssText+"}");
}

最后一行可能需要针对不同的浏览器进行不同的实现。未经测试。


谢谢,我一直在使用 Chrome 的 Firebug(Lite)版本,但找不到这个选项。完美解决了问题。 - rcphq

6

请查看此CSS选择器生成库@medv/finder

  • 生成最短的选择器
  • 每个页面有唯一的选择器
  • 稳定和健壮的选择器
  • 2.9 kB gzip和minify大小

生成的选择器示例:

.blog > article:nth-child(3) .add-comment

4

function getCssSelector(el)
{
    names = [];
    do {
        index = 0;
        var cursorElement = el;
        while (cursorElement !== null)
        {
            ++index;
            cursorElement = cursorElement.previousElementSibling;
        };
        names.unshift(el.tagName + ":nth-child(" + index + ")");
        el = el.parentElement;
    } while (el !== null);

    return names.join(" > ");
}


有什么想法为什么它在Safari中不起作用吗? - emvaized

0

如果元素是 div 表格或 body 中的第一个子元素,您可以使用 css 的 first-child 伪类。

您可以使用 jQuery 的 nth child() 函数。

http://api.jquery.com/nth-child-selector/

来自jquery.com的示例

<!DOCTYPE html>
<html>
<head>
  <style>

  div { float:left; }
  span { color:blue; }
  </style>
  <script src="http://code.jquery.com/jquery-1.4.4.js"></script>
</head>
<body>
  <div><ul>
    <li>John</li>
    <li>Karl</li>
    <li>Brandon</li>

  </ul></div>
  <div><ul>
    <li>Sam</li>
  </ul></div>

  <div><ul>
    <li>Glen</li>
    <li>Tane</li>
    <li>Ralph</li>

    <li>David</li>
  </ul></div>
<script>$("ul li:nth-child(2)").append("<span> - 2nd!</span>");</script>

</body>
</html>

如果我正确理解了问题,这是我的一点建议。


1
这个方法是可行的,但我是在一个我没有建立的页面上进行操作,所以我需要想办法获取它是第几个“nth-child”。这是我的主要问题。 - rcphq

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接