Javascript 在 Jquery 中获取元素的唯一选择器

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5706837/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 18:32:30  来源:igfitidea点击:

Get unique selector of element in Jquery

javascriptjqueryhtmlcss-selectors

提问by Alp

I want to create something like a recorder whichs tracks all actions of a user. For that, i need to identify elements the user interacts with, so that i can refer to these elements in a later session.

我想创建一个记录器之类的东西,它可以跟踪用户的所有操作。为此,我需要识别用户与之交互的元素,以便我可以在以后的会话中引用这些元素。

Spoken in pseudo-code, i want to be able to do something like the following

用伪代码说话,我希望能够执行以下操作

Sample HTML (could be of any complexity):

示例 HTML(可以是任何复杂性):

<html>
<body>
  <div class="example">
    <p>foo</p>
    <span><a href="bar">bar</a></span>
  </div>
</body>
</html>

User clicks on something, like the link. Now i need to identify the clicked element and save its location in the DOM tree for later usage:

用户点击了一些东西,比如链接。现在我需要识别被点击的元素并将它的位置保存在 DOM 树中以备后用:

(any element).onclick(function() {
  uniqueSelector = $(this).getUniqueSelector();
})

Now, uniqueSelector should be something like (i don't mind if it is xpath or css selector style):

现在,uniqueSelector 应该类似于(我不介意它是 xpath 还是 css 选择器样式):

html > body > div.example > span > a

This would provide the possibility to save that selector string and use it at a later time, to replay the actions the user made.

这将提供保存该选择器字符串并在以后使用它以重播用户所做的操作的可能性。

How is that possible?

这怎么可能?

Update

更新

Got my answer: Getting a jQuery selector for an element

得到了我的答案:为一个元素获取一个 jQuery 选择器

采纳答案by Alp

I'll answer this myself, because i found a solution which i had to modify. The following script is working and is based on a script of Blixt:

我会自己回答这个问题,因为我找到了一个必须修改的解决方案。以下脚本正在运行,并且基于Blixt脚本

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) { 
                var allSiblings = parent.children();
                var index = allSiblings.index(realNode) + 1;
                if (index > 1) {
                    name += ':nth-child(' + index + ')';
                }
            }

            path = name + (path ? '>' + path : '');
            node = parent;
        }

        return path;
    }
});

回答by algorhythm

Same solution like that one from @Alp but compatible with multiple jQuery elements.

与@Alp 的解决方案相同,但与多个 jQuery 元素兼容。

jQuery('.some-selector')can result in one or many DOM elements. @Alp's solution works unfortunately only with the first one. My solution concatenates all them with ,.

jQuery('.some-selector')可以产生一个或多个 DOM 元素。不幸的是,@Alp 的解决方案仅适用于第一个。我的解决方案将所有这些与,.

If you want just handle the first element do it like this:

如果您只想处理第一个元素,请这样做:

jQuery('.some-selector').first().getPath();

// or
jQuery('.some-selector:first').getPath();

Improved version

改良版

jQuery.fn.extend({
    getPath: function() {
        var pathes = [];

        this.each(function(index, element) {
            var path, $node = jQuery(element);

            while ($node.length) {
                var realNode = $node.get(0), name = realNode.localName;
                if (!name) { break; }

                name = name.toLowerCase();
                var parent = $node.parent();
                var sameTagSiblings = parent.children(name);

                if (sameTagSiblings.length > 1)
                {
                    var allSiblings = parent.children();
                    var index = allSiblings.index(realNode) + 1;
                    if (index > 0) {
                        name += ':nth-child(' + index + ')';
                    }
                }

                path = name + (path ? ' > ' + path : '');
                $node = parent;
            }

            pathes.push(path);
        });

        return pathes.join(',');
    }
});

回答by Eli

I think a better solution would be to generate a random id and then access an element based on that id:

我认为更好的解决方案是生成一个随机 id,然后根据该 id 访问一个元素:

Assigning unique id:

分配唯一ID:

// or some other id-generating algorithm
$(this).attr('id', new Date().getTime()); 

Selecting based on the unique id:

根据唯一标识进行选择:

// getting unique id
var uniqueId = $(this).getUniqueId();

// or you could just get the id:
var uniqueId = $(this).attr('id');

// selecting by id:
var element = $('#' + uniqueId);

// if you decide to use another attribute other than id:
var element = $('[data-unique-id="' + uniqueId + '"]');

回答by Gary Green

(any element).onclick(function() {
  uniqueSelector = $(this).getUniqueSelector();
})

thisISthe unique selector and path to that clicked element. Why not use that? You can utilise jquery's $.data()method to set the jquery selector. Alternatively just push the elements you need to use in the future:

this该单击元素的唯一选择器和路径。为什么不使用它?您可以利用 jquery 的$.data()方法来设置 jquery 选择器。或者,只需推送您将来需要使用的元素:

var elements = [];
(any element).onclick(function() {
  elements.push(this);
})


If you really need the xpath, you can calculate it using the following code:

如果你真的需要xpath,你可以使用下面的代码来计算它:

  function getXPath(node, path) {
    path = path || [];
    if(node.parentNode) {
      path = getXPath(node.parentNode, path);
    }

    if(node.previousSibling) {
      var count = 1;
      var sibling = node.previousSibling
      do {
        if(sibling.nodeType == 1 && sibling.nodeName == node.nodeName) {count++;}
        sibling = sibling.previousSibling;
      } while(sibling);
      if(count == 1) {count = null;}
    } else if(node.nextSibling) {
      var sibling = node.nextSibling;
      do {
        if(sibling.nodeType == 1 && sibling.nodeName == node.nodeName) {
          var count = 1;
          sibling = null;
        } else {
          var count = null;
          sibling = sibling.previousSibling;
        }
      } while(sibling);
    }

    if(node.nodeType == 1) {
      path.push(node.nodeName.toLowerCase() + (node.id ? "[@id='"+node.id+"']" : count > 0 ? "["+count+"]" : ''));
    }
    return path;
  };

Reference: http://snippets.dzone.com/posts/show/4349

参考:http: //snippets.dzone.com/posts/show/4349

回答by Nate

This answer does not satisfy the original question description, however it does answer the title question. I came to this question looking for a way to get a unique selector for an element but I didn't have a need for the selector to be valid between page-loads. So, my answer will not work between page-loads.

此答案不满足原始问题描述,但确实回答了标题问题。我来到这个问题寻找一种方法来为元素获取唯一选择器,但我不需要选择器在页面加载之间有效。所以,我的答案在页面加载之间不起作用。

I feel like modifying the DOM is not idel, but it is a good way to build a selector that is unique without a tun of code. I got this idea after reading @Eli's answer:

我觉得修改 DOM 并不理想,但它是构建一个独特的选择器而无需大量代码的好方法。阅读@Eli 的回答后,我有了这个想法:

Assign a custom attribute with a unique value.

分配具有唯一值的自定义属性。

$(element).attr('secondary_id', new Date().getTime())
var secondary_id = $(element).attr('secondary_id');

Then use that unique id to build a CSS Selector.

然后使用该唯一 id 构建一个 CSS 选择器。

var selector = '[secondary_id='+secondary_id+']';

Then you have a selector that will select your element.

然后你有一个选择器来选择你的元素。

var found_again = $(selector);

And you many want to check to make sure there isn't already a secondary_idattribute on the element.

并且你们很多人想要检查以确保secondary_id元素上还没有属性。

if ($(element).attr('secondary_id')) {
  $(element).attr('secondary_id', (new Date()).getTime());
}
var secondary_id = $(element).attr('secondary_id');

Putting it all together

把这一切放在一起

$.fn.getSelector = function(){
  var e = $(this);

  // the `id` attribute *should* be unique.
  if (e.attr('id')) { return '#'+e.attr('id') }

  if (e.attr('secondary_id')) {
    return '[secondary_id='+e.attr('secondary_id')+']'
  }

  $(element).attr('secondary_id', (new Date()).getTime());

  return '[secondary_id='+e.attr('secondary_id')+']'
};

var selector = $('*').first().getSelector();

回答by Brett Zamir

While the question was for jQuery, in ES6, it is pretty easy to get something similar to @Alp's for Vanilla JavaScript (I've also added a couple lines, tracking a nameCount, to minimize use of nth-child):

虽然问题是针对 jQuery 的,但在 ES6 中,很容易获得类似于 @Alp 的用于 Vanilla JavaScript 的内容(我还添加了几行,跟踪nameCount,以尽量减少对 的使用nth-child):

function getSelectorForElement (elem) {
    let path;
    while (elem) {
        let subSelector = elem.localName;
        if (!subSelector) {
            break;
        }
        subSelector = subSelector.toLowerCase();

        const parent = elem.parentElement;

        if (parent) {
            const sameTagSiblings = parent.children;
            if (sameTagSiblings.length > 1) {
                let nameCount = 0;
                const index = [...sameTagSiblings].findIndex((child) => {
                    if (elem.localName === child.localName) {
                        nameCount++;
                    }
                    return child === elem;
                }) + 1;
                if (index > 1 && nameCount > 1) {
                    subSelector += ':nth-child(' + index + ')';
                }
            }
        }

        path = subSelector + (path ? '>' + path : '');
        elem = parent;
    }
    return path;
}

回答by Raphael Rafatpanah

Pure JavaScript Solution

纯 JavaScript 解决方案

Note:This uses Array.fromand Array.prototype.filter, both of which need to be polyfilled in IE11.

注意:这里使用了Array.fromArray.prototype.filter,两者都需要在 IE11 中进行 polyfill。

function getUniqueSelector(node) {
  let selector = "";
  while (node.parentElement) {
    const siblings = Array.from(node.parentElement.children).filter(
      e => e.tagName === node.tagName
    );
    selector =
      (siblings.indexOf(node)
        ? `${node.tagName}:nth-of-type(${siblings.indexOf(node) + 1})`
        : `${node.tagName}`) + `${selector ? " > " : ""}${selector}`;
    node = node.parentElement;
  }
  return `html > ${selector.toLowerCase()}`;
}

Usage

用法

getUniqueSelector(document.getElementsByClassName('SectionFour')[0]);

getUniqueSelector(document.getElementById('content'));

回答by Rikki Nik

I found for my self some modified solution. I added to path selector #id, .className and cut the lenght of path to #id:

我为自己找到了一些修改后的解决方案。我添加到路径选择器#id, .className 并将路径的长度减少到#id:

$.fn.extend({
            getSelectorPath: function () {
                var path,
                    node = this,
                    realNode,
                    name,
                    parent,
                    index,
                    sameTagSiblings,
                    allSiblings,
                    className,
                    classSelector,
                    nestingLevel = true;

                while (node.length && nestingLevel) {
                    realNode = node[0];
                    name = realNode.localName;

                    if (!name) break;

                    name = name.toLowerCase();
                    parent = node.parent();
                    sameTagSiblings = parent.children(name);

                    if (realNode.id) {
                        name += "#" + node[0].id;

                        nestingLevel = false;

                    } else if (realNode.className.length) {
                        className =  realNode.className.split(' ');
                        classSelector = '';

                        className.forEach(function (item) {
                            classSelector += '.' + item;
                        });

                        name += classSelector;

                    } else if (sameTagSiblings.length > 1) {
                        allSiblings = parent.children();
                        index = allSiblings.index(realNode) + 1;

                        if (index > 1) {
                            name += ':nth-child(' + index + ')';
                        }
                    }

                    path = name + (path ? '>' + path : '');
                    node = parent;
                }

                return path;
            }
        });

回答by George Siggouroglou

In case you have an identity attribute (for example id="something"), you should get the value of it like,

如果你有一个身份属性(例如 id="something"),你应该得到它的值,比如,

var selector = "[id='" + $(yourObject).attr("id") + "']";
console.log(selector); //=> [id='something']
console.log($(selector).length); //=> 1

In case you do not have an identity attribute and you want to get the selector of it, you can create an identity attribute. Something like the above,

如果您没有身份属性并且想要获取它的选择器,您可以创建一个身份属性。像上面这样的,

var uuid = guid();
$(yourObject).attr("id", uuid); // Set the uuid as id of your object.

You can use your own guid method, or use the source code found in thisso answer,

您可以使用您自己的 guid 方法,或使用在答案中找到的源代码,

function guid() {
    function s4() {
        return Math.floor((1 + Math.random()) * 0x10000)
                .toString(16)
                .substring(1);
    }
    return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
            s4() + '-' + s4() + s4() + s4();
}

回答by Ashraf Sabry

You may also have a look at findCssSelector. Code is in my other answer.

您也可以查看findCssSelector。代码在我的另一个答案中