element.classList is of DOMTokenList type.
Is there a method to clear this list?
I'm not aware of a "method" in the sense of a "function" associated with classList. It has .add() and .remove() methods to add or remove individual classes, but you can clear the list in the sense of removing all classes from the element like this:
element.className = "";
With ES6 and the spread operator, this is a breeze.
element.classList.remove(...element.classList);
This will spread the list items as arguments to the remove method.
Since the classList.remove method can take many arguments, they all are removed and the classList is cleared.
Even though it is readable it is not very efficient. #Fredrik Macrobond's answer is faster.
View different solutions and their test results at jsperf.
var classList = element.classList;
while (classList.length > 0) {
classList.remove(classList.item(0));
}
Here's another way to do it:
element.setAttribute("class", "")
Nowadays, classList is preferred to (remove|set)Attribute or className.
Pekaaw's answer above is good, 1 similar alternative is to set the DomTokenList.value
elem.classList.value = ''
Another option is to simply remove the class attribute:
elem.removeAttribute('class')
I recommend not using className as classList could result in faster DOM updates.
The remove() method of DOMTokenList (which is what classList is) can take multiple arguments - each a string of a classname to remove (reference). First you need to convert the classList to a plan array of classnames. Some people use Array.prototype.slice() to do that, but I'm not a fan and I think a for loop is faster in most browsers - but either way I have nothing against for loops and the slice often feels like a hack to me. Once you have the array you simply use apply() to pass that array as a list of arguments.
I use a utility class I wrote to accomplish this. The first method is the one you are looking for, but I'm including slightly more for your reference.
ElementTools.clearClassList = function(elem) {
var classList = elem.classList;
var classListAsArray = new Array(classList.length);
for (var i = 0, len = classList.length; i < len; i++) {
classListAsArray[i] = classList[i];
}
ElementTools.removeClassList(elem, classListAsArray);
}
ElementTools.removeClassList = function(elem, classArray) {
var classList = elem.classList;
classList.remove.apply(classList, classArray);
};
ElementTools.addClassList = function(elem, newClassArray) {
var classList = elem.classList;
classList.add.apply(classList, newClassArray);
};
ElementTools.setClassList = function(elem, newClassArray) {
ElementTools.clearClassList(elem);
ElementTools.addClassList(elem, newClassArray);
};
Please note that I have not thoroughly tested this in all browsers as the project I am working on only needs to work in a very limited set of modern browsers. But it should work back to IE10, and if you include a shim (https://github.com/eligrey/classList.js) for classList, it should work back to IE7 (and also with SVGs since Eli Grey's shim adds support for SVG in unsupported browsers too).
An alternative approach I considered was to loop backwards through the classList and call remove() on classList for each entry. (Backwards because the length changes as you remove each.) While this should also work, I figured using the multiple arguments on remove() could be faster since modern browsers may optimize for it and avoid multiple updates to the DOM each time I call remove() in a for loop. Additionally both approaches require a for loop (either to build a list or to remove each) so I saw no benefits to this alternative approach. But again, I did not do any speed tests.
If somebody tests speeds of the various approaches or has a better solution, please post.
EDIT: I found a bug in the shim which stops it from correctly adding support to IE11/10 for multiple arguments to add() and remove(). I have filed a report on github.
Related
I'm working on an existing web page that has all sorts of javascript that I'm not able to edit. I only have access to a certain part of the page to do my stuff. Now the problem is using jQuery. The previous developer had modified the getElementsByClassName method with a custom version, which I'm assuming is kind of polyfill for IE. But this breaks jQuery that uses getElementsByClassName on supported browsers.
Now how may I revert getElementsByClassName to its original version before my code is executed. I can't find the original method anywhere online. Not using jQuery is not really an option as I'm trying to integrate a big piece of code written with jQuery.
Thanks.
Since the prototype chain of document wasn't altered, you could restore it by deleting the current implementation, as mentioned in the comments:
delete document.getElementsByClassName;
Demo
That will make the implementation available again via the prototype chain.
Old answer
You could try to restore it with this hack:
document.getElementsByClassName = document.documentElement.getElementsByClassName
.bind(document.documentElement);
I'm not certain whether it has any downsides, though.
If you need an implementation of the getElementsByClassName(), you may write something like this:
document.getElementsByClassName=function (theclass) {
els=document.getElementsByTagName("*")
temp=[]
for (var k=0; k<els.length; k++) {
if(els[k].className==theclass){
temp.push(els[k])
}
}
return temp;
}
And there is a good discussion here that you may refer to: http://www.webdeveloper.com/forum/showthread.php?256068-Need-object-getElementsByClassName-snippet
EDIT, taking Jack's first comment on this answer into account, as well as the edited answer by him:
Deleting getElementsByClassName from the prototype chain and thus automatically reverting back to the original function apparently works in itself, but that does leave IE8, which does not support getElementsByClassName.
This script comes very close to the original function, takes multiple classes into account, now deals with hyphens correctly, and makes IE8 support it as well:
document.getElementsByClassName = function(theClass) {
var elemArray = [];
var elems = this.getElementsByTagName('*');
for (var i=0; i<elems.length; i++) {
var allClasses = elems[i].className;
var classRegex = new RegExp('^('+theClass+')$|(\\s'+theClass+'\\b)');
// pattern demo and explanation on http://regex101.com/r/pP8nS2
if (classRegex.test(allClasses) == true)
elemArray.push(elems[i]);
}
return elemArray;
}
Live whole-page demo on http://codepen.io/anon/pen/Hhswl?editors=100, regex pattern demo and explanation on http://regex101.com/r/pP8nS2.
The only limitation I know is that you cannot use this script in the concatenated form of document.getElementById(id).getElementsByClassName(class). You should use document.getElementById(id).querySelectorAll(.class) for that, which is also supported by IE8 (not 7). Mind the dot.
For example:
var inputs = document.querySelectorAll('input[type="checkbox"]');
I want to set the checked to true on all inputs. Can this be done like in jQuery, without using a for loop?
querySelectorAll returns a NodeList. You will need to iterate over the list to change each item in it (jQuery loops over the entire collection too in the background using .each)
You can either use a loop (for, while, etc) or you could use the forEach method on the Array prototype
https://developer.mozilla.org/en-US/docs/DOM/NodeList#Workarounds
No, this cannot be done without some form of iteration over the nodes. jQuery iterates over the nodes as well, it just hides it from the user.
The closest that comes to jQuery-like style is using .forEach [MDN] in browser where it is supported:
[].forEach.call(inputs, function() {
this.checked = true;
});
without using a for loop
At some point, you're going to need some form of loop. Are you just hoping to abstract it?
NodeLists are Array-like, so you can call or apply methods from Array.prototype, including iterators like forEach(). An example from MDN:
Another approach without extending the DOM:
var forEach = Array.prototype.forEach;
var links = document.getElementsByTagName('a');
forEach.call(links, function(link){ // works in most browsers (please document here if you find browsers where it doesn't)
link.style.color = '#0F0';
});
But note that even forEach() depends on a loop (while in this case).
Using pure JavaScript you can use the querySelectorAll() function as below:
var checkboxes = document.querySelectorAll('input[type="checkbox"]');
for(var i=0; i<checkboxes.length; i++) { checkboxes[i].checked = true; }
Unfortunately this doesn't avoid looping which I know you wanted to avoid and It also doesn't have perfect cross browser compatibility as can be checked here with IE 8 only being able to hook on to CSS 2.1 selectors and Pre IE8 not being supported at all but it's the best that can be done without using lots of code and avoiding jQuery.
I know you want to avoid jQuery but, if you change your mind you can try the following:
$('input[type="checkbox"]').attr('checked', 'checked')
This should hook on to any checkbox input and set its attribute to checked using the jQuery attr() function.
If this doesn't work try giving your checkboxes their own class name such as checkbox and try like as follows:
$('.checkbox').attr('checked', 'checked')
This would help with compatibility issues and would avoid looping but if you don't want to go the jQuery route then please ignore.
I have a javascript function MyFunc() that does what it has to do with id="item_for_MyFunc".
In the function there is a
var elem = document.getElementById("item_for_MyFunc");
and html body has:
<body onload="MyFunc()">
<p id="item_for_MyFunc">
Everything works, but I want to use this function for several items on the page. That's why I should use <p class="item_for_MyFunc"> several times on the page to be processed by this function, so using id is not a solution.
What changes in the function should be done so it could be changed for class="item_for_MyFunc"?
So what you're doing there is pretty simple. Let me give you a slightly more robust version:
document.addEventListener('load', function(){
var elements = document.querySelectorAll('.myClassName');
for(var i=0, len=elements.length; i<len; i++){
MyFunc.call( elements[i] );
}
}, false);
So old versions of IE, 6 and 7 to be specific, don't have querySelectorAll. I'm assuming you aren't worried about them. If you are, there's other ways to do this and I can show you how if you need.
Basically we're giving all of your elements a class of 'myClassName', and querySelectorAll finds all of them and returns an array of DOM elements.
We then iterate through the list, and execute MyFunc on each of those elements.
Edit
So one key principal of writing good javascript is to separate the js code from your html markup as much as possible. You should almost never use inline event handlers like onload="myFunc" anymore. Use event handlers written in the js itself.
If you have the option, you should use the jQuery library. It makes a lot of this stuff incredibly easy, has great support for very old browsers, and is used on hundreds of thousands of websites.
document.getElementsByClassName
would return an array of all HTML elements using the class name. Iterate over the results and you are set. Supported in all browsers except IE <= 8, FF < 3. Works just like document.querySelectorAll (works in IE >= 7, FF >=3.5)
Refer:
http://quirksmode.org/dom/w3c_core.html#gettingelements
for compatibility chart.
You could try:
var elems = document.getElementsByClassName('myClass');
I've not used that one before, but you could use jQuery as that's even simpler;
var elems = $('.myClass');
var paras = document.getElementsByClassName('hi');
for (var i = 0; i < paras.length; i++) {
paras[i].style.color = '#ff0011';
// $('.hi').remove();
}
<p class="hi">dood</p>
<p class="hi">dood</p>
<p class="hi">dood</p>
<p class="hi">dood</p>
<p class="hi">dood</p>
<p>not class 'hi'</p>
In jQuery, this would be very easy: $('.hi').remove();. I want to learn JS, and then jQuery.
I am stuck and Google has not provided. I do not want to become a copy/paste jQuery programmer. I am just starting to learn JS. Thanks.
To remove an element you do this:
el.parentNode.removeChild(el);
MDN is a great reference. Here are a few relevant pages:
Node
parentNode
removeChild
However you'll run into issues if you loop like that since getElementsByClassName returns a live list, when you remove a node the element is removed from the list as well so you shouldn't increment or you will end up skipping every other element. Instead just continually remove the first element in the list, until there is no first element:
var paras = document.getElementsByClassName('hi');
while(paras[0]) {
paras[0].parentNode.removeChild(paras[0]);
}
IMO jQuery is great at showing you what is possible to do in Javascript. I actually recommend that after about a week or two of plain JS you learn jQuery, get good at it, and remember what it's abstracting away. One day when you have an excellent grasp of Javascript scoping, objects, and such which you can obtain while using jQuery, go back and try learning how to interact better with the DOM without a library. That way you'll have an easier time learning plain JS and you'll appreciate the abstraction that libraries can provide you even more, while learning that when you only need one or two things a library provides you may be able to write them yourself without including the entire library.
Simple ES6 answer:
document.querySelectorAll('.classname').forEach(e => e.remove());
Explanation:
document.querySelectorAll() loops through the elements in the document returning a NodeList of all elements with the specified selector (e.g. '.class', '#id', 'button')
forEach() loops through the NodeList and executes the specified action for each element
e => e.remove() removes the element from the DOM
Note, however, that this solution is not supported by Internet Explorer. Then again, nothing is.
[].forEach.call(document.querySelectorAll('.hi'),function(e){
e.parentNode.removeChild(e);
});
Here I'm using Array.prototype.forEach to easily traverse all elements in an array-like object, i.e. the static NodeList returned by querySelectorAll, and then using removeChild() to remove the item from the DOM.
For older browsers that don't support querySelectorAll() or forEach():
var classMatcher = /(?:^|\s)hi(?:\s|$)/;
var els = document.getElementsByTagName('*');
for (var i=els.length;i--;){
if (classMatcher.test(els[i].className)){
els[i].parentNode.removeChild(els[i]);
}
}
Note that because getElementsByTagName returns a live NodeList, you must iterate over the items from back to front while removing them from the DOM.
There are also some older browsers that don't support querySelectorAll but that do support getElementsByClassName, which you could use for increased performance and reduced code.
Afaik only a parent can remove a child in native JS. So you would first have to get that elements parent, then use the parent to remove the element. Try this:
var parent = paras[i].parentNode;
parent.removeChild(paras[i]);
I have a snippet of code like this:
var profileLinks = new Array();
for (var i = 0; i<searchResult.length; ++i)
{
var profileLink=searchResult[i].getElementsByTagName("a");
profileLinks[i]=profileLink[0].href;
alert(i+1+" of "+searchResult.length+" "+profileLinks[i]);
}
It seems like I should be able to make it more concise by using this:
//alternate construction (why doesn't this work?)
var searchResult = document.getElementsByClassName("f_foto").getElementsByTagName("a");
What's wrong here?
Use querySelectorAll() instead:
var searchResult = document.querySelectorAll(".f_foto a");
IE 8 supports querySelectorAll() but not getElementsByClassName(), so this should give you better compatibility too.
For full compatibility, stick to your original code or use a library like jQuery.
document.getElementsByClassName("f_foto")
returns a selection, therefore you cannot chain functions to it. You need to specify an element directly not a whole selection, for example this would work correctly.
document.getElementsByClassName("f_foto")[0].getElementsByTagName("a");
Because document.getElementsByClassName("f_foto")[0] points to an object and not to a selection of objects.
This is why we have libraries - or even modern browsers. You are looking for the css selector $('.f_foto a') in jQuery, or $$('.f_foto a') in Prototoype/Chrome
You call getElementsByTagName on a node, not an array, which is what is returned by getElementsByClassName.
I believe that getElementsByTagName can only be applied to an element node, but the result of getElementsByClassName is surely going to be a list of nodes. You'll either have to pick one ([0]?) or iterate over the collection (make sure it's not empty!).