What is the best method to retrieve an array of elements that have a certain class?
I would use document.getElementsByClassName but IE does not support it.
So I tried Jonathan Snook\'s solution:
function getElementsByClassName(node, classname) {
var a = [];
var re = new RegExp(\'(^| )\'+classname+\'( |$)\');
var els = node.getElementsByTagName(\"*\");
for(var i=0,j=els.length; i<j; i++)
if(re.test(els[i].className))a.push(els[i]);
return a;
}
var tabs = document.getElementsByClassName(document.body,\'tab\');
...but IE still says:
Object doesn\'t support this property or method
Any ideas, better methods, bug fixes?
I would prefer not to use any solutions involving jQuery or other \"bulky javascript\".
Update:
I got it to work!
As @joe mentioned the function is not a method of document
.
So the working code would look like this:
function getElementsByClassName(node, classname) {
var a = [];
var re = new RegExp(\'(^| )\'+classname+\'( |$)\');
var els = node.getElementsByTagName(\"*\");
for(var i=0,j=els.length; i<j; i++)
if(re.test(els[i].className))a.push(els[i]);
return a;
}
var tabs = getElementsByClassName(document.body,\'tab\');
...Also if you only need IE8+ support then this will work:
if(!document.getElementsByClassName) {
document.getElementsByClassName = function(className) {
return this.querySelectorAll(\".\" + className);
};
Element.prototype.getElementsByClassName = document.getElementsByClassName;
}
Use it just like normal:
var tabs = document.getElementsByClassName(\'tab\');
It\'s not a method of document:
function getElementsByClassName(node, classname) {
var a = [];
var re = new RegExp(\'(^| )\'+classname+\'( |$)\');
var els = node.getElementsByTagName(\"*\");
for(var i=0,j=els.length; i<j; i++)
if(re.test(els[i].className))a.push(els[i]);
return a;
}
tabs = getElementsByClassName(document.body,\'tab\'); // no document
you may create the function for older browsers
if (typeof document.getElementsByClassName!=\'function\') {
document.getElementsByClassName = function() {
var elms = document.getElementsByTagName(\'*\');
var ei = new Array();
for (i=0;i<elms.length;i++) {
if (elms[i].getAttribute(\'class\')) {
ecl = elms[i].getAttribute(\'class\').split(\' \');
for (j=0;j<ecl.length;j++) {
if (ecl[j].toLowerCase() == arguments[0].toLowerCase()) {
ei.push(elms[i]);
}
}
} else if (elms[i].className) {
ecl = elms[i].className.split(\' \');
for (j=0;j<ecl.length;j++) {
if (ecl[j].toLowerCase() == arguments[0].toLowerCase()) {
ei.push(elms[i]);
}
}
}
}
return ei;
}
}
function getElementsByClassName(className) {
if (document.getElementsByClassName) {
return document.getElementsByClassName(className); }
else { return document.querySelectorAll(\'.\' + className); } }
Pretty sure this is the same as Leonid\'s function but this uses document.getElementsByClassName
when it can.
You can\'t really replicate getElementsByClassName, because it returns a nodeList,
and so its value is live, and updates with the document.
You can return a static Array of elements who share the same classnames-
but it won\'t \'know\'when the document changes.
(It won\'t take too many of these kind of things to make a library look svelte...)
function getArrayByClassNames(classes, pa){
if(!pa) pa= document;
var C= [], G;
if(pa.getElementsByClassName){
G= pa.getElementsByClassName(classes);
for(var i= 0, L= G.length; i<L; i++){
C[i]= G[i];
}
}
else{
classes= classes.split(/\\s+/);
var who, cL= classes.length,
cn, G= pa.getElementsByTagName(\'*\'), L= G.length;
for(var i= 0; i<cL; i++){
classes[i]= RegExp(\'\\\\b\'+classes[i]+\'\\\\b\');
}
classnameLoop:
while(L){
who= G[--L];
cn= who.className;
if(cn){
for(var i= 0; i<cL; i++){
if(classes[i].test(cn)== false) {
continue classnameLoop;
}
}
C.push(who);
}
}
}
return C;
}
//Example
var A= getArrayByClassNames(\'sideBar local\')
IE8:
document.getElementsByClassName = function (className) {
return document.querySelectorAll(\'.\' + className)
}
function _getClass(whatEverClasNameYouWant){
var a=document.getElementsByTagName(\'*\');
for(b in a){
if((\' \'+a[b].className+\' \').indexOf(\' \'+whatEverClasNameYouWant+\' \')>-1){
return a[b];
}
}
}
I just want to improve querySelectorAll
fallback for IE8.
Like others answered, the simple way is adding the function to Element.prototype
with
this.querySelectorAll(\'.\' + className);
But there are some problems:
- It doesn\'t work with untrimmed strings (at the beginning).
- It doesn\'t work with multiple classes.
- It doesn\'t work with \"strange\" class characters (
/
, $
, *
, etc.)
- It doesn\'t work with classes which begin with a digit (invalid identifiers)
That means there should be some \"fixing\", for example:
\"abcd\" -> \".abcd\"
\"a b cd\" -> \".a.b.cd\"
\" a b \" -> \".a.b \"
\"a/b$c d\" -> \".a\\/b\\$c.d\"
\"1234\" -> \".\\000031234\"
Code:
this.querySelectorAll(className
.replace(/(?=[^ \\w])/g, \'\\\\\') // Escape non-word characters
.replace(/\\b\\d/g, \'\\\\00003$&\') // Escape digits at the beginning
.replace(/(^| +)(?!$| )/g, \'.\') // Add \".\" before classes, removing spaces
);