#javascript #documentfragment
#javascript #getelementsbyclassname
Вопрос:
Возможно ли получить элементы, имеющие один из указанных классов? Это не то же самое, что получение элементов, которые имеют все указанные классы.
Например, я хочу захватить все элементы, список классов которых содержит либо one
, two
, либо three
.
Может быть, что-то вроде этого:
var oneTwoThree = document.getElementsByClassName("one, two, three");
Я также не хочу использовать jQuery. Это единственный вариант getElementsByClassName
для каждого класса и их объединения?
Ответ №1:
querySelector
принимает практически любой CSS-селектор:
var oneTwoThree = document.querySelectorAll('.one, .two, .three');
Комментарии:
1. Опередите меня на 40 секунд;-) Обратите внимание, если вам нужна поддержка IE7 или ниже, вам понадобится какая-то библиотека полизаполнения / селектора.
2. Ого, я не знал об этой функции. Большое вам спасибо. Я узнал кое-что новое.
Кстати, мне нужен только IE10 и выше, так что это работает хорошо.
3.
getElementsByClassName
является IE9 , поэтому я сомневаюсь, что требуется поддержка старого браузера4. Просто чтобы отметить для кого-то в будущем. Вот сравнение производительности между jQuery и querySelector jsperf.com/jquery-vs-queryselectorall/42
5. @Tibos Большое вам спасибо за то, что указали на это… Похоже, я сам голосую против. Я отредактировал свой комментарий.
Ответ №2:
Я также не хочу использовать jQuery. Это единственный вариант получить elementsbyclassname для каждого класса и объединить их?
Используя querySelectorAll
(как указано в ответе), он будет искать и выполнять объединение для вас. IE9 {IE8 (только для селекторов CSS2}
В противном случае, getElementsByClassName
это не единственный доступный вам метод, он даже недоступен в некоторых старых браузерах (IE9 , FF3 ).
Но практически любой другой метод, который вы могли бы использовать, потребовал бы объединения результатов. Итак, просто в качестве демонстрации я создал для вас несколько примеров.
Определите пробелы в соответствии с атрибутом класса HTML4.01
«Этот атрибут присваивает элементу имя класса или набор имен классов. Любому количеству элементов может быть присвоено одно и то же имя или имена класса. Несколько имен классов должны быть разделены пробелами. »
var whiteSpaces = '[u0009u000Au000Bu000Cu000Du0020u00A0u1680u180Eu2000u2001u2002u2003u2004u2005u2006u2007u2008u2009u200Au202Fu205Fu3000u2028u2029uFEFF]';
Определите символы пробела в соответствии с HTML5 3.2.5.7 в атрибуте класса
У каждого элемента HTML может быть указан атрибут class.
Атрибут, если он указан, должен иметь значение, представляющее собой набор разделенных пробелами токенов, представляющих различные классы, к которым принадлежит элемент.
Классы, которые присвоил ему элемент HTML, состоят из всех классов, возвращаемых при разделении значения атрибута class на пробелы. (Дубликаты игнорируются.)
Назначение классов элементу влияет на сопоставление классов в селекторах в CSS, метод getElementsByClassName() в DOM и другие подобные функции.
Нет никаких дополнительных ограничений на токены, которые авторы могут использовать в атрибуте класса, но авторам рекомендуется использовать значения, описывающие характер содержимого, а не значения, описывающие желаемое представление содержимого.
Атрибуты className и classList IDL, определенные в спецификации DOM, отражают атрибут содержимого класса. [DOM]»
var whiteSpaces = '[ nrtf]';
Определение начальных и конечных символов
var starts = '(^|' whiteSpaces ')',
ends = '(' whiteSpaces '|$)';
Кроссбраузерный (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;
}
Кроссбраузерный (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;
}
Современный браузер (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;
}
Современный браузер (IE9 )
function getElementsByClassName4(node, className) {
return Array.prototype.slice.call(document.getElementsByClassName(className));
}
Кроссбраузерный массив.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 amp;amp; searchElement === array[index]) {
val = index;
break;
}
}
}
}
return val;
}
Кроссбраузерный массив.фильтр
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;
};
Объединение результатов и поддержание порядка
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();
}
Тест
<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'));
В jsFiddle
И, наконец, сравнение производительности для вас, на jsPerf
Комментарии:
1. Просто для дальнейшего использования, вместо одного пробела в регулярном выражении, которое вы должны использовать
[ nrtf]
из-за того, как W3C определяет символы пробела . Или вы можете просто использоватьs
, чтобы упростить задачу.2. Извините, я был недостаточно ясен. Имена классов в элементе могут быть разделены «пробелами», как определено в W3C. Таким образом, вместо пробела между ‘A’ и ‘B’ в вашем тестовом div у вас может быть разрыв строки, и это все равно будет допустимый HTML. Это означает, что ваше регулярное выражение, использующее только символ пробела, завершится ошибкой в некоторых ситуациях, когда в имени класса элемента используются другие пробелы.
3. Проверьте изменения, которые я внес в ваш jsFiddle .
4. Да, я уже прошел через все это, создавая
hasClass()
функцию. И да, технически все это символы пробела, но вам нужно учитывать только те, которые указаны в спецификации HTML5: w3.org/TR/html5/single-page.html#space-character (как я уже говорил ранее).5. Хорошо, я вижу, что я просмотрел спецификацию HMT4.01 , в которой указаны все, а спецификация HTML5 сокращает этот список только до тех, которые вы упомянули. Или так кажется? Но обновили обе возможности.