2013-05-08 4 views
0

Я практикую базовые навыки JS, создавая для себя маленькие упражнения. В этом случае у меня есть список <a> s внутри div. Цель упражнения - обернуть каждый <a> в div. В этом случае я использую replaceChild.Понимание «parentNode» неопределенной ошибки

Как ни странно (для меня по крайней мере) скрипт работает в течение первых трех звеньев, но после этого выдает ошибку:

Uncaught TypeError: Cannot read property 'parentNode' of undefined

Я не могу сказать, почему сценарий частично работает. Может ли кто-нибудь увидеть, что я здесь делаю неправильно? Вот код, я использую:

<!DOCTYPE html> 
<html lang="en"> 
<head> 
<meta charset="utf-8"> 

<style media="all"> 
div div {padding: 10px; background: #e7e7e7; margin: 5px;} 
</style> 

</head> 
<body> 

<div> 
    <a href="">link</a> 
    <a href="">link</a> 
    <a href="">link</a> 
    <a href="">link</a> 
    <a href="">link</a> 
    <a href="">link</a> 
</div> 

<script> 
var links = document.getElementsByTagName("a"); 
for (var i=0, ii=links.length; i<ii; i++) 
{ 
    var container = document.createElement("div"); 
    links[i].parentNode.replaceChild(container, links[i]); 
    container.appendChild(links[i]); 
} 
</script> 

</body> 
</html> 

и вот онлайн версия: http://codepen.io/anon/pen/Lpuky

Я пробовал несколько методов отладки, которые я знаю и читать об этом сообщении об ошибке, но не работали что здесь не так. Мне кажется смешным, что он работает для 3 из 6 ссылок.

+1

Быстрый совет: 'for (var i = links.length-1; i> = 0; i -)' избегает проблемы. Работа с обратной стороны обычно является хорошей идеей при работе с узлами. –

+0

Да, хорошая точка. Это также появилось в комментариях ниже.:) –

ответ

2

Вы изменяете нодлист, когда вы перебираете его. Используйте метод среза массива, чтобы сделать копию списка:

var linksCopy = Array.prototype.slice.call(links); 
for (var i=0; i<linksCopy.length; i++) 
{ 
    var container = document.createElement("div"); 
    linksCopy[i].parentNode.replaceChild(container, linksCopy[i]); 
    container.appendChild(linksCopy[i]); 
} 
+0

Спасибо за объяснение и ответ. Это хорошо работает. Выглядит довольно сложно. Я с нетерпением жду возможности узнать больше о «прототипе» и т. Д. Из интереса, есть ли более простой вариант? (Это не критика, заметьте, я просто наслаждаюсь процессом обучения здесь.) –

+0

@ ralph.m Да, это немного лишнее. Просто зацикливайте назад (от длины коллекции минус 1, до 0). – Ian

+0

Спасибо Яну. Я настолько безнадежно новичок в этом, что не совсем уверен, как это сделать. Будет ли это связано с изменением цикла 'for' на декремент, или это работает с той же проблемой? –

4

Сбор links составляет NodeList и является активным.

Поскольку вы заменяете их, они исчезают из коллекции, и наш индекс в них больше не указывает ни на что.

+0

А, я вижу. В этом есть смысл. Спасибо за объяснение. –

1

В продолжение этого, я часто слышу, что querySelectorAll() отличается тем, что он возвращает статический Nodelist, а не массив, так что я подумал, что может пригодиться здесь, и это действительно делает:

<!DOCTYPE html> 
<html lang="en"> 
<head> 
<meta charset="utf-8"> 

<style media="all"> 
div div {padding: 10px; background: #e7e7e7; margin: 5px;} 
</style> 

</head> 
<body> 

<div> 
    <a href="">link</a> 
    <a href="">link</a> 
    <a href="">link</a> 
    <a href="">link</a> 
    <a href="">link</a> 
    <a href="">link</a> 
</div> 

<script> 
var links = document.querySelectorAll("a"); 
for (var i=0, ii=links.length; i<ii; i++) 
{ 
    var container = document.createElement("div"); 
    links[i].parentNode.replaceChild(container, links[i]); 
    container.appendChild(links[i]); 
} 
</script> 

</body> 
</html> 

Кроме того, альтернатива Array.prototype.slice.call(links) является [].slice.call(links):

<!DOCTYPE html> 
<html lang="en"> 
<head> 
<meta charset="utf-8"> 

<style media="all"> 
div div {padding: 10px; background: #e7e7e7; margin: 5px;} 
</style> 

</head> 
<body> 

<div> 
    <a href="">link</a> 
    <a href="">link</a> 
    <a href="">link</a> 
    <a href="">link</a> 
    <a href="">link</a> 
    <a href="">link</a> 
</div> 

<script> 
var links = document.getElementsByTagName("a"); 
var linksCopy = [].slice.call(links); 
for (var i=0; i<linksCopy.length; i++) 
{ 
    var container = document.createElement("div"); 
    linksCopy[i].parentNode.replaceChild(container, linksCopy[i]); 
    container.appendChild(linksCopy[i]); 
} 
</script> 

</body> 
</html> 

и еще один вариант усиления заключается в использовании [].forEach.call():

<!DOCTYPE html> 
<html lang="en"> 
<head> 
<meta charset="utf-8"> 

<style media="all"> 
div div {padding: 10px; background: #e7e7e7; margin: 5px;} 
</style> 

</head> 
<body> 

<div> 
    <a href="">link</a> 
    <a href="">link</a> 
    <a href="">link</a> 
    <a href="">link</a> 
    <a href="">link</a> 
    <a href="">link</a> 
</div> 

<script> 
[].forEach.call(document.querySelectorAll('a'), function(el) { 
var container = document.createElement("div"); 
el.parentNode.replaceChild(container, el); 
container.appendChild(el); 
}); 
</script> 

</body> 
</html> 

Еще один вариант, используя Array.from():

<!DOCTYPE html> 
<html lang="en"> 
<head> 
<meta charset="utf-8"> 

<style media="all"> 
div div {padding: 10px; background: #e7e7e7; margin: 5px;} 
</style> 

</head> 
<body> 

<div> 
    <a href="">link</a> 
    <a href="">link</a> 
    <a href="">link</a> 
    <a href="">link</a> 
    <a href="">link</a> 
    <a href="">link</a> 
</div> 

<script> 
var links = document.getElementsByTagName("a"); 
var linksCopy = Array.from(links); 
for (var i=0; i<linksCopy.length; i++) 
{ 
    var container = document.createElement("div"); 
    linksCopy[i].parentNode.replaceChild(container, linksCopy[i]); 
    container.appendChild(linksCopy[i]); 
} 
</script> 

</body> 
</html> 
1

Что касается вашего собственного последующего ответа: если ваша цель просто найти самый простой способ обернуть <a> с в <div> с, а не на практике с createElement, replaceChild или appendChild или любой из других методов, это было бы его:

<!DOCTYPE html> 
<html> 
<head> 
    <meta charset="utf-8"> 
    <title>Demo</title> 
<style> 
div div { 
    padding: 10px; 
    background: #e7e7e7; 
    margin: 5px; 
} 
</style> 
</head> 
<body> 

    <div> 
     <a href="#">link</a> 
     <a href="#">link</a> 
     <a href="#">link</a> 
     <a href="#">link</a> 
     <a href="#">link</a> 
     <a href="#">link</a> 
    </div> 

<script> 
var links = document.querySelectorAll("a"); 
for (var i=0; i<links.length; i++) { 
    links[i].outerHTML = '<div>'+links[i].outerHTML+'</div>'; 
} 
</script> 
</body> 
</html> 

.
Демо-версия здесь: http://jsbin.com/jasoho/1/edit?html,output. Другим преимуществом метода outerHTML является то, что он не меняет nodeList. Таким образом, вы также можете использовать getElementsByTagName вместо querySelectorAll.

+0

Спасибо, Фрэнк. Извините, я пропустил этот ответ, когда вы его разместили. Хорошая работа. :-) –

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