代码之家  ›  专栏  ›  技术社区  ›  Robert J. Walker

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

  •  29
  • Robert J. Walker  · 技术社区  · 16 年前

    我正在编写一个greasemonkey脚本,在其中我迭代了一系列元素。对于每个元素,我需要一个字符串ID,稍后可以使用它来引用该元素。元素本身没有 id 属性,并且我不能修改原始文档来给它一个(尽管我可以在脚本中更改DOM)。我无法将引用存储在脚本中,因为当我需要它们时,GreaseMonkey脚本本身将超出范围。例如,有没有办法获得浏览器使用的“内部”ID?一个只支持火狐的解决方案是很好的;一个可以应用于其他场景的跨浏览器解决方案是很棒的。

    编辑:

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

    • 不能对元素使用自定义属性吗? 是的,但问题在于查找。我不得不迭代所有元素,寻找一个将自定义属性设置为所需ID的元素。当然,这是可行的,但在大型文档中,这可能非常耗时。我在找浏览器可以做查找工作的东西。

    • 等等,你能不能修改文件? 我不能修改源文档,但我可以在脚本中更改DOM。我将澄清这个问题。

    • 你不能用闭包吗? 克洛索斯确实发挥了作用,尽管我起初认为他们不会。看我后来的帖子。

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

    13 回复  |  直到 16 年前
        1
  •  7
  •   Borgar    16 年前

    答案是“否”,没有您可以访问的内部ID。歌剧和IE(也许是狩猎?)支持 .sourceIndex (如果dom会改变)但是firefox没有类似的功能。

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

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

    我不明白的是,您如何可以释放对节点的引用,而不是(理论上)内部ID的引用?关闭和分配工作或者它们不工作,或者我遗漏了什么?

        2
  •  6
  •   Kornel    16 年前

    关闭是一条路。这样,您就可以精确地引用元素,这些元素甚至可以经受住一些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
  •  6
  •   Jason Bunting    16 年前

    对问题的措辞有点困惑——你说你“需要一个字符串ID,稍后你可以使用它来引用该元素”,但是你“不能将引用存储在你的脚本中,因为当你需要它们的时候,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;
    

    这对我很有用,我只是用一个油脂猴子的脚本来测试它。


    更新: 闭包是一种方式-就个人而言,作为一个核心的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,或者其他任何元素)。

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

        4
  •  5
  •   Robert J. Walker    16 年前

    更新: 关闭确实是答案。所以,在对它进行了更多的处理之后,我发现了闭包最初有问题的原因以及如何修复它。闭包的棘手之处在于,在遍历元素时必须小心,不要以引用同一元素的所有闭包结束。例如,这不起作用:

    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的人,以及任何提到闭包的人。谢谢你的帮助!

        5
  •  4
  •   Borgar    16 年前

    如果你 可以 写信给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交付给您时,您不必担心它是什么样子的。

        6
  •  3
  •   Diodeus - James MacFarlane    16 年前

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

    ( Prototype 实例)

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

    您可以循环访问所有节点,并为它们提供一个唯一的类名,稍后您可以轻松地选择。

        7
  •  2
  •   Michael    16 年前

    可以将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;
            });
      });
    
        8
  •  1
  •   Robert Krimen    16 年前

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

        9
  •  1
  •   Paul Tierney    14 年前

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

        10
  •  1
  •   wybe    7 年前

    可以使用以下函数为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,那么如果要写的属性受到限制,请使用另一个串联字符。

        11
  •  0
  •   sblundy    16 年前

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

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

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

        12
  •  0
  •   Tom Carnell    14 年前

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

    var obja=$(“selector to some dom element”); var objb=$(“其他DOM元素的选择器”);

    如果(obja[0]==objb[0])。{ 很好!这两个对象指向完全相同的DOM节点 }

        13
  •  0
  •   kisp    10 年前

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