Unique element ID, even if element doesn't hav

2019-01-22 11:59发布

I'm writing a GreaseMonkey script where I'm iterating through a bunch of elements. For each element, I need a string ID that I can use to reference that element later. The element itself doesn't have an id attribute, and I can't modify the original document to give it one (although I can make DOM changes in my script). I can't store the references in my script because when I need them, the GreaseMonkey script itself will have gone out of scope. Is there some way to get at an "internal" ID that the browser uses, for example? A Firefox-only solution is fine; a cross-browser solution that could be applied in other scenarios would be awesome.

Edit:

  • If the GreaseMonkey script is out of scope, how are you referencing the elements later? They GreaseMonkey script is adding events to DOM objects. I can't store the references in an array or some other similar mechanism because when the event fires, the array will be gone because the GreaseMonkey script will have gone out of scope. So the event needs some way to know about the element reference that the script had when the event was attached. And the element in question is not the one to which it is attached.

  • Can't you just use a custom property on the element? Yes, but the problem is on the lookup. I'd have to resort to iterating through all the elements looking for the one that has that custom property set to the desired id. That would work, sure, but in large documents it could be very time consuming. I'm looking for something where the browser can do the lookup grunt work.

  • Wait, can you or can you not modify the document? I can't modify the source document, but I can make DOM changes in the script. I'll clarify in the question.

  • Can you not use closures? Closuses did turn out to work, although I initially thought they wouldn't. See my later post.

It sounds like the answer to the question: "Is there some internal browser ID I could use?" is "No."

13条回答
ら.Afraid
2楼-- · 2019-01-22 12:25

The answer is no, there isn't an internal id you can access. Opera and IE (maybe Safari?) support .sourceIndex (which changes if DOM does) but Firefox has nothing of this sort.

You can simulate source-index by generating Xpath to a given node or finding the index of the node from document.getElementsByTagName('*') which will always return elements in source order.

All of this requires a completely static file of course. Changes to DOM will break the lookup.

What I don't understand is how you can loose references to nodes but not to (theoretical) internal id's? Either closures and assignments work or they don't. Or am I missing something?

查看更多
3楼-- · 2019-01-22 12:25

You can also use pguid (page-unique identifier) for unique identifier generation:

 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

查看更多
男人必须洒脱
4楼-- · 2019-01-22 12:26

Closure is the way to go. This way you'll have exact reference to the element that even will survive some shuffling of DOM.

Example for those who don't know closures:

var saved_element = findThatDOMNode();

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

If you had to store it in a cookie, then I recommend computing XPath for it (e.g. walk up the DOM counting previous siblings until you find element with an ID and you'll end up with something like [@id=foo]/div[4]/p[2]/a).

XPointer is W3C's solution to that problem.

查看更多
淡お忘
5楼-- · 2019-01-22 12:30

If you're not modifying the DOM you can get them all by indexed order:

(Prototype example)

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

You could loop through all of the nodes and give them a unique className that you could later select easily.

查看更多
叛逆
6楼-- · 2019-01-22 12:31

UPDATE: Closures are indeed the answer. So after fiddling with it some more, I figured out why closures were initially problematic and how to fix it. The tricky thing with a closure is you have to be careful when iterating through the elements not to end up with all of your closures referencing the same element. For example, this doesn't work:

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)
}

But this does:

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)
}

Anyway, I decided not to select one answer because the question had two answers: 1) No, there are no internal IDs you can use; 2) you should use closures for this. So I simply upvoted the first people to say whether there were internal IDs or who recommended generating IDs, plus anyone who mentioned closures. Thanks for the help!

查看更多
做个烂人
7楼-- · 2019-01-22 12:37

You can set the id attribute to a computed value. There is a function in the prototype library that can do this for you.

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

My favorite javascript library is jQuery. Unfortunately jQuery does not have a function like identify. However, you can still set the id attribute to a value that you generate on your own.

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

Here is a partial snippet from jQuery docs that sets id for divs based on the position in the page:

  $(document).ready(function(){

    $("div").attr("id", function (arr) {
          return "div-id" + arr;
        });
  });
查看更多
登录 后发表回答