41

我正在编写一个 GreaseMonkey 脚本,在其中迭代一堆元素。对于每个元素,我需要一个字符串 ID,以后可以使用它来引用该元素。元素本身没有id属性,我无法修改原始文档以赋予它属性(尽管我可以在脚本中进行 DOM 更改)。我无法将引用存储在我的脚本中,因为当我需要它们时,GreaseMonkey 脚本本身就会超出范围。例如,有什么方法可以获取浏览器使用的“内部”ID?仅 Firefox 的解决方案很好;可以应用于其他场景的跨浏览器解决方案会很棒。

编辑:

  • 如果 GreaseMonkey 脚本超出范围,您以后如何引用这些元素?他们的 GreaseMonkey 脚本正在向 DOM 对象添加事件。我无法将引用存储在数组或其他类似机制中,因为当事件触发时,数组将消失,因为 GreaseMonkey 脚本将超出范围。因此,事件需要某种方式来了解附加事件时脚本所具有的元素引用。并且有问题的元素不是它所附加的元素。

  • 你不能只在元素上使用自定义属性吗?是的,但问题在于查找。我不得不求助于遍历所有元素,以寻找将自定义属性设置为所需 id 的元素。这当然可以,但在大型文档中可能非常耗时。我正在寻找浏览器可以进行查找工作的东西。

  • 等等,你能不能修改文件?我无法修改源文档,但我可以在脚本中进行 DOM 更改。我会在问题中澄清。

  • 你不能使用闭包吗?Closuses 确实有效,尽管我最初认为它们不会。看我后面的帖子。

这听起来像是对以下问题的回答:“我可以使用一些内部浏览器 ID 吗?” 没有。”

4

13 回答 13

10

答案是否定的,没有您可以访问的内部 ID。Opera 和 IE(也许是 Safari?)支持.sourceIndex(如果 DOM 支持,则会更改),但 Firefox 没有这种支持。

您可以通过生成给定节点的 Xpath 或查找节点的索引来模拟源索引,从该节点document.getElementsByTagName('*')总是按源顺序返回元素。

当然,所有这些都需要一个完全静态的文件。对 DOM 的更改将破坏查找。

我不明白的是你如何可以松散对节点的引用而不是(理论上的)内部ID?闭包和分配工作或不工作。还是我错过了什么?

于 2008-10-22T17:40:45.200 回答
10

关闭是要走的路。通过这种方式,您将准确引用甚至可以在 DOM 的一些改组中幸存下来的元素。

不知道闭包的人的例子:

var saved_element = findThatDOMNode();

document.body.onclick = function() 
{
   alert(saved_element); // it's still there!
}

如果您必须将它存储在 cookie 中,那么我建议为它计算 XPath(例如,遍历 DOM 计算以前的兄弟姐妹,直到找到具有 ID 的元素,您最终会得到类似的东西[@id=foo]/div[4]/p[2]/a)。

XPointer是 W3C 对该问题的解决方案。

于 2008-10-22T19:26:28.997 回答
7

对您的问题的措辞有点困惑-您说您“需要一个 [您] 以后可以用来引用该元素的字符串 ID”,但是您“不能将引用存储在 [您的] 脚本中,因为当 [你] 需要它们,GreaseMonkey 脚本本身就会超出范围。”

如果脚本超出范围,那么您以后如何引用它们?!

我将忽略我对您的理解感到困惑的事实,并告诉您我经常编写 Greasemonkey 脚本并且可以修改我访问的 DOM 元素以赋予它们一个 ID 属性。这是您可以用来获取伪唯一值以供临时使用的代码:

var PseudoGuid = new (function() {
    this.empty = "00000000-0000-0000-0000-000000000000";
    this.GetNew = function() {
        var fourChars = function() {
            return (((1 + Math.random()) * 0x10000)|0).toString(16).substring(1).toUpperCase();
        }
        return (fourChars() + fourChars() + "-" + fourChars() + "-" + fourChars() + "-" + fourChars() + "-" + fourChars() + fourChars() + fourChars());
    };
})();

// usage example:
var tempId = PseudoGuid.GetNew();
someDomElement.id = tempId;

这对我有用,我自己只是在 Greasemonkey 脚本中对其进行了测试。


更新:闭包是要走的路——就个人而言,作为一个核心的 JavaScript 开发人员,我不知道你是怎么没有立即想到这些的。:)

myDomElement; // some DOM element we want later reference to

someOtherDomElement.addEventListener("click", function(e) {
   // because of the closure, here we have a reference to myDomElement
   doSomething(myDomElement);
}, false);

现在,myDomElement根据您的描述,显然是您已经拥有的元素之一(因为您正在考虑为其添加 ID 或其他)。

也许如果你发布一个你正在尝试做的事情的例子,那么帮助你会更容易,假设这没有。

于 2008-10-22T17:52:45.317 回答
5

更新:闭包确实是答案。因此,在又摆弄了一些之后,我弄清楚了为什么闭包最初是有问题的以及如何解决它。闭包的棘手之处在于,在遍历元素时必须小心,不要让所有闭包都引用同一个元素。例如,这不起作用:

for (var i = 0; i < elements.length; i++) {
    var element = elements[i];
    var button = document.createElement("button");
    button.addEventListener("click", function(ev) {
        // do something with element here
    }, false)
}

但这确实:

var buildListener = function(element) {
    return function(ev) {
        // do something with event here
    };
};

for (var i = 0; i < elements.length; i++) {
    var element = elements[i];
    var button = document.createElement("button");
    button.addEventListener("click", buildListener(element), false)
}

无论如何,我决定不选择一个答案,因为这个问题有两个答案:1)不,没有可以使用的内部 ID;2)你应该为此使用闭包。所以我只是简单地支持第一批说出是否有内部 ID 或建议生成 ID 的人,以及任何提到闭包的人。谢谢您的帮助!

于 2008-10-23T15:12:45.967 回答
4

如果您可以写入 DOM(我相信您可以)。我会这样解决这个问题:

有一个函数返回或生成一个 ID:

//(function () {

  var idCounter = new Date().getTime();
  function getId( node ) {
    return (node.id) ? node.id : (node.id = 'tempIdPrefix_' + idCounter++ );
  }

//})();

根据需要使用它来获取 ID:

var n = document.getElementById('someid');
getId(n);  // returns "someid"

var n = document.getElementsByTagName('div')[1];
getId(n);  // returns "tempIdPrefix_1224697942198"

这样,您就不必担心服务器将 HTML 交给您时的样子。

于 2008-10-22T18:04:57.023 回答
3

如果您不修改 DOM,则可以通过索引顺序获取它们:

原型示例)

myNodes = document.body.descendants()
alert(document.body.descendants()[1].innerHTML)

您可以遍历所有节点并为它们提供一个唯一的类名,您以后可以轻松选择该类名。

于 2008-10-22T17:38:43.597 回答
2

您可以将 id 属性设置为计算值。原型库中有一个函数可以为您执行此操作。

http://www.prototypejs.org/api/element/identify

我最喜欢的 javascript 库是 jQuery。不幸的是,jQuery 没有识别功能。但是,您仍然可以将 id 属性设置为您自己生成的值。

http://docs.jquery.com/Attributes/attr#keyfn

这是来自 jQuery 文档的部分片段,它根据页面中的位置为 div 设置 id:

  $(document).ready(function(){

    $("div").attr("id", function (arr) {
          return "div-id" + arr;
        });
  });
于 2008-10-22T17:30:12.633 回答
2

您可以使用以下函数为 DOM 中的任何给定节点生成稳定的唯一标识符:

function getUniqueKeyForNode (targetNode) {
    const pieces = ['doc'];
    let node = targetNode;

    while (node && node.parentNode) {
        pieces.push(Array.prototype.indexOf.call(node.parentNode.childNodes, node));
        node = node.parentNode
    }

    return pieces.reverse().join('/');
}

这将为这样的结构创建标识符,例如doc/0, doc/0/0, doc/0/1, doc/0/1/0, doc/0/1/1

<div>
    <div />
    <div>
        <div />
        <div />
    </div>
</div>

您还可以进行一些优化和更改,例如:

  • while循环中,break当它node具有您知道是唯一的属性时,例如@id

  • 不是reverse()pieces目前它只是看起来更像是生成 ID 的 DOM 结构

  • piece doc如果您不需要文档节点的标识符,则不包括第一个

  • 以某种方式将标识符保存在节点上,并将该值重新用于子节点,以避免不得不再次遍历树。

  • 如果您将这些标识符写回 XML,如果您正在编写的属性受到限制,请使用另一个连接字符。

于 2017-05-19T11:44:18.090 回答
1

使用元素的鼠标和/或位置属性生成唯一 ID。

于 2010-03-26T18:10:42.157 回答
0

在 javascript 中,您可以将自定义 ID 字段附加到节点

if(node.id) {
  node.myId = node.id;
} else {
  node.myId = createId();
}

// store myId

这有点小技巧,但它会给每个节点一个你可以使用的 id。当然,document.getElementById()不会关注它。

于 2008-10-22T17:06:42.647 回答
0

您还可以使用 pguid(页面唯一标识符)生成唯一标识符:

 pguid = b9j.pguid.next() // A unique id (suitable for a DOM element)
                          // is generated
                          // Something like "b9j-pguid-20a9ff-0"
 ...
 pguid = b9j.pguid.next() // Another unique one... "b9j-pguid-20a9ff-1"

 // Build a custom generator
 var sequence = new b9j.pguid.Sequence({ namespace: "frobozz" })
 pguid = sequence.next() "frobozz-c861e1-0"

http://appengine.bravo9.com/b9j/documentation/pguid.html

于 2008-10-24T02:44:32.763 回答
0

我“认为”我刚刚解决了一个类似的问题。但是,我在浏览器 DOM 环境中使用 jQuery。

var objA = $("选择器到某个 dom 元素"); var objB = $("其他 dom 元素的选择器");

if(objA[0] === objB[0]) { //太棒了!这两个对象指向完全相同的 dom 节点 }

于 2010-03-30T16:18:57.830 回答
0

好的,没有自动关联到 DOM 元素的 ID。DOM 具有元素的层次结构,这是主要信息。从这个角度来看,您可以使用 jQuery 或 jQLite将数据与 DOM 元素相关联。当您必须将自定义数据绑定到元素时,它可以解决一些问题。

于 2014-06-24T13:23:48.800 回答