如何从jQuery对象中获取选择器

122
$("*").click(function(){
    $(this); // how can I get selector from $(this) ?
});

有没有一种简单的方法可以从$(this)获取选择器呢?有一种方式可以通过选择器选择元素,但是如何从元素中获取选择器呢


1
你明白嵌套元素的点击会返回不止一个div在body中吧?或者我现在太累了? - Mark Schultheiss
2
如果您可以描述您尝试实现的“最终目标”,那将会很有帮助。这样您可能会获得更好的帮助。 - jessegavin
3
一个元素可以通过多种方式进行选择,而选择器并不等同于路径。你对这些信息有什么期望? - deceze
我同意deceze关于选择器 !== path 的观点。你可能有一个UL标签,其中包含三个嵌套的LI标签。它们都将具有相同的路径。 - jessegavin
3
如果您已经找到了一个 JQuery 元素,并且正在使用需要选择器才能工作的插件/模块/子程序(可能不受您控制),那么这将非常有用。 - Andy
显示剩余3条评论
20个回答

60

好的,所以在问题提问者Fidilip的评论中,他/她想要的实际上是获取当前元素的路径。

这里有一段脚本,它将“向上遍历”DOM祖先树,然后构建相当特定的选择器,包括所点击项上的任何idclass属性。

在jsFiddle上查看它的工作原理:http://jsfiddle.net/Jkj2n/209/

<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script>
    $(function() {
        $("*").on("click", function(e) {
          e.preventDefault();
          var selector = $(this)
            .parents()
            .map(function() { return this.tagName; })
            .get()
            .reverse()
            .concat([this.nodeName])
            .join(">");

          var id = $(this).attr("id");
          if (id) { 
            selector += "#"+ id;
          }

          var classNames = $(this).attr("class");
          if (classNames) {
            selector += "." + $.trim(classNames).replace(/\s/gi, ".");
          }

          alert(selector);
      });
    });
    </script>
</head>
<body>
<h1><span>I love</span> jQuery</h1>
<div>
  <p>It's the <strong>BEST THING</strong> ever</p>
  <button id="myButton">Button test</button>
</div>
<ul>
  <li>Item one
    <ul>
      <li id="sub2" >Sub one</li>
      <li id="sub2" class="subitem otherclass">Sub two</li>
    </ul>
  </li>
</ul>
</body>
</html>
例如,如果您点击以下HTML中嵌套列表的第二个列表项,将得到以下结果:
HTML>BODY>UL>LI>UL>LI#sub2.subitem.otherclass

2
谢谢!我会使用您的代码,只需在连接上进行一次修改... join(" > ");,这将使我获得直接子级,从而获得更严格的路径。 - nonsensickle
太棒了...我将在我的Chrome扩展中使用它。只有一件事,有时会使用冒号':'前缀的ID,我们可以用'\:'替换它。 - Savaratkar
好的点子 @nonsensickle。我更新了我的例子,也删除了一个if语句。 - jessegavin
上面的代码片段没有返回SVG对象的类。这里有一个适用于SVG的jsFiddle:http://jsfiddle.net/mikemsq/vbykja4x/7/。 - mikemsq
@nonsensickle,空格可选:https://w3c.github.io/csswg-drafts/selectors/#child-combinators - jrdioko

35

::警告::
.selector在1.7版本之后已被弃用,在1.9版本中已被删除

昨天我在挖掘jQuery代码时看到了它的选择器属性。不知道它是否在文档中定义以及它的可靠性如何(为了未来的保障)。但它是有效的!

$('*').selector // returns *
编辑:如果你在事件内找到选择器,那么这个信息最好是事件本身的一部分,而不是元素的一部分,因为一个元素可能通过各种选择器被分配多个点击事件。解决方案是使用包装器将bind()click()等添加事件的方法包裹起来,而不是直接添加。
jQuery.fn.addEvent = function(type, handler) {
    this.bind(type, {'selector': this.selector}, handler);
};

选择器作为对象的属性被命名为selector。使用event.data.selector访问它。
让我们在一些标记上尝试一下(http://jsfiddle.net/DFh7z/):
<p class='info'>some text and <a>a link</a></p>​

$('p a').addEvent('click', function(event) {
    alert(event.data.selector); // p a
});

免责声明:请记住,与live()事件一样,如果使用DOM遍历方法,则选择器属性可能无效。

<div><a>a link</a></div>

下面的代码将无法工作,因为live依赖于选择器属性,而在这种情况下选择器是a.parent() - 一个无效的选择器。
$('a').parent().live(function() { alert('something'); });

我们的addEvent方法将会被触发,但您也会看到错误的选择器-a.parent()


我认为这是我们能从jQuery获得的最好的解决方案 =)。也许我以后会找到完整的解决方案。无论如何,这真的很方便,谢谢!=) - Fidilip
@MarkoDumic:一定已经被弃用了。该链接现在已经失效,跟随它只会告诉我们:“不存在这样的jQuery方法”。 - hippietrail
13
你真的因为一个回答已经过时而将其点踩三年后吗?!你应该编辑回答并加上警告。 - A. Wolff

22

我们与@drzaus合作,开发了以下jQuery插件。

jQuery.getSelector

!(function ($, undefined) {
    /// adapted http://jsfiddle.net/drzaus/Hgjfh/5/

    var get_selector = function (element) {
        var pieces = [];

        for (; element && element.tagName !== undefined; element = element.parentNode) {
            if (element.className) {
                var classes = element.className.split(' ');
                for (var i in classes) {
                    if (classes.hasOwnProperty(i) && classes[i]) {
                        pieces.unshift(classes[i]);
                        pieces.unshift('.');
                    }
                }
            }
            if (element.id && !/\s/.test(element.id)) {
                pieces.unshift(element.id);
                pieces.unshift('#');
            }
            pieces.unshift(element.tagName);
            pieces.unshift(' > ');
        }

        return pieces.slice(1).join('');
    };

    $.fn.getSelector = function (only_one) {
        if (true === only_one) {
            return get_selector(this[0]);
        } else {
            return $.map(this, function (el) {
                return get_selector(el);
            });
        }
    };

})(window.jQuery);

压缩的Javascript

// https://dev59.com/f3E95IYBdhLWcg3wMa51#15623322
!function(e,t){var n=function(e){var n=[];for(;e&&e.tagName!==t;e=e.parentNode){if(e.className){var r=e.className.split(" ");for(var i in r){if(r.hasOwnProperty(i)&&r[i]){n.unshift(r[i]);n.unshift(".")}}}if(e.id&&!/\s/.test(e.id)){n.unshift(e.id);n.unshift("#")}n.unshift(e.tagName);n.unshift(" > ")}return n.slice(1).join("")};e.fn.getSelector=function(t){if(true===t){return n(this[0])}else{return e.map(this,function(e){return n(e)})}}}(window.jQuery)

使用方法和注意事项

<html>
    <head>...</head>
    <body>
        <div id="sidebar">
            <ul>
                <li>
                    <a href="/" id="home">Home</a>
                </li>
            </ul>
        </div>
        <div id="main">
            <h1 id="title">Welcome</h1>
        </div>

        <script type="text/javascript">

            // Simple use case
            $('#main').getSelector();           // => 'HTML > BODY > DIV#main'

            // If there are multiple matches then an array will be returned
            $('body > div').getSelector();      // => ['HTML > BODY > DIV#main', 'HTML > BODY > DIV#sidebar']

            // Passing true to the method will cause it to return the selector for the first match
            $('body > div').getSelector(true);  // => 'HTML > BODY > DIV#main'

        </script>
    </body>
</html>

修改QUnit测试用例

http://jsfiddle.net/CALY5/5/


我喜欢你的想法,所以我做了一些修复/更改
  • jQuery插件利用jQuery实用程序$.map
  • 可选分隔符(需要剥离奇怪的空标签名)
  • foreach循环中,您不需要检查hasOwnProperty以确保安全吗?
- drzaus
感谢您的评论,我会在这个周末建立一个适当的测试套件,并努力融入您的建议。 - Will
1
@drzaus 我已经更新了问题。我决定去掉你添加的自定义分隔符,因为任何除了“'>'”之外的分隔符都会导致它无法返回元素的选择器。 - Will
很棒,干得好;之前没见过qunit。我原以为分隔符只是用于展示,但现在我明白它是文字选择器路径的一部分。 - drzaus

5

你试过这个吗?

 $("*").click(function(){
    $(this).attr("id"); 
 });

3
尽量避免使用 * 选择器,因为它非常慢! - Ben Carey
3
只有当元素具有ID时,它才有效,所以并不是很好。 - Glen

4
只需在 $ 函数上添加一层即可,如下所示:

$ = (function(jQ) { 
 return (function() { 
  var fnc = jQ.apply(this,arguments);
  fnc.selector = (arguments.length>0)?arguments[0]:null;
  return fnc; 
 });
})($);

现在你可以像这样做

$("a").selector
,即使在较新的jQuery版本中也会返回 "a"。


这正是我想要的。考虑到我寻找了这么长时间,它的工作效果非常好!谢谢你,阿尔伯特。 - Mark Notton

3

试试这个:

$("*").click(function(event){
    console.log($(event.handleObj.selector));
 });

1

嗯,我写了这个简单的jQuery插件。

它会检查id或类名,并尝试给出尽可能精确的选择器。

jQuery.fn.getSelector = function() {

    if ($(this).attr('id')) {
        return '#' + $(this).attr('id');
    }

    if ($(this).prop("tagName").toLowerCase() == 'body')    return 'body';

    var myOwn = $(this).attr('class');
    if (!myOwn) {
        myOwn = '>' + $(this).prop("tagName");
    } else {
        myOwn = '.' + myOwn.split(' ').join('.');
    }

    return $(this).parent().getSelector() + ' ' + myOwn;
}

1

考虑到这里阅读的一些答案,我想提出以下建议:

function getSelectorFromElement($el) {
  if (!$el || !$el.length) {
    return ;
  }

  function _getChildSelector(index) {
    if (typeof index === 'undefined') {
      return '';
    }

    index = index + 1;
    return ':nth-child(' + index + ')';
  }

  function _getIdAndClassNames($el) {
    var selector = '';

    // attach id if exists
    var elId = $el.attr('id');
    if(elId){
      selector += '#' + elId;
    }

    // attach class names if exists
    var classNames = $el.attr('class');
    if(classNames){
      selector += '.' + classNames.replace(/^\s+|\s+$/g, '').replace(/\s/gi, '.');
    }

    return selector;
  }

  // get all parents siblings index and element's tag name,
  // except html and body elements
  var selector = $el.parents(':not(html,body)')
    .map(function() {
      var parentIndex = $(this).index();

      return this.tagName + _getChildSelector(parentIndex);
    })
    .get()
    .reverse()
    .join(' ');

  if (selector) {
    // get node name from the element itself
    selector += ' ' + $el[0].nodeName +
      // get child selector from element ifself
      _getChildSelector($el.index());
  }

  selector += _getIdAndClassNames($el);

  return selector;
}

也许有用来创建一个jQuery插件?

5年过去了。我在Cypress - 页面对象模型中使用它。在这里记录一下,如果有人需要Cypress.Chaniable对象的选择器,只需将$(this)替换为Cypress.$(this),然后其他都没问题了。 - celoaga

1
我之前尝试了以上的解决方案,但还是得到了多个元素。因此,我扩展了dds1024的工作,以便更精准地定位DOM元素。
例如:DIV:nth-child(1) DIV:nth-child(3) DIV:nth-child(1) ARTICLE:nth-child(1) DIV:nth-child(1) DIV:nth-child(8) DIV:nth-child(2) DIV:nth-child(1) DIV:nth-child(2) DIV:nth-child(1) H4:nth-child(2)
代码:
function getSelector(el)
{
    var $el = jQuery(el);

    var selector = $el.parents(":not(html,body)")
                .map(function() { 
                                    var i = jQuery(this).index(); 
                                    i_str = ''; 

                                    if (typeof i != 'undefined') 
                                    {
                                        i = i + 1;
                                        i_str += ":nth-child(" + i + ")";
                                    }

                                    return this.tagName + i_str; 
                                })
                .get().reverse().join(" ");

    if (selector) {
        selector += " "+ $el[0].nodeName;
    }

    var index = $el.index();
    if (typeof index != 'undefined')  {
        index = index + 1;
        selector += ":nth-child(" + index + ")";
    }

    return selector;
}

1

我对@jessegavin的修复进行了一些修改。

如果元素上有ID,它将立即返回。我还添加了一个名称属性检查和nth-child选择器,以防元素没有id、class或name。

如果页面上有多个表单并且具有相似的输入,则可能需要对名称进行作用域限定,但我尚未处理这个问题。

function getSelector(el){
    var $el = $(el);

    var id = $el.attr("id");
    if (id) { //"should" only be one of these if theres an ID
        return "#"+ id;
    }

    var selector = $el.parents()
                .map(function() { return this.tagName; })
                .get().reverse().join(" ");

    if (selector) {
        selector += " "+ $el[0].nodeName;
    }

    var classNames = $el.attr("class");
    if (classNames) {
        selector += "." + $.trim(classNames).replace(/\s/gi, ".");
    }

    var name = $el.attr('name');
    if (name) {
        selector += "[name='" + name + "']";
    }
    if (!name){
        var index = $el.index();
        if (index) {
            index = index + 1;
            selector += ":nth-child(" + index + ")";
        }
    }
    return selector;
}

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