2015-07-28 2 views
1

В попытке сделать мой ответ более гибким на this вопрос:JavaScript InsertBefore не работает должным образом

function invertDivs(parentDiv) { 
 
    var first = document.getElementById(parentDiv).firstChild; 
 
    console.log(first); 
 
    var second = document.getElementById(parentDiv).lastChild; 
 
    console.log(second); 
 
    document.getElementById(parentDiv).insertBefore(second, first); 
 
}
<div id="parent"> 
 
    <div id="first">Div 1</div> 
 
    <div id="second">Div 2</div> 
 
</div> 
 
<button onclick="invertDivs('parent');">Invert Divs</button>

Однако дивы только перевернутый иногда, не всегда.

Начальный щелчок на выходах кнопки это на консоли:

enter image description here

Второй щелчок:

enter image description here

После кучу кликов:

enter image description here

Я смущен, что не так с кодом. Я выбираю первый родительский родительский div и делаю то же самое для последнего ребенка. Затем я просто вставляю текущий второй div перед первым. Это конец функции. Они также являются прямыми дочерними элементами родительского div, как требуется функцией insertBefore.

+2

Имейте в виду, что текстовые узлы также являются дочерними узлами. Я предполагаю, что вы хотите использовать ['firstElementChild'] (https://developer.mozilla.org/en-US/docs/Web/API/ParentNode/firstElementChild) или' childNodes [0] '. –

+1

Каждое из белых пространств в вашем «HTML» будет считаться «текстовым узлом». Вы можете не столкнуться с проблемой, когда избавляетесь от всех пробелов в своем HTML. Но это боль. Вместо этого вам нужно проверить «nodeValue». '1' - это узел элемента, а' 3' - текстовый узел. Поэтому вам нужно добавить дополнительные проверки, чтобы избежать обработки '1'''. –

ответ

5

Как указано в комментариях, firstChild и lastChild могут возвращать текстовые узлы для пробелов между элементами. Вы можете использовать firstElementChild и lastElementChild, чтобы игнорировать их.

function invertDivs(parentDiv) { 
 
    var first = document.getElementById(parentDiv).firstElementChild; 
 
    console.log(first); 
 
    var second = document.getElementById(parentDiv).lastElementChild; 
 
    console.log(second); 
 
    document.getElementById(parentDiv).insertBefore(second, first); 
 
}
<div id="parent"> 
 
    <div id="first">Div 1</div> 
 
    <div id="second">Div 2</div> 
 
</div> 
 
<button onclick="invertDivs('parent');">Invert Divs</button>

Для некоторых других обходных путей, которые могут вам понадобиться для старых браузеров, см element.firstChild is returning '<TextNode ...' instead of an Object in FF

+0

Даже не знал, что эти варианты элементов были добавлены - кажется, мне нужно освежить DOM. – crush

+0

@Barmar является 'firstElementChild' по существу' parent.children [0] 'под капотом? – Abdul

+0

@Abdul Похоже, что это так. – Barmar

3

Вы не принимаете во внимание текстовые узлы. В приведенном выше примере HTML имеется 5 узлов.

[0] => TextNode 
[1] => #first 
[2] => TextNode 
[3] => #second 
[4] => TextNode 

Кажется довольно очевидным, что здесь не нужны текстовые узлы. У вас есть несколько вариантов.

Один из вариантов - отфильтровать все текстовые узлы. (Нельзя использовать метод Array.prototype.filter, потому что childNodes не является массивом, а NodeList)

Это даст вам массив только элементов DOM.

function invertDivs(parentNodeId) { 
 
    var childElements = [], 
 
     parentNode = document.getElementById(parentNodeId); 
 
    
 
    //Filter out the child nodes that aren't elements. 
 
    //parentNode.childNodes is a NodeList, and not an array (even though it looks like one) 
 
    for (var i = 0; i < parentNode.childNodes.length; ++i) { 
 
    if (parentNode.childNodes[i].nodeType === 1) 
 
     childElements.push(parentNode.childNodes[i]); 
 
    } 
 
    
 
    parentNode.insertBefore(childElements[childElements.length - 1], childElements[0]); 
 
}
<div id="parent"> 
 
    <div id="first">Div 1</div> 
 
    <div id="second">Div 2</div> 
 
</div> 
 
<button onclick="invertDivs('parent');">Invert Divs</button>

Другим вариантом было бы использовать более современные свойства DOM API: См Barmar или GolezTrol ответами. Они будут гораздо более эффективными, если у вас есть поддержка браузеров IE9 +.

1

Это не случайно. Если я нажму 2 раза, чтобы добавить Div 2 в конец списка, нажмите 3 раза, чтобы получить Div 1 в конце списка. Эта картина повторяется.

Причина в том, что между ними также находятся следующие узлы. Это пробел между элементами.

Чтобы обойти это, используйте атрибут children. Это выбирает ребенка элементов (вместо узлов).

function invertDivs(parentDiv) { 
 
    var parent = document.getElementById(parentDiv); 
 
    var first = parent.children[0]; 
 
    console.log(first); 
 
    var second = parent.children[parent.children.length-1]; 
 
    console.log(second); 
 
    document.getElementById(parentDiv).insertBefore(second, first); 
 
}
<div id="parent"> 
 
    <div id="first">Div 1</div> 
 
    <div id="second">Div 2</div> 
 
</div> 
 
<button onclick="invertDivs('parent');">Invert Divs</button>

+0

является 'firstElementChild' по существу' parent.children [0] 'под капотом? – Abdul

+0

Да, но это не так, но возвращать 'null', когда нет ребенка. Таким образом, фактически 'firstElementChild' проще в использовании. Обратите внимание на совместимость браузеров. 'firstElementChild' доступен в IE9 +. 'children' уже был доступен в более ранних версиях, хотя у него была небольшая ошибка в IE8 и до этого: он также включал комментарии ... Если вы не заботитесь о IE8- (и не должны), то' firstElementChild' самый простой выбор. – GolezTrol

1

Ответ на ваш вопрос будет дан в документации MDN (https://developer.mozilla.org/en-US/docs/Web/API/Node/firstChild) для Node.firstChild. Если вы обратитесь к документам, вы поймете, почему вы получаете #text как первый узел.

+0

Вы правы, это объясняет это хорошо там – Abdul

Смежные вопросы