I also do not want to use jQuery. Is the only option to getElementsByClassName for each class and combine them?
By using querySelectorAll
(as answered), it will search and do the combining for you.
IE9+ {IE8 (CSS2 selectors only}
Otherwise, getElementsByClassName
is not the only method available to you, it's not even available on some older browsers (IE9+, FF3+).
But pretty much any other method that you could use would require that you combine the results. So, just as a demonstration I have created a few examples for you.
Define the white spaces as per HTML4.01 class attribute
"This attribute assigns a class name or set of class names to an element. Any number of elements may be assigned the same class name or names. Multiple class names must be separated by white space characters."
var whiteSpaces = '[\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029\uFEFF]';
Define the space characters as per HTML5 3.2.5.7 The class attribute
Every HTML element may have a class attribute specified.
The attribute, if specified, must have a value that is a set of space-separated tokens representing the various classes that the element belongs to.
The classes that an HTML element has assigned to it consists of all the classes returned when the value of the class attribute is split on spaces. (Duplicates are ignored.)
Assigning classes to an element affects class matching in selectors in CSS, the getElementsByClassName() method in the DOM, and other such features.
There are no additional restrictions on the tokens authors can use in the class attribute, but authors are encouraged to use values that describe the nature of the content, rather than values that describe the desired presentation of the content.
The className and classList IDL attributes, defined in the DOM specification, reflect the class content attribute. [DOM]"
var whiteSpaces = '[ \n\r\t\f]';
Define start an end echaratcers
var starts = '(^|' + whiteSpaces + ')',
ends = '(' + whiteSpaces +'|$)';
Cross browser (DOM walker)
function walkTheDOM(node, func) {
func(node);
node = node.firstChild;
while (node) {
walkTheDOM(node, func);
node = node.nextSibling;
}
}
function getElementsByClassName1(node, className) {
var regex = new RegExp(starts + className + ends),
results = [];
walkTheDOM(node, function (currentNode) {
if (regex.test(currentNode.className)) {
results.push(currentNode);
}
});
return results;
}
Cross browser (getElementsByClassName)
function getElementsByClassName2(node, className) {
var array = [],
regex = new RegExp(starts + className + ends),
elements = node.getElementsByTagName("*"),
length = elements.length,
i = 0,
element;
while (i < length) {
element = elements[i];
if (regex.test(element.className)) {
array.push(element);
}
i += 1;
}
return array;
}
Modern browser (IE9+)
function getElementsByClassName3(node, className) {
var results = [],
treeWalker = document.createTreeWalker(
node,
NodeFilter.SHOW_ELEMENT, {
acceptNode: function (thisNode) {
var accept = NodeFilter.FILTER_SKIP;
if (thisNode.classList.contains(className)) {
accept = NodeFilter.FILTER_ACCEPT;
}
return accept;
}
}, false);
while (treeWalker.nextNode()) {
results.push(treeWalker.currentNode);
}
return results;
}
Modern browser (IE9+)
function getElementsByClassName4(node, className) {
return Array.prototype.slice.call(document.getElementsByClassName(className));
}
Cross browser Array.indexOf
function indexOf(array, searchElement, fromIndex) {
var length = array.length,
val = -1,
index;
if (length !== 0) {
if (arguments.length > 2) {
fromIndex = fromIndex >> 0;
} else {
fromIndex = 0;
}
if (fromIndex < length) {
if (fromIndex < 0) {
fromIndex = length - Math.abs(fromIndex);
}
if (fromIndex < 0) {
fromIndex = 0;
}
for (index = fromIndex; index < length; index += 1) {
if (index in array && searchElement === array[index]) {
val = index;
break;
}
}
}
}
return val;
}
Cross browser Array.filter
function filter(array, fn, thisArg) {
var length = array.length,
arr = [],
index,
element;
for (index = 0; index < length; index += 1) {
if (index in array) {
element = array[index];
if (fn.call(thisArg, element, index, array)) {
arr.push(element);
}
}
}
return arr;
};
Combining the results and maintaining order
function getElements(node, classes, func) {
if (typeof classes === 'string') {
classes = classes.split(/\s*,\s*/);
}
var length = classes.length,
results = [],
index,
name;
for (index = 0; index < length; index += 1) {
name = classes[index];
if (name.charAt(0) === '.') {
name = name.slice(1);
}
results = results.concat(func(node, name));
}
return filter(results.reverse(), function (element, index, arr) {
return index <= indexOf(arr, element);
}).reverse();
}
Test
<div class='A'></div>
<div class='B'></div>
<div class='A B'></div>
<div class='C'></div>
console.log(getElements(document, '.A, .B', getElementsByClassName1));
console.log(getElements(document, '.A, .B', getElementsByClassName2));
console.log(getElements(document, '.A, .B', getElementsByClassName3));
console.log(getElements(document, '.A, .B', getElementsByClassName4));
console.log(document.querySelectorAll('.A, .B'));
On jsFiddle
And finally a performace comparison for you, on jsPerf