От автора: в этой статье мы узнаем все о том, как обходить элементы DOM в JavaScript, а также вносить в них изменения. Мы продолжаем тему, начатую в статье Изучаем DOM! (Часть 1). Итак, начнем!
Обход DOM
Когда мы проходим DOM, мы по сути перемещаемся по нему. Мы работаем с родительскими дочерними и одноуровневыми свойствами, чтобы применить JavaScript к элементам DOM. Мы будем использовать новый пример, который приведен ниже:
<!DOCTYPE html> <html> <head> <title>Traversing the DOM</title> </head> <body> <h1>Traversing the DOM</h1> <p>Let's work with this example to <strong>MASTER</strong> the DOM!</p> <h2>List of Items..</h2> <ul> <li>Pizza</li> <li>Burgers</li> <li>Doritos</li> </ul> </body> <script> const h1 = document.getElementsByTagName('h1')[0]; const p = document.getElementsByTagName('p')[0]; const ul = document.getElementsByTagName('ul')[0]; </script> </html>
Наш файл выглядит в браузере так:
Корневые узлы
Объект document является корнем каждого узла в DOM. Следующий уровень — это объект window, который включает в себя такие элементы, как вкладки браузера, панели инструментов, командные строки и оповещения. Мы будем работать с DOM и, следовательно, с объектом document, который состоит из того, что находится внутри window.
По умолчанию каждый документ содержит элементы html, head и body. Проверьте содержимое каждого из них, запустив в консоли следующее:
document.head; // ► <head>...</head> document.body; // ► <body>...</body>
Родительские узлы
Как упоминалось ранее, узлы в DOM называются родительскими, дочерними и одноуровневыми, в зависимости от их отношения к другим узлам. Родителем любого конкретного узла является узел, который находится на один уровень выше его в иерархии DOM. В нашем примере:
html является родителем head, body и script.
body является родителем h1, h2, p и ul, а не li так как li на два уровня ниже от body.
Мы можем проверить родителя элемента p, например, с помощью свойства parentNode. Поскольку мы присвоили p переменной, все что нам нужно сделать, это набрать:
p.parentNode; // ► <body>...</body>
Мы можем даже подняться на два уровня:
p.parentNode.parentNode; // ► <html>...</html>
Дочерние узлы
Дочерними узлами являются узлы, которые находятся на один уровень ниже. Любые узлы за пределами одного уровня вложенности обычно называются дочерними. Есть целый ряд таких свойств, с которыми мы часто работаем: childNodes, firstChild, lastChild, children, firstElementChildи lastElementChild. Начнем со свойства childNodes, оно возвращает список всех дочерних элементов данного узла:
ul.childNodes // ► NodeList(7) [text, li, text, li, text, li, text]
Возможно, вы ожидаете, что будут возвращены только три li? Узлы text фактически являются пробелами для установки отступов между элементами — которые DOM рассматривает в качестве узлов. Вы не можете видеть их на вкладке Элементы, так как Dev Tools автоматически удаляет эти узлы.
По этой причине, если мы попытаемся изменить цвет фона, например, первого дочернего узла, произойдет сбой, так как первый дочерний элемент является текстом.
ul.firstChild.style.background = "purple"; // output: Uncaught TypeError: Cannot set property 'background' of undefined
Когда мы хотим работать только с узлами элементов, нам нужно использовать свойства children, firstElementChild и lastElementChild. ul.children вернул бы только три элемента li. И чтобы изменить только первый li, нам нужно использовать:
ul.firstElementChild.style.background = 'purple';
Вот наша обновленная страница:
Если мы хотим изменить все элементы children, мы могли бы использовать цикл for…of следующим образом:
for (let element of ul.children) { element.style.background = 'purple'; }
Теперь для всех дочерних элементов цвет фона изменен:
Если мы посмотрим на элемент p, то увидим, что он содержит как текст, так и другие элементы (тег strong).
for (let element of p.childNodes) { console.log(element); } // output: "Let's work with this example to " <strong>MASTER</strong> " the DOM!"
Свойство childNodes полезно, когда мы хотим получить доступ к этой информации. И childNodes, и children не возвращают стандартные массивы JavaScript (со всеми связанными с ними свойствами и методами), а скорее объекты, подобные массивам. Однако, как и в случае с массивом, вы можете получить доступ к узлам по номеру индекса или даже найти их свойство length.
document.body.children[3].lastElementChild.style.background = 'pink';
С помощью этого кода мы найдем последний элемент четвертого дочернего li элемента ul из body и применим стиль…
Используя свойства parent и child, вы можете получить любой узел в DOM!
Узлы одного уровня
Давайте теперь рассмотрим одноуровневые узлы. Одноуровневый узел — это любой узел на одном уровне дерева в DOM. Они не обязательно должны быть одного и того же типа — узлы текста, элементы и комментарии могут быть одноуровневыми. Свойства, которые часто используются — nextSibling, previousSibling, nextElementSibling и previousElementSibling.
Свойства одноуровневого узла работают так же, как и для дочерних узлов: previousSibling и nextSibling получат узел, который непосредственно предшествует или следует за указанным узлом, previousElementSibling и nextElementSibling получат только узлы элемента. Давайте выберем средний элемент из нашего ul:
const burger = ul.children[1];
И давайте используем свойства Sibling для доступа к следующему и предыдущему элементам (не включая пробелы):
burger.nextElementSibling.style.background = 'orange'; burger.previousElementSibling.style.background = 'green';
Эти изменения в действии:
Внесение изменений в DOM
Давайте теперь посмотрим, как мы можем добавлять, изменять, заменять и удалять узлы из DOM. Мы рассмотрим конкретные методы createElement() и createTextNode(), а также node.textContentи node.innerHTML.
Создание новых узлов
В этом разделе мы будем работать со следующим HTML:
<!DOCTYPE html> <html lang="en"> <head> <title>Master the DOM!</title> </head> <body> <h1>Master the DOM!</h1> </body> </html>
Откройте вкладку Консоль в Инструментах разработчика. Давайте используем метод createElement() объекта document для создания нового элемента p.
const paragraph = document.createElement('p'); console.log(paragraph); // <p></p>
Отлично! Переменная paragraph дает нам элемент p. Теперь мы можем добавить текст к элементу с помощью свойства textContent:
paragraph.textContent = "I'm a paragraph!"; console.log(paragraph); // <p>I'm a paragraph!</p>
Таким образом, комбинируя createElement() и textContent мы можем создать узел элемента. Мы также можем использовать свойство innerHTML для добавления содержимого в элемент. С помощью этого свойства мы можем добавить как текст, так и HTML:
paragraph.innerHTML = "I'm a <strong>bold</strong> paragraph!";
Из двух методов textContent лучше — он отображается немного быстрее, чем innerHTML. Также возможно создать текстовый узел, используя метод createTextNode():
const text = document.createTextNode("I'm a text node!"); console.log(text); // "I'm a text node!"
Все эти методы создали новые элементы и текстовые узлы, однако они не видны во front-end веб-сайта до тех пор, пока не будут вставлены в документ!
Вставка узлов в DOM
Чтобы увидеть новые текстовые узлы и элементы, которые мы создаем во front end, нам нужно вставить их в document. Методы appendChild() и insertBefore() используются для добавления элементов в начало, середину или конец родительского элемента, а replaceChild() используются для замены старого узла новым. Давайте продемонстрируем это, добавив в HTML список:
<ul> <li>Get pizza</li> <li>Get burgers</li> <li>Get Doritos</li> </ul>
И вот наша страница:
Скажем, мы хотим добавить новый элемент в конец списка, нам нужно сначала создать элемент и добавить к нему текст, как мы это делали ранее:
// Get the ul element const list = document.querySelector('ul'); // Create new list item const newItem = document.createElement('li'); newItem.textContent = 'Get nice cheese';
Теперь у нас есть полный элемент для нового элемента списка! Таким образом, мы можем добавить его в конец списка, используя appendChild():
// Add our new item to the end of the list list.appendChild(newItem);
И наш новый элемент li был добавлен в конец ul:
Чтобы добавить элемент в начало списка, давайте создадим еще один элемент (мы должны снова создать новый элемент, поскольку createElement() не может использовать его повторно):
// Create a new list item const anotherNewItem = document.createElement('li'); anotherNewItem.textContent = 'Get Party Whistles';
Мы используем метод insertBefore(), чтобы добавить пункт в начало списка. Он будет принимать два аргумента, первый из которых будет новым дочерним узлом, который будет добавлен, а второй — это дочерний узел, который будет следовать сразу же за ним.
parentNode.insertBefore(newNode, nextSibling);
Таким образом для нашего примера мы добавим в начало списка новый элемент anotherNewItem:
// Add new item to the beginning of the list list.insertBefore(anotherNewItem, list.firstElementChild);
И теперь наш новый узел был добавлен в начало списка! Давайте также рассмотрим, как мы можем заменить существующий узел новым, используя replaceChild(). Сначала мы создаем новый элемент:
const modifiedItem = document.createElement('li'); modifiedItem.textContent = "Get Poppin' Jalapeno Doritos";
replaceChild() также принимает два аргумента: сначала новый узел, а затем заменяемый узел.
parentNode.replaceChild(newNode, oldNode);
В нашем примере мы заменяем третий дочерний элемент в списке:
// Replace list item list.replaceChild(modifiedItem, list.children[3])
Используя комбинацию appendChild(), insertBefore() и replaceChild() мы можем вставлять узлы и элементы в любом месте DOM!
Удаление узлов из DOM
Чтобы удалить узлы из DOM, мы можем использовать removeChild(), чтобы удалить дочерние узлы из их родителя, или remove() чтобы удалить сам узел. Возвращаясь к нашему примеру, давайте удалим последний элемент в списке:
// Remove the last list item list.removeChild(list.lastElementChild);
И результат:
В качестве альтернативы, мы могли бы использовать remove(), чтобы удалить сам узел:
// Remove the third element from our list list.children[2].remove();
Используя removeChild() и remove(), вы можете удалить любой узел из DOM.
Изменение атрибутов, классов и стилей в DOM
В этом последнем разделе мы рассмотрим, как мы можем изменить атрибуты, классы и стили узлов HTML-элементов.
Изменение атрибутов
Атрибуты часто используются в HTML для предоставления дополнительной информации об элементе. Некоторые примеры — атрибут src тега img, атрибут href тега a, а также class, id и style. Возможно, вы также видели атрибуты, начинающиеся с data — это пользовательские атрибуты, которые мы также можем изменять.
Методы, которые мы используем в JavaScript для изменения атрибутов элемента:
hasAttribute() - возвращает логическое значение.
getAttribute() - возвращает значение определенного атрибута.
setAttribute() - добавляет или обновляет значение определенного атрибута.
removeAttribute() - удаляет атрибут из элемента.
Давайте используем следующий HTML в качестве примера:
<!DOCTYPE html> <html lang="en"> <body> <img src="https://res.cloudinary.com/trobes/image/upload/c_scale,w_400/v1549941322/before.png"> </body> </html>
Откройте консоль и проверьте методы атрибутов:
// Assign image element const image = document.querySelector('img'); image.hasAttribute('src'); // true image.getAttribute('src'); // returns the src link image.removeAttribute('src'); // removes the src
И давайте используем setAttribute(), чтобы назначить новое изображение для src:
image.setAttribute('src', 'https://res.cloudinary.com/trobes/image/upload/c_scale,w_400/v1549941322/after.png');
Методы hasAttribute() и getAttribute() часто используются с условными операторами, в то время как setAttribute() и removeAttribute() чаще используются для непосредственного изменения в DOM.
Изменение классов
При работе с CSS мы используем классы для применения стилей к нескольким элементам. Давайте посмотрим, как мы можем работать с атрибутом класса в JavaScript, используя className и classList.
className - получает или задает значение класса.
classList.add() - добавляет значения класса.
classList.toggle() - включение или выключение класса.
classList.contains() - проверяет, существует ли значение.
classList.replace() - заменяет старое значение новым.
classList.remove() - удаляет значение.
Давайте рассмотрим каждый из этих методов на следующем примере:
<!DOCTYPE html> <html lang="en"> <style> .active { border: 5px solid green; } .alert { border: 5px solid red; } .hidden { display: none; } div { border: 5px solid lightgrey; padding: 15px; margin: 5px; } </style> <body> <div>Div #1</div> <div class="active">Div #2</div> </body> </html>
И вот начальный вывод HTML:
Мы используем className для присвоения значений классам:
// Select div #1 const div = document.querySelector('div'); // Assign the alert class to div #1 div.className = 'alert';
Теперь класс alert, определенный в HTML, был присвоен первому div:
Это переопределяет любые существующие классы элемента. Вы можете добавить несколько классов, используя className, если разделите имена классов пробелами. Другой способ изменить классы — это свойство classList, оно содержит несколько очень полезных методов:
// Select div #2 by its class name const activeDiv = document.querySelector('.active'); // Add the hidden class activeDiv.classList.add('hidden'); // Remove the hidden class activeDiv.classList.remove('hidden'); // Switch between hidden true and false activeDiv.classList.toggle('hidden'); // Replace active class with alert class activeDiv.classList.replace('active', 'alert');
После выполнения этих методов наш HTML будет выглядеть так:
Примечание: classList.add() добавляет новый класс в список существующих классов (не забывайте className переопределять любые существующие классы). Вы также можете добавить несколько классов в виде строк через запятую.
Изменение стилей
Обычно стили добавляются с помощью отдельных таблиц стилей CSS, однако бывают случаи, когда нам нужно использовать встроенный стиль — и полезно знать, как мы можем изменить его напрямую с помощью JavaScript! Давайте продемонстрируем это с помощью следующего HTML:
<!DOCTYPE html> <html lang="en"> <body> <div style="height: 200px; width: 200px; border: 5px solid black; display: flex; justify-content: center; align-items: center;">Style me!</div> </body> </html>
Наш начальный вывод:
При работе со свойствами CSS в DOM мы используем camelCase. Таким образом, вместо использования черточек, как это было бы с CSS, например border-radius, мы используем camelCase, например borderRadius. Где первое слово указывается в нижнем регистре, а все последующие слова указываются с большой буквы.
// Select the div const div = document.querySelector('div'); // Make div into a circle, change color and font size div.style.borderRadius = '50%'; div.style.backgroundColor = 'lightgreen'; div.style.fontSize = '30px';
Примечание: мы могли бы использовать для изменения стилей setAttribute(), например. div.setAttribute(‘style’, ‘border-radius: 50%’); однако это удалит все существующие встроенные стили. Так что гораздо лучше использовать атрибут style напрямую. Наш вывод после добавления новых стилей:
Конечно, если нам нужно применить несколько изменений стиля, лучше применить их к классу в CSS и просто добавить новый класс к элементу через JavaScript.
Заключение
И это все. Мы рассмотрели работу с деревом DOM и узлами все о получении доступа, обходе и изменении элементов. Если вы продержались так долго — вы молодец! Вы на пути к освоению DOM! Я надеюсь, что вы нашли эту статью полезной!
Автор: Timothy Robards
Источник: https://itnext.io/
Редакция: Команда webformyself.