Why do you use Attribute Nodes? - javascript

This is an example of working with attribute nodes. Why would I need to use attribute nodes when I am accessing the DOM in JavaScript
Learning how to setAtttribute,getAttribute,removeAttribute
Need to know why and when I use this ?
if(document.getElementById("plc").hasAttribute("class"))
penguin.setAttribute("class", "hidden");
var penguin = document.getElementById("plc");
alert(document.getElementById("plc").getAttribute("class"));

getAttribute, setAttribute, and removeAttribute are technically called methods, and they act on the DOM node by manipulating the attributes. The DOM node itself is what you've assigned to your variable penguin (but it should be assigned before you use it).
One common use case has to do with storing bits of data on a node. You can set that information and retrieve it later.
Say you have a couple penguins.
<div id='penguin-crowd'>
<div class='penguin' data-species='emperor'></div>
<div class='penguin' data-species='king'></div>
</div>
Now you want to list their species.
let penguins = document.getElementsByClassName('penguin');
for (let i = 0; i < penguins.length; i++) {
let species = penguin[i].getAttribute('data-species');
console.log(`This penguin's species is ${species}`);
}
Result:
// This penguin's species is emperor
// This penguin's species is king
Add another penguin to the crowd.
let penguinCrowd = document.getElementById('penguin-crowd');
let newPenguin = document.createElement('div');
newPenguin.classList.add('penguin');
newPenguin.setAttribute('data-species','emperor');
penguinCrowd.appendChild(newPenguin);
Now you have
<div id='penguin-crowd'>
<div class='penguin' data-species='emperor'></div>
<div class='penguin' data-species='king'></div>
<div class='penguin' data-species='emperor'></div>
</div>
removeAttribute removes the whole attribute from a node, so if for some reason that needs to happen, it's your go-to method.

setAttribute() for custom attributes you create ie node.className vs node.userName. className is a standard attribute vs userName is custom. For example, node.setAttribute('userName', 'someName') vs node.className = 'someName'.
getAttribute() gives you back attributes of the DOM vs properties like node.id. Most of the time the values are the same, but there are cases when they aren't. For example input's checked property: getAttribute() will retrieve checked/empty string, but input.checked will come back with true/false. There was more discussion on this in this question you can check out getAttribute() versus Element object properties?
removeAttribute() is simply to remove any attribute instead of setting it to null. In the official docs it says you should use removeAttribute() instead of setting the attribute value to null either directly or using setAttribute(). Many attributes will not behave as expected if you set them to null.

Related

create variables for all elements using id attributes

need to create variables from all elements having id attribute
name of each variable should be just the value of id attribute
for example <div id='btnsave'>SAVE</div> - should be a variable named btnsave
here is my try - without success:
let els = $("*");
els.forEach(function(el){
if(el.hasId()){
console.log(el.attr('id'));
window[el] = $('#' + el);
}
});
If you're using id attributes for your elements then they already have references accessible through the properties of the window object which match their id value:
console.log(window.foo.textContent);
console.log(window.fizz.textContent);
<p id="foo">bar</p>
<p id="fizz">buzz</p>
Given your comment under the question:
I have a lot of divs as buttons and is simpler to write btnsave.on('click'... then $('#btnsave').on('click'...
In that case you simply need to cache the selector at the top of your script (within scope) and use it where required. This is a standard pattern to follow.
Creating jQuery objects from every element in the DOM with an id and storing them in the window object is an anti-pattern, which will cause performance issues and most likely break native code which expects those references to contain DOMElement objects, not jQuery objects.
Do not do it.
To get elements by any attribute you can use query selector.
let elementsWithId = {}
document.querySelectorAll('[id]').forEach(el => {
elementsWithId[el.id] = el
})
console.log(elementsWithId)
You can record a jQuery element to window variable. Example:
window[el.id] = $(el)
But as people mentioned, this is a bad practice.

whats a difference between storing data in variable attached to Dom element vs storing data in attribute like data? [duplicate]

Has a best-practice around using setAttribute instead of the dot (.) attribute notation been developed?
E.g.:
myObj.setAttribute("className", "nameOfClass");
myObj.setAttribute("id", "someID");
or
myObj.className = "nameOfClass";
myObj.id = "someID";
From Javascript: The Definitive Guide, it clarifies things. It notes that HTMLElement objects of a HTML doc define JS properties that correspond to all standard HTML attributes.
So you only need to use setAttribute for non-standard attributes.
Example:
node.className = 'test'; // works
node.frameborder = '0'; // doesn't work - non standard attribute
node.setAttribute('frameborder', '0'); // works
None of the previous answers are complete and most contain misinformation.
There are three ways of accessing the attributes of a DOM Element in JavaScript. All three work reliably in modern browsers as long as you understand how to utilize them.
1. element.attributes
Elements have a property attributes that returns a live NamedNodeMap of Attr objects. The indexes of this collection may be different among browsers. So, the order is not guaranteed. NamedNodeMap has methods for adding and removing attributes (getNamedItem and setNamedItem, respectively).
Notice that though XML is explicitly case sensitive, the DOM spec calls for string names to be normalized, so names passed to getNamedItem are effectively case insensitive.
Example Usage:
var div = document.getElementsByTagName('div')[0];
//you can look up specific attributes
var classAttr = div.attributes.getNamedItem('CLASS');
document.write('attributes.getNamedItem() Name: ' + classAttr.name + ' Value: ' + classAttr.value + '<br>');
//you can enumerate all defined attributes
for(var i = 0; i < div.attributes.length; i++) {
var attr = div.attributes[i];
document.write('attributes[] Name: ' + attr.name + ' Value: ' + attr.value + '<br>');
}
//create custom attribute
var customAttr = document.createAttribute('customTest');
customAttr.value = '567';
div.attributes.setNamedItem(customAttr);
//retreive custom attribute
customAttr = div.attributes.getNamedItem('customTest');
document.write('attributes.getNamedItem() Name: ' + customAttr.name + ' Value: ' + customAttr.value + '<br>');
<div class="class1" id="main" data-test="stuff" nonStandard="1234"></div>
2. element.getAttribute & element.setAttribute
These methods exist directly on the Element without needing to access attributes and its methods but perform the same functions.
Again, notice that string name are case insensitive.
Example Usage:
var div = document.getElementsByTagName('div')[0];
//get specific attributes
document.write('Name: class Value: ' + div.getAttribute('class') + '<br>');
document.write('Name: ID Value: ' + div.getAttribute('ID') + '<br>');
document.write('Name: DATA-TEST Value: ' + div.getAttribute('DATA-TEST') + '<br>');
document.write('Name: nonStandard Value: ' + div.getAttribute('nonStandard') + '<br>');
//create custom attribute
div.setAttribute('customTest', '567');
//retreive custom attribute
document.write('Name: customTest Value: ' + div.getAttribute('customTest') + '<br>');
<div class="class1" id="main" data-test="stuff" nonStandard="1234"></div>
3. Properties on the DOM object, such as element.id
Many attributes can be accessed using convenient properties on the DOM object. Which properties exist on a given object depends on the object's DOM node type, regardless of which attributes are specified in the HTML. The available properties are defined somewhere in the prototype chain of DOM object in question. So, the specific properties that are defined will depend on the type of Element you are accessing.
For example, className and id are defined on Element and exist on all DOM nodes that are elements, but not text or comment nodes. value is more narrowly defined. It only available on HTMLInputElement and it's descendants.
Notice that JavaScript properties are case sensitive. Although most properties will use lowercase, some are camelCase. So always check the spec to be sure.
This "chart" captures a portion of the prototype chain for these DOM objects. It's not even close to complete, but it demonstrates the overall structure.
____________Node___________
| | |
Element Text Comment
| |
HTMLElement SVGElement
| |
HTMLInputElement HTMLSpanElement
Example Usage:
var div = document.getElementsByTagName('div')[0];
//get specific attributes
document.write('Name: class Value: ' + div.className + '<br>');
document.write('Name: id Value: ' + div.id + '<br>');
document.write('Name: ID Value: ' + div.ID + '<br>'); //undefined
document.write('Name: data-test Value: ' + div.dataset.test + '<br>'); //.dataset is a special case
document.write('Name: nonStandard Value: ' + div.nonStandard + '<br>'); //undefined
<div class="class1" id="main" data-test="stuff" nonStandard="1234"></div>
Caveat: This is an explanation of how the HTML spec define attributes and how modern, evergreen browsers handle them. There certainly are old browsers (IE, Netscape, etc.) that didn't adhere to or even predated the spec. If you need to support old ancient (broken) browsers, you'll need more information than provided here.
You should always use the direct .attribute form (but see the quirksmode link below) if you want programmatic access in JavaScript. It should handle the different types of attributes (think "onload") correctly.
Use getAttribute/setAttribute when you wish to deal with the DOM as it is (e.g. literal text only). Different browsers confuse the two. See Quirks modes: attribute (in)compatibility.
One case I found where setAttribute is necessary is when changing ARIA attributes, since there are no corresponding properties. For example
x.setAttribute('aria-label', 'Test');
x.getAttribute('aria-label');
There's no x.arialabel or anything like that, so you have to use setAttribute.
Edit: x["aria-label"] does not work. You really do need setAttribute.
x.getAttribute('aria-label')
null
x["aria-label"] = "Test"
"Test"
x.getAttribute('aria-label')
null
x.setAttribute('aria-label', 'Test2')
undefined
x["aria-label"]
"Test"
x.getAttribute('aria-label')
"Test2"
These answers aren't really addressing the large confusion with between properties and attributes. Also, depending on the Javascript prototype, sometimes you can use a an element's property to access an attributes and sometimes you can't.
First, you have to remember that an HTMLElement is a Javascript object. Like all objects, they have properties. Sure, you can create a property called nearly anything you want inside HTMLElement, but it doesn't have to do anything with the DOM (what's on the page). The dot notation (.) is for properties. Now, there some special properties that are mapped to attributes, and at the time or writing there are only 4 that are guaranteed (more on that later).
All HTMLElements include a property called attributes. HTMLElement.attributes is a live NamedNodeMap Object that relates to the elements in the DOM. "Live" means that when the node changes in the DOM, they change on the JavaScript side, and vice versa. DOM attributes, in this case, are the nodes in question. A Node has a .nodeValue property that you can change. NamedNodeMap objects have a function called setNamedItem where you can change the entire node. You can also directly access the node by the key. For example, you can say .attributes["dir"] which is the same as .attributes.getNamedItem('dir'); (Side note, NamedNodeMap is case-insensitive, so you can also pass 'DIR');
There's a similar function directly in HTMLElement where you can just call setAttribute which will automatically create a node if it doesn't exist and set the nodeValue. There are also some attributes you can access directly as properties in HTMLElement via special properties, such as dir. Here's a rough mapping of what it looks like:
HTMLElement {
attributes: {
setNamedItem: function(attr, newAttr) {
this[attr] = newAttr;
},
getNamedItem: function(attr) {
return this[attr];
},
myAttribute1: {
nodeName: 'myAttribute1',
nodeValue: 'myNodeValue1'
},
myAttribute2: {
nodeName: 'myAttribute2',
nodeValue: 'myNodeValue2'
},
}
setAttribute: function(attr, value) {
let item = this.attributes.getNamedItem(attr);
if (!item) {
item = document.createAttribute(attr);
this.attributes.setNamedItem(attr, item);
}
item.nodeValue = value;
},
getAttribute: function(attr) {
return this.attributes[attr] && this.attributes[attr].nodeValue;
},
dir: // Special map to attributes.dir.nodeValue || ''
id: // Special map to attributes.id.nodeValue || ''
className: // Special map to attributes.class.nodeValue || ''
lang: // Special map to attributes.lang.nodeValue || ''
}
So you can change the dir attributes 6 ways:
// 1. Replace the node with setNamedItem
const newAttribute = document.createAttribute('dir');
newAttribute.nodeValue = 'rtl';
element.attributes.setNamedItem(newAttribute);
// 2. Replace the node by property name;
const newAttribute2 = document.createAttribute('dir');
newAttribute2.nodeValue = 'rtl';
element.attributes['dir'] = newAttribute2;
// OR
element.attributes.dir = newAttribute2;
// 3. Access node with getNamedItem and update nodeValue
// Attribute must already exist!!!
element.attributes.getNamedItem('dir').nodeValue = 'rtl';
// 4. Access node by property update nodeValue
// Attribute must already exist!!!
element.attributes['dir'].nodeValue = 'rtl';
// OR
element.attributes.dir.nodeValue = 'rtl';
// 5. use setAttribute()
element.setAttribute('dir', 'rtl');
// 6. use the UNIQUELY SPECIAL dir property
element["dir"] = 'rtl';
element.dir = 'rtl';
You can update all properties with methods #1-5, but only dir, id, lang, and className with method #6.
Extensions of HTMLElement
HTMLElement has those 4 special properties. Some elements are extended classes of HTMLElement have even more mapped properties. For example, HTMLAnchorElement has HTMLAnchorElement.href, HTMLAnchorElement.rel, and HTMLAnchorElement.target. But, beware, if you set those properties on elements that do not have those special properties (like on a HTMLTableElement) then the attributes aren't changed and they are just, normal custom properties. To better understand, here's an example of its inheritance:
HTMLAnchorElement extends HTMLElement {
// inherits all of HTMLElement
href: // Special map to attributes.href.nodeValue || ''
target: // Special map to attributes.target.nodeValue || ''
rel: // Special map to attributes.ref.nodeValue || ''
}
Custom Properties
Now the big warning: Like all Javascript objects, you can add custom properties. But, those won't change anything on the DOM. You can do:
const newElement = document.createElement('div');
// THIS WILL NOT CHANGE THE ATTRIBUTE
newElement.display = 'block';
But that's the same as
newElement.myCustomDisplayAttribute = 'block';
This means that adding a custom property will not be linked to .attributes[attr].nodeValue.
Performance
I've built a jsperf test case to show the difference: https://jsperf.com/set-attribute-comparison. Basically, In order:
Custom properties because they don't affect the DOM and are not attributes.
Special mappings provided by the browser (dir, id, className).
If attributes already exists, element.attributes.ATTRIBUTENAME.nodeValue =
setAttribute();
If attributes already exists, element.attributes.getNamedItem(ATTRIBUTENAME).nodeValue = newValue
element.attributes.ATTRIBUTENAME = newNode
element.attributes.setNamedItem(ATTRIBUTENAME) = newNode
Conclusion (TL;DR)
Use the special property mappings from HTMLElement: element.dir, element.id, element.className, or element.lang.
If you are 100% sure the element is an extended HTMLElement with a special property, use that special mapping. (You can check with if (element instanceof HTMLAnchorElement)).
If you are 100% sure the attribute already exists, use element.attributes.ATTRIBUTENAME.nodeValue = newValue.
If not, use setAttribute().
"When to use setAttribute vs .attribute= in JavaScript?"
A general rule is to use .attribute and check if it works on the browser.
..If it works on the browser, you're good to go.
..If it doesn't, use .setAttribute(attribute, value) instead of .attribute for that attribute.
Rinse-repeat for all attributes.
Well, if you're lazy you can simply use .setAttribute. That should work fine on most browsers. (Though browsers that support .attribute can optimize it better than .setAttribute(attribute, value).)
Interesting observation from Google API script regarding this:
They do it like this:
var scriptElement = document.createElement("script");
scriptElement = setAttribute("src", "https://some.com");
scriptElement = setAttribute("nonce", "https://some.com");
scriptElement.async = "true";
Notice, how they use setAttribute for "src" and "nonce", but then .async = ... for "async" attribute.
I'm not 100% sure, but probably that's because "async" is only supported on browsers that support direct .attr = assignment. So, there's no sense trying to setAttribute("async") because if browser doesn't understand .async=... - it will not understand "async" attribute.
Hopefully, that's a helpful insight from my ongoing "Un-minify GAPI" research project. Correct me if I'm wrong.
This looks like one case where it is better to use setAttribute:
Dev.Opera — Efficient JavaScript
var posElem = document.getElementById('animation');
var newStyle = 'background: ' + newBack + ';' +
'color: ' + newColor + ';' +
'border: ' + newBorder + ';';
if(typeof(posElem.style.cssText) != 'undefined') {
posElem.style.cssText = newStyle;
} else {
posElem.setAttribute('style', newStyle);
}
methods for setting attributes(for example class) on an element:
1. el.className = string
2. el.setAttribute('class',string)
3. el.attributes.setNamedItem(object)
4. el.setAttributeNode(node)
I have made a simple benchmark test (here)
and it seems that setAttributeNode is about 3 times faster then using setAttribute.
so if performance is an issue - use "setAttributeNode"
This is very good discussion. I had one of those moments when I wished or lets say hoped (successfully that I might add) to reinvent the wheel be it a square one.
Any ways above is good discussion, so any one coming here looking for what is the difference between Element property and attribute. here is my penny worth and I did have to find it out hard way. I would keep it simple so no extraordinary tech jargon.
suppose we have a variable calls 'A'. what we are used to is as following.
Below will throw an error because simply it put its is kind of object that can only have one property and that is singular left hand side = singular right hand side object. Every thing else is ignored and tossed out in bin.
let A = 'f';
A.b =2;
console.log(A.b);
who has decided that it has to be singular = singular. People who make JavaScript and html standards and thats how engines work.
Lets change the example.
let A = {};
A.b =2;
console.log(A.b);
This time it works ..... because we have explicitly told it so and who decided we can tell it in this case but not in previous case. Again people who make JavaScript and html standards.
I hope we are on this lets complicate it further
let A = {};
A.attribute ={};
A.attribute.b=5;
console.log(A.attribute.b); // will work
console.log(A.b); // will not work
What we have done is tree of sorts level 1 then sub levels of non-singular object. Unless you know what is where and and call it so it will work else no.
This is what goes on with HTMLDOM when its parsed and painted a DOm tree is created for each and every HTML ELEMENT. Each has level of properties per say. Some are predefined and some are not. This is where ID and VALUE bits come on.
Behind the scene they are mapped on 1:1 between level 1 property and sun level property aka attributes. Thus changing one changes the other. This is were object getter ans setter scheme of things plays role.
let A = {
attribute :{
id:'',
value:''
},
getAttributes: function (n) {
return this.attribute[n];
},
setAttributes: function (n,nn){
this.attribute[n] = nn;
if(this[n]) this[n] = nn;
},
id:'',
value:''
};
A.id = 5;
console.log(A.id);
console.log(A.getAttributes('id'));
A.setAttributes('id',7)
console.log(A.id);
console.log(A.getAttributes('id'));
A.setAttributes('ids',7)
console.log(A.ids);
console.log(A.getAttributes('ids'));
A.idsss=7;
console.log(A.idsss);
console.log(A.getAttributes('idsss'));
This is the point as shown above ELEMENTS has another set of so called property list attributes and it has its own main properties. there some predefined properties between the two and are mapped as 1:1 e.g. ID is common to every one but value is not nor is src. when the parser reaches that point it simply pulls up dictionary as to what to when such and such are present.
All elements have properties and attributes and some of the items between them are common. What is common in one is not common in another.
In old days of HTML3 and what not we worked with html first then on to JS. Now days its other way around and so has using inline onlclick become such an abomination. Things have moved forward in HTML5 where there are many property lists accessible as collection e.g. class, style. In old days color was a property now that is moved to css for handling is no longer valid attribute.
Element.attributes is sub property list with in Element property.
Unless you could change the getter and setter of Element property which is almost high unlikely as it would break hell on all functionality is usually not writable off the bat just because we defined something as A.item does not necessarily mean Js engine will also run another line of function to add it into Element.attributes.item.
I hope this gives some headway clarification as to what is what.
Just for the sake of this I tried Element.prototype.setAttribute with custom function it just broke loose whole thing all together, as it overrode native bunch of functions that set attribute function was playing behind the scene.
Adding 2 more points related to .textContent and .innerHTML
<div id="mydiv"></div>
var elem = document.getElementById("mydiv");
elem.textContent = "hello"; // OK - Content has been updated
elem.setAttribute("textContent", "hello"); // NOT OK - You are trying to set
// the attribute than it's content
elem.innerHTML = "world"; // OK - Content has been updated
elem.setAttribute("innerHTML", "world"); // NOT OK - You are trying to set
// the attribute than it's content
One difference between the two is that setAttribute, when used to set the value of an <input/> will make that the default value when you call .reset() on the form it's part of, but .value = will not do this.
https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/reset
Note that if setAttribute() is called to set the value of a particular attribute, a subsequent call to reset() won’t reset the attribute to its default value, but instead will keep the attribute at whatever value the setAttribute() call set it to.
If the element you are referring to does not already include a Javascript object property for a given attribute (as others have described), then setting that property will not propagate the change back to the DOM, it just adds the named property to the Javascript object, and the DOM ignores it. For example, getting the element mySpan by id and then doing mySpan.class = 'warning' will do nothing, whether or not the span element in question already has a class attribute defined, because mySpan.class is not defined in the Javascript object for a span element. You have to use mySpan.setAttribute('class', 'warning').
However a second nuance is that setting the Javascript object's innerHTML property using mySpan.setAttribute("innerHTML", someHTML) does not update the content of the element. I don't know how Javascript even catches mySpan.innerHTML = something, and calls the HTML parser, but there's some magic going on under the hood.

how to access properties of document.getElementsByClassName

I am trying to check whether the a css class is used inside the DOM or not. So, I have
var x = document.getElementsByClassName('classname');
When I print x out, I get a [object NodeList] for classes that exist on the page and classes that dont. Is there a property of x that I can access ? Like the tag name or something. Would be great if somebody can tell me the different properties of x and the ways I can access them.
Thank you :)
Notice that it's plural:
var x = document.getElementsByClassName('classname');
^
You need to iterate over x to get the individual elements:
for (var i = 0; i < x.length; i++) {
var element = x[i];
console.log(element);
}
Make sure to add fallback support for Internet Exploder: http://ejohn.org/blog/getelementsbyclassname-speed-comparison/
If you just want to check for the presence of a class in the document, you can also use querySelector.
var x = document.querySelector('.classname');
It returns null if no elements have that class, otherwise the first element with that class name. If you want all elements using classname:
var x = document.querySelectorAll('.classname');
Now x is null if the class is not used in the document, otherwise a Nodelist, containing all elements with class classname, which can be iterated the way Blender showed. In that iteration you can, for example, retrieve the elements tagName or its id. Like:
for (var i=0;i<x.lenght;(i=i+1)){
console.log(x[i].id + ': ' + x[i].tagName);
}
document.querySelector is available in all modern browsers, and for IE version 8 and up.
I almost always use document.querySelector (which: "Returns the first element within the document that matches the specified group of selectors"), which returns an element object and it's.
I don't know why but in my Chrome's console I write:
var img = document.getElementsByClassName('image__pic');
img[0]...
img[0], despite its happy existance, it doesn't generate any further attributes/methods to use in the completion window. Like there were none (even though I could use img[0].src for instance)
On the other hand:
var imgq = document.querySelector('.image__pic')
Gives me very useful autocompletion on the Console:
As far as its browser support it is phenomenal:
It is also less tricky to use, because getElementsByClassName returns an HTMLCollection, which is a little beast of its own.
Another plus for querySelector is its versatility: any kind of CSS selector goes!
On the negative side, querySelector is a bit slower, but I think it's worth it.

How to get all elements with a given property?

I'd like to get all <input /> elements with the placeholder property set, how could I do this without jquery? it should support IE6+, and other popular browsers.
Assuming all the fields have an ID
DEMO
var fieldsWithPlaceholder = [];
var inputs = document.getElementsByTagName("input");
for (var i=0, n=inputs.length;i<n;i++) {
if (inputs[i].getAttribute("placeholder") !=null) {
fieldsWithPlaceholder.push(inputs[i].id);
}
}
alert(fieldsWithPlaceholder);
Update: you can as posted elsewhere test for null, undefined or blank in one go:
if (inputs[i].getAttribute("placeholder")) { // there is something there
Use getElementsByTagName, filter for those with a placeholder property value that isn't undefined.
Incidentally, there is a big difference between attributes and properties, which are you really after? If you are setting an attribute called placeholder in the HTML, then you should use getAttribute. However, if you are setting the property of a DOM element (which is what I presumed from your post) then you should use the property directly. Hopefully you aren't mixing the two.
In some browsers, attributes and properties are kept synchronised but in others they aren't. For example, Firefox will not create element properties for non–standard attributes (i.e. those that aren't specified in the related markup standard), nor will it create or modify standard attributes based on property changes in versions that aren't consistent with HTML5.
get all the elements of type input first
document.getElementsByTagName("input")
loop over the above list and then use the
getAttribute on each of the element as follows getAttribute("placeholder") if it is not null or undefined then the input has it set.
Try something like this:
var myEls = [],
allEls = document.getElementsByTagName('input');
for (var i=0; i < allEls.length; i++) {
if (allEls[i].placeholder)
myEls.push(allEls[i]);
}

Select all elements with a "data-xxx" attribute without using jQuery

Using only pure JavaScript, what is the most efficient way to select all DOM elements that have a certain data- attribute (let's say data-foo).
The elements may be different, for example:
<p data-foo="0"></p><br/><h6 data-foo="1"></h6>
You can use querySelectorAll:
document.querySelectorAll('[data-foo]');
document.querySelectorAll("[data-foo]")
will get you all elements with that attribute.
document.querySelectorAll("[data-foo='1']")
will only get you ones with a value of 1.
document.querySelectorAll('[data-foo]')
to get list of all elements having attribute data-foo
If you want to get element with data attribute which is having some specific value e.g
<div data-foo="1"></div>
<div data-foo="2"></div>
and I want to get div with data-foo set to "2"
document.querySelector('[data-foo="2"]')
But here comes the twist ... what if I want to match the data attirubte value with some variable's value? For example, if I want to get the elements where data-foo attribute is set to i
var i=2;
so you can dynamically select the element having specific data element using template literals
document.querySelector(`[data-foo="${i}"]`)
Note even if you don't write value in string it gets converted to string like if I write
<div data-foo=1></div>
and then inspect the element in Chrome developer tool the element will be shown as below
<div data-foo="1"></div>
You can also cross verify by writing below code in console
console.log(typeof document.querySelector(`[data-foo="${i}"]`).dataset('dataFoo'))
why I have written 'dataFoo' though the attribute is data-foo reason dataset properties are converted to camelCase properties
I have referred below links:
MDN: data-*
MDN: Using data attributes
Try it → here
<!DOCTYPE html>
<html>
<head></head>
<body>
<p data-foo="0"></p>
<h6 data-foo="1"></h6>
<script>
var a = document.querySelectorAll('[data-foo]');
for (var i in a) if (a.hasOwnProperty(i)) {
alert(a[i].getAttribute('data-foo'));
}
</script>
</body>
</html>
Here is an interesting solution: it uses the browsers CSS engine to to add a dummy property to elements matching the selector and then evaluates the computed style to find matched elements:
It does dynamically create a style rule [...] It then scans the whole document (using the
much decried and IE-specific but very fast document.all) and gets the
computed style for each of the elements. We then look for the foo
property on the resulting object and check whether it evaluates as
“bar”. For each element that matches, we add to an array.
Native JavaScript's querySelector and querySelectorAll methods can be used to target the element(s). Use a template string if your dataset value is a variable.
var str = "term";
var term = document.querySelectorAll(`[data-type=${str}]`);
console.log(term[0].textContent);
var details = document.querySelector('[data-type="details"]');
console.log(details.textContent);
<dl>
<dt data-type="term">Thing</dt>
<dd data-type="details">The most generic type.</dd>
</dl>
var matches = new Array();
var allDom = document.getElementsByTagName("*");
for(var i =0; i < allDom.length; i++){
var d = allDom[i];
if(d["data-foo"] !== undefined) {
matches.push(d);
}
}
Not sure who dinged me with a -1, but here's the proof.
http://jsfiddle.net/D798K/2/
While not as pretty as querySelectorAll (which has a litany of issues), here's a very flexible function that recurses the DOM and should work in most browsers (old and new). As long as the browser supports your condition (ie: data attributes), you should be able to retrieve the element.
To the curious: Don't bother testing this vs. QSA on jsPerf. Browsers like Opera 11 will cache the query and skew the results.
Code:
function recurseDOM(start, whitelist)
{
/*
* #start: Node - Specifies point of entry for recursion
* #whitelist: Object - Specifies permitted nodeTypes to collect
*/
var i = 0,
startIsNode = !!start && !!start.nodeType,
startHasChildNodes = !!start.childNodes && !!start.childNodes.length,
nodes, node, nodeHasChildNodes;
if(startIsNode && startHasChildNodes)
{
nodes = start.childNodes;
for(i;i<nodes.length;i++)
{
node = nodes[i];
nodeHasChildNodes = !!node.childNodes && !!node.childNodes.length;
if(!whitelist || whitelist[node.nodeType])
{
//condition here
if(!!node.dataset && !!node.dataset.foo)
{
//handle results here
}
if(nodeHasChildNodes)
{
recurseDOM(node, whitelist);
}
}
node = null;
nodeHasChildNodes = null;
}
}
}
You can then initiate it with the following:
recurseDOM(document.body, {"1": 1}); for speed, or just recurseDOM(document.body);
Example with your specification: http://jsbin.com/unajot/1/edit
Example with differing specification: http://jsbin.com/unajot/2/edit

Categories

Resources