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

IT技术 javascript jquery html css-selectors
2021-02-09 06:24:59

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

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

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

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

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

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

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

html > body > div.example > span > a

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

这怎么可能?

更新

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

6个回答

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

jQuery.fn.extend({
    getPath: function () {
        var path, node = this;
        while (node.length) {
            var realNode = node[0], name = realNode.name;
            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;
    }
});
最内部的if子句不是错误if (index > 1) { /*...*/ }吗?一定是 if (index > 0) { /*...*/ },不是吗?
2021-03-16 06:24:59
@Mark:只需创建一个点击侦听器并将此方法用作回调
2021-03-23 06:24:59
@Alp——我是 jQuery 的新手……希望你能告诉我当点击任何项目时这个函数是如何实际调用的。听起来我和你有类似的需求,我想跟踪在屏幕上点击的内容。您是否有某种侦听器函数正在等待单击以调用此“getpath”函数?您能提供的任何帮助将不胜感激。谢谢!
2021-03-29 06:24:59
+1 不错的解决方案,我改进了该解决方案以获得多次jQuery回报。看我的回答...
2021-04-07 06:24:59
请注意 - 如果您想要对页面结构变化具有鲁棒性的选择器,那么问题比看起来更复杂,并且有 10 多个库(比较)实现了此功能。
2021-04-11 06:24:59

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

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

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

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

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

改进版

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(',');
    }
});
@DanDascalescu:我不确定问题是否相同。我不想决定这个。我只是改进了现有的答案...
2021-03-17 06:24:59
如何投票关闭这个问题为您发布的项目重复同样的答案来?
2021-03-31 06:24:59

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

分配唯一ID:

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

根据唯一标识进行选择:

// 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 + '"]');
您可以这样做的唯一方法是使用诸如 HTML5 存储之类的东西来存储数据,或者将其保存在服务器上的数据库中。我们正在做类似的事情。
2021-03-15 06:24:59
我不想依赖于我所做的文档更改。我需要一种在不更改源文档的情况下拥有唯一选择器的方法。
2021-03-16 06:24:59
如果元素还没有id,这是一个不错的技巧。但是if检查id元素中存在的a必须更安全。
2021-03-18 06:24:59
这在我重新加载页面后不起作用。我希望能够识别用户在上一个会话中引用的元素
2021-03-24 06:24:59
有趣的想法,可能有用:)
2021-03-26 06:24:59
(any element).onclick(function() {
  uniqueSelector = $(this).getUniqueSelector();
})

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

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

如果你真的需要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;
  };

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

this无法在页面重新加载后使用,以在外部应用程序中使用该元素。+1 用于 xpath 函数
2021-03-20 06:24:59

纯 JavaScript 解决方案

注意:这里使用了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()}`;
}

用法

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

getUniqueSelector(document.getElementById('content'));
我建议检查元素是否有一个idfirst,因为那样我们将有一个非常简单的选择器,而无需遍历 DOM。
2021-03-18 06:24:59
@DanielVeihelmann 当然,但这在具有多个id同名的代码库中不起作用这在我身上发生太多次了
2021-03-19 06:24:59