Ответ 1
Обновить
См. Keen answer, а также ccproj ответ на тесно связанный вопрос. Для этого isEqualNode
, но он сравнивает атрибуты class
и style
как текст, поэтому один и тот же набор классов или один и тот же набор свойств стиля в разных порядках заставит его думать, что узлы не эквивалентны. Ответ ccprog справляется с этим.
Оригинальный ответ
(См. Ниже полное, в значительной степени непроверенное и, конечно же, не подвергнутое рефакторингу решение "без подзарядки". Но сначала о кусочках.)
Сравнивать их innerHTML
легко:
if (divs[0].innerHTML === divs[1].innerHTML)
// or if you prefer using jQuery
if (divs.html() === $(divs[1]).html()) // The first one will just be the HTML from div 0
... хотя вы должны спросить себя, эквивалентны ли эти два элемента по вашим критериям:
<div><span class="foo" data-x="bar">x</span></div>
<div><span data-x="bar" class="foo">x</span></div>
... потому что их innerHTML
будет другим (по крайней мере, в Chrome, и я подозреваю, что в большинстве, если не во всех браузерах). (Подробнее об этом ниже.)
Тогда вам нужно сравнить все их атрибуты. Насколько я знаю, jQuery не дает вам средства перечисления атрибутов, но DOM делает:
function getAttributeNames(node) {
var index, rv, attrs;
rv = [];
attrs = node.attributes;
for (index = 0; index < attrs.length; ++index) {
rv.push(attrs[index].nodeName);
}
rv.sort();
return rv;
}
затем
var names = [getAttributeNames(div[0]), getAttributeNames(div[1])];
if (names[0].length === names[1].length) {
// Same number, loop through and compare names and values
...
}
Обратите внимание, что сортируя массивы выше, я предполагаю, что порядок их атрибутов не имеет значения в вашем определении "эквивалент". Я надеюсь, что это так, потому что, похоже, он не сохранился, так как я получаю разные результаты из разных браузеров при запуске этого теста. В этом случае мы должны вернуться к вопросу о innerHTML
, потому что, если порядок атрибутов на самих элементах незначителен, то, вероятно, порядок атрибутов на дочерних элементах не должен быть значимым. Если это так, вам понадобится рекурсивная функция, которая проверяет потомков в соответствии с вашим определением эквивалента и не использует innerHTML
вообще.
Затем возникает вопрос, связанный с этим последующим вопросом: что, если элементы имеют разные, но эквивалентные атрибуты style
? Например:
<div id="a" style="color: red; font-size: 28px">TEST A</div>
<div id="b" style="font-size: 28px; color: red">TEST B</div>
Мой ответ там обращается к нему, просматривая содержимое объектов style
элементов, например так:
const astyle = div[0].style;
const bstyle = div[1].style;
const rexDigitsOnly = /^\d+$/;
for (const key of Object.keys(astyle)) {
if (!rexDigitsOnly.test(key) && astyle[key] !== bstyle[key]) {
// Not equivalent, stop
}
}
// Equivalent
К сожалению, как я говорю в этом ответе:
Обратите внимание, что приведенное выше не выполнится, если (один из них имеет
color: red
а другой -color: #ff0000
), по крайней мере, в некоторых браузерах, потому что, когда свойство стиля использует строковое значение, обычно вы получаете значение таким, каким оно было поставляется, не нормируется. Вы можете использоватьgetComputedStyle
, чтобы получить вычисленное значение (МОГ) вместо этого, но тогда мы попадаем в вопросах вокруг CSS применения: Два элемента с точно такой же разметки может иметь различные значения изgetComputedStyle
из - за того, где они находятся в DOM и CSS применяется к их в результате. ИgetComputedStyle
не работает на узлах, которых нет в документе, поэтому вы не можете просто клонировать узлы, чтобы устранить эту проблему.
Но вы должны быть в состоянии собрать что-то вместе из приведенных выше фрагментов, чтобы сравнить два элемента в соответствии с вашими критериями.
Больше для изучения:
Вопрос странно интересовал меня, поэтому я немного подумала над ним и пришла к следующему. Он в основном не проверен, может использовать некоторый рефакторинг и т.д., Но он должен помочь вам в этом. Я, опять же, предполагаю, что порядок атрибутов не имеет значения. Ниже предполагается, что даже малейшая разница в тексте значительна.
function getAttributeNames(node) {
var index, rv, attrs;
rv = [];
attrs = node.attributes;
for (index = 0; index < attrs.length; ++index) {
rv.push(attrs[index].nodeName);
}
rv.sort();
return rv;
}
function equivElms(elm1, elm2) {
var attrs1, attrs2, name, node1, node2;
// Compare attributes without order sensitivity
attrs1 = getAttributeNames(elm1);
attrs2 = getAttributeNames(elm2);
if (attrs1.join(",") !== attrs2.join(",")) {
display("Found nodes with different sets of attributes; not equiv");
return false;
}
// ...and values
// unless you want to compare DOM0 event handlers
// (onclick="...")
for (index = 0; index < attrs1.length; ++index) {
name = attrs1[index];
if (elm1.getAttribute(name) !== elm2.getAttribute(name)) {
display("Found nodes with mis-matched values for attribute '" + name + "'; not equiv");
return false;
}
}
// Walk the children
for (node1 = elm1.firstChild, node2 = elm2.firstChild;
node1 && node2;
node1 = node1.nextSibling, node2 = node2.nextSibling) {
if (node1.nodeType !== node2.nodeType) {
display("Found nodes of different types; not equiv");
return false;
}
if (node1.nodeType === 1) { // Element
if (!equivElms(node1, node2)) {
return false;
}
}
else if (node1.nodeValue !== node2.nodeValue) {
display("Found nodes with mis-matched nodeValues; not equiv");
return false;
}
}
if (node1 || node2) {
// One of the elements had more nodes than the other
display("Found more children of one element than the other; not equivalent");
return false;
}
// Seem the same
return true;
}
Живые примеры: