Associate an element with a unique number - javascript

I'm looking for a way to associate a DOM element with a unique number, such that no other element in the DOM will be associated with that number. Obviously, I can't use an Id attribute because not all elements have an Id.
The most obvious way to do this is to (somehow) acquire a number that will give the element's position within the DOM, but I'm not sure if this is feasible. Ultimately, given an arbitrary element from the DOM, I'd like to have a way of mapping that element to a number.
Everyone is asking why I need to do this -- given a DOM element, I need to use that element as a key in a JS Object. JS Objects must be strings. So, technically, I do not need a unique number, per se, but I need a unique value that can be turned into a "short string" and used as the key in a JS object.

Technically, I do not need a unique number, per se, but I need a unique value that can be turned into a "short string" and used as the key in a JS object.
I see that you have jQuery tagged. This might be a possible solution to your problem. jQuery has a way to associate an element to an object:
var ele = /* your element */
$(ele).data({name: "Paul"});
// later you can use the element as a key
$(ele).data(); //returns {name: "Paul"}
This will avoid the whole "assigning unique id" mess and just let jQuery does all the hard work for you (creating a map data structure).
Edit by Roamer-1888
Basically, I need to "tag" an element uniquely so that if I encounter the same element in the future, I will know I've seen it before. The "tag" value will be the key in my JS object. I will associate various values in the tagged element. I don't want to use Map or WeakMap because they might not be supported on all browsers.
To meet this requirement, you would typically use jQuery's .data() as follows :
//...
var elementData = $(ele).data('myApplication');
if(!elementData) { // this element was not previously encountered
elementData = {
prop_A: ...,
prop_B: ...,
prop_C: ...
};
$(ele).data('myApplication', elementData);
}
// Here `elementData` is guaranteed to exist and its properties can be read/written.
//...
This technique is particularly useful for preseving state in jQuery plugins. Here is an example
Multiple applications/modules can do this without interfering with each other.

I'm guessing that there might be a better way to solve your problem if we understood what the real problem was. But, at any point in time, you can find what position an element is at (if it is currently inserted in the document) with something like this:
var items = document.getElementsByTagName("*");
If you needed to then get a unique index for a DOM element (at this particular point in time), you can do so by just searching for it in that HTMLCollection. It's position in the collection is guaranteed to be a unique index.
But, of course as the DOM is modified, this HTMLCollection will change as will the index.
If you wanted to assign a non-changing unique index, you could just assign a property to each DOM element based on a monotomically increasing counter.
var idCntr = 1;
function assignIds() {
var items = document.getElementsByTagName("*");
for (var i = 0; i < items.length; i++) {
if (!items[i]._uniqueId) {
items[i]._uniqueId = idCntr++;
}
}
}
You can call this function as many times as you want and it will assign new unique IDs to any DOM elements that don't yet have an id, but will leave ids that were already assigned the same (so they will never change).
If you just want to be able to generate a unique ID for any given DOM node such that you can always have the same ID for that DOM node, then you can just again use a monotomically increasing counter.
var idCntr = 1;
function assignId(elem) {
if (!elem._uniqueId) {
elem._uniqueId = idCntr++;
}
return elem._uniqueId;
}
You can then call this on any DOM element that you want to assign a unique ID to and it will both assign the id to the element and return the id that it assigned. If you pass it an element that already has an id, it will leave that id in place and just return it.
Based on your latest edit, it appears you're just trying to generate an id string that you can use as a key in a JS object. You can certainly use the above assignId() function for that.
In a modern browser, you can also use a Map or a
WeakMap object which will accept the DOM object itself as the key - you don't need to manufacture your own string key. You can then look it up directly with the DOM element too (since it's the key).

Related

Javascript (pure): Set many style attributes using list of values from array

I'm working on a JS class that creates several elements and attaches them to the DOM, and I would like for the class to also set the style attribute properties of those elements. I have a function already that does this for other attributes:
function setAttrs(el, attrValArray){
for(var i=0; i<attrValArray.length;i++){
el.setAttribute(attrValArray[i][0],attrValArray[i][1]);
}
}
The attrValArray input is a 2D array that would look something like
attrValArray = [["class","container"],["id","cc1]];
So what I want is to be able to create a similar array for style property pairs such as:
propValArray = [["display","inline-block"],["position","relative"]];
Which I would then pass to a similar setStyles function.
I could use the same setAttribute method, but instead of looping over the array and setting attributes individually I would have to construct a long string and pass the whole thing as the second argument of setAttribute since I am actually setting many properties of only 1 attribute. But I'd like to avoid this because of the fact that it would override any existing inline styles (not that I use inline styles, but for the sake of principle).
The better option is to set the properties of style on the element. I.e.
el.style.<property-to-set> = "property value";
This does not overwrite anything other than the specific property being set. But I don't see any way to select the "property-to-set" using values from a list. Is there an appropriate way around this?
To be clear, I already know that I can simply assign the desired style attributes to the to-be-created element classes/ids, and was actually wondering if that is the technically correct thing to do based on "good practices" and whatnot. That is what I plan on doing if there is no satisfying alternative.
Also, as stated in the question, I'm strictly interested in a pure JS solution, no JQuery or any other such library/framework.
I don't see any way to select the "property-to-set" using values from a list.
Unless I'm misunderstanding the question, you would do it pretty much the exact same way:
function setStyles(el, cssArray){
for(var i=0; i<cssArray.length;i++){
el.style[cssArray[i][0]] = cssArray[i][1];
}
}
Remember with objects, you can access properties using dot notation object.property or bracket notation object['property']. When you use bracket notation, the text inside the brackets is evaluated, just like when looking up indexes in an array (arrays are really just special objects with number properties).
Alternatively, you could build one long string out of all the property/values pairs and use setAttribute or cssText:
function setStyles(el, cssArray){
let cssText = '';
for(var i=0; i<attrValArray.length;i++){
cssText += cssArray[i][0] + ':' + cssArray[i][1] + ';';
}
el.style.cssText = cssText;
// or el.setAttribute('style', cssText);
}

What does this code using [].filter.call do?

I’m learning javascript and trying to write code that sorts a list, removing elements if they meet certain criteria.
I found this snippet that seems promising but don't have a clue how it works so I can adapt it to my needs:
list = document.getElementById("raffles-list").children; // Get a list of all open raffles on page
list = [].filter.call(list, function(j) {
if (j.getAttribute("style") === "") {
return true;
} else {
return false;
}
});
Can you guys help me learn by explaining what this code block does?
It's getting all the children of the "raffles-list" element, then returning a filtered list of those that contain an empty "style" attribute.
The first line is pretty self-evident - it just retrieves the children from the element with id "raffles-list".
The second line is a little more complicated; it's taking advantage of two things: that [], which is an empty array, is really just an object with various methods/properties on it, and that the logic on the right hand side of the equals sign needs to be evaluated before "list" gets the new value.
Uses a blank array in order to call the "filter" method
Tells the filter to use list as the array to filter, and uses function(j) to do the filtering, where j is the item in the list being tested
If the item has a style attribute that is empty, i.e. has no style applied, it returns true.
Edit:
As per OP comment, [].filter is a prototype, so essentially an object which has various properties just like everything else. In this case filter is a method - see here. Normally you just specify an anonymous function/method that does the testing, however the author here has used the .call in order to specify an arbitrary object to do the testing on. It appears this is already built into the standard filter method, so I don't know why they did it this way.
Array like objects are some of javascript objects which are similar to arrays but with differences for example they don't implement array prototypes. If you want to achieve benefits of array over them (for example like question filter children of an element )you can do it this way:
Array.prototype.functionName.call(arrayLikeObject, [arg1, [arg2 ...]]);
Here in question array like is html element collection; and it takes items without any styling.
list is assigned a collection of elements that are children of the raffles-list element
list is then reassigned by filtering its elements as follows
an empty array is filtered by calling it with the parameter list and a callback function. The formal parameters for call are this (which is the list) and optionally further objects (in this case a callback function)
The callback function receives a formal parameter j and is called for each element
If the element's value for the style attribute is empty the element is retained in the array. Otherwise it is discarded.
At the end list should contain all elements that don't have a value for its style attribute

can I use document.prototype.getElementById to get an element in another element, not in the whole document

I recently found on this javascript tip :
element_number = Array.prototype.indexOf.call(element_1, element_2);
It allows developers to use the indexOf method on an object wich is not an Array.
I would like to know if it is possible to use a similar syntax to call the getElementById method but not on the whole document (document.getElementById), just on an element like this :
my_div_2 = document.prototype.getElementById.call(div_1, "id_of_my_div_2");
The idea is that my document contains tabs and elements having the same id can be present several times in the document.
If it is not possible, did somebody write a function doing that :
Search in an element another element by id.
No, but you can accomplish what you want using querySelector instead.
my_div_2 = div_1.querySelector("#id_of_my_div_2");
Not supported in IE6/7.
Docs: MDN element.querySelector()
That said, duplicate IDs are invalid.
This technique is useful if the same script is used on different pages where the ID may or may not be in a particular container.
No. The getElementById method must be applied on objects of type Document, otherwise it would throw a WRONG_THIS_VALUE exception. You could try it with
myDiv1 = document.getElementById("div1");
myDiv2 = document.getElementById.call(myDiv1, "div2"); // no ".prototype"!
This makes no sense as only one element with a given id is possible in a document.
So the only right syntax would be
my_div_2 = document.getElementById("id_of_my_div_2");
Don't reuse an id in a document. You probably can achieve the desired result using classes instead of id.

Creating a constant time data structure for DOM element references

Relevant discussion.
I understand I can build an array of references to elements/nodes. I realize also that I could use the neat trick of treating an array like a heap (index 2n and 2n+1 for children) to build a (potentially wasteful) binary search tree using it.
But all that's still not enough for the premature optimizer in me. Besides, implementing a BST is going to be bug prone.
Here's my question. Can I somehow use an element reference as index into javascript's hashes (which are objects, or vice versa?). If not, can I conjure up a unique string from an element reference, which I can then use as my hash key? If not, how the hell does jQuery do it?
The easiest option is to just use your own attribute on the DOM object:
var element = document.getElementById("test");
element.myData = "whatever";
Here's the general idea behind how jQuery's .data() function works that you could use in your own plain javascript. It uses one custom attribute on the object and then stores everything else in a data structure indexed by the value of that custom attribute.
var idCntr = 0; // global cntr
var data = {};
var element = document.getElementById("test");
var id = element.uniqueID;
if (!id) {
id = idCntr++ + "";
element.uniqueID = id;
}
data[id] = "whatever";
// then some time later, you can do this
var element = document.getElementById("test");
console.log(data[element.uniqueID]); // whatever
It is a bit more involved to store multiple attributes for a given object in the data object, but this is the general idea.
And, if you can use jQuery, it's trivial:
$("#test").data("myData", "whatever"); // sets the data
console.log($("#test").data("myData")); // retrieves the data
If you want to really see how jQuery's .data() works, you can step through the first call to set data and then retrieve it when using the unminified jQuery. It's easy to see what it does.

Unique identifier for HTML elements

Besides the ID, if you say you want a unique identifier for an HTML element (let’s say a div).
I browsed the DOM for something (like a number or string) that was unique for each element; but the DOM was big and I failed to find that on the Internet.
Is there a property (in the DOM obviously) that is unique only to that element? (Other than the ID and also you don't specify it, but it comes when the DOM is constructed.)
Depending on the objective, here are two suggestions.
Unless you actually need to express the id as some kind of string, you can save the normal DOM reference.
If you do need to express it as a string for some reason, then you'll need to assign a unique id yourself.
var getId = (function () {
var incrementingId = 0;
return function(element) {
if (!element.id) {
element.id = "id_" + incrementingId++;
// Possibly add a check if this ID really is unique
}
return element.id;
};
}());
The only other identifier I can think of is the XPath of the element in the document.
For instance, the title link inside the heading of this very page has an XPath of
/html/body/div[3]/div[2]/div/h1/a
But like Pekka already said, it depends on what you want to do. And I don’t think you can get the XPath easily from the DOM in JavaScript, despite XPath being available nowadays in JavaScript engines.
Internet Explorer has a property, "uniqueID", for every element. The problem is that the other browsers don't support it.
You can use a library or roll your own to create a unique identifier. jQuery has .data():
Store arbitrary data associated with the matched elements or return
the value at the named data store for the first element in the set of
matched elements.
I just encountered the same situation, and while I was looking into some DOM elements in the Chrome developer tools inspector, I noticed that they all seem to have a property like jQuery11230892710877873282 assigned with a unique number.
Obviously the number after 'jQuery' is different every time you load the page. My guess is that jQuery is generating this internally every time it tries to access or manipulate any DOM element.
I played a little bit with it, and it looks like elements that are never accessed/manipulated by jQuery may not have this property, but the moment you do something like $(elem), the property will be there. So, since we're using both jQuery and Lodash, I devised the following function to return a unique ID regardless of whether the element actually has a DOM id attribute.
_.reduce(
$(elem),
function(result, value, key) {
if(_.startsWith(key, 'jQuery'))
return value;
},
0)
There is the name attribute that can be addressed by document.getElementByName.
I don't think other unique identifiers exist - even though you could simulate one by setting a property (like title) to a unique value, and then query for that. But that is kludgy.

Categories