唯一元素ID,即使元素没有

时间:2008-10-22 16:58:56

标签: javascript firefox dom greasemonkey

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

编辑:

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

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

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

  • 你能不能使用闭包吗?虽然我最初认为他们不会这样做但Closuses确实有效。见我后来的帖子。

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

13 个答案:

答案 0 :(得分:7)

答案是否定的,没有可以访问的内部ID。 Opera和IE(可能是Safari?)支持.sourceIndex(如果DOM有,它会改变)但Firefox没有这种类型。

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

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

我不明白你如何放松对节点的引用而不是(理论上的)内部id?关闭和分配工作或他们不工作。或者我错过了什么?

答案 1 :(得分:6)

你的问题的措辞有点困惑 - 你说你需要一个[你]可以用来引用该元素的字符串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,或其他任何内容)。

也许如果你发布一个你想要做的事情的例子,假设不这样做会更容易帮助你。

答案 2 :(得分:6)

关闭是要走的路。通过这种方式,您可以准确地引用即使在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解决该问题的方法。

答案 3 :(得分: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,以及任何提到闭包的人。谢谢你的帮助!

答案 4 :(得分: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的样子。

答案 5 :(得分:3)

如果你没有修改DOM,你可以按索引顺序获取它们:

Prototype示例)

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

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

答案 6 :(得分:2)

您可以将id属性设置为计算值。原型库中有一个函数可以为您完成此任务。

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

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

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

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

  $(document).ready(function(){

    $("div").attr("id", function (arr) {
          return "div-id" + arr;
        });
  });

答案 7 :(得分:1)

您还可以使用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

答案 8 :(得分:1)

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

答案 9 :(得分:1)

您可以使用以下函数为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/0doc/0/0doc/0/1doc/0/1/0doc/0/1/1等标识符:

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

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

  • while循环中breaknode具有您知道唯一的属性时@id,例如reverse()

  • pieces不是piece,目前看起来更像是从

    生成ID的DOM结构

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

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

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

答案 10 :(得分:0)

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

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

// store myId

这是一个黑客攻击,但它会给每个节点一个你可以使用的id。当然,document.getElementById()不会关注它。

答案 11 :(得分:0)

我想'我刚刚解决了类似的问题。但是,我在浏览器DOM环境中使用jQuery。

var objA = $(“某些dom元素的选择器”); var objB = $(“选择一些其他dom元素”);

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

答案 12 :(得分:0)

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