Читать книгу «JavaScript: От Основ до Full-Stack Разработки» онлайн полностью📖 — Александра Ольшевски — MyBook.
image

Глава 7: Взаимодействие с Веб-Страницей: DOM.

В предыдущих главах мы освоили основы языка JavaScript: переменные, типы данных, операторы, условия, циклы и функции. Мы также научились работать с коллекциями данных – массивами и объектами. Теперь пришло время соединить JavaScript с тем, что видит пользователь – с веб-страницей.

Термин: DOM (Document Object Model) – это объектно-ориентированное представление HTML-документа. DOM (Document Object Model) представляет собой независимый от платформы и языка интерфейс программирования, который предоставляет возможность программам и скриптам взаимодействовать с HTML-, XHTML- и XML-документами. С его помощью можно получать доступ к содержимому документов, а также изменять их структуру, контент и визуальное представление.

Представьте DOM как дерево, где каждый узел дерева представляет собой часть HTML-документа: тег, атрибут, текст внутри тега. JavaScript может получить доступ к этому “дереву” и манипулировать им – изменять содержимое, стили, структуру, реагировать на действия пользователя.

Когда браузер загружает HTML-страницу, он создает DOM-представление этой страницы. JavaScript, запущенный в браузере, может взаимодействовать с этим DOM.

7.1. Что такое DOM (Структура дерева)

DOM не устанавливает жестких рамок для структуры документа. Любой документ, имеющий определенную структуру, может быть представлен в виде древовидной структуры узлов. Каждый узел в этой структуре соответствует элементу, текстовому фрагменту, графическому объекту или другому компоненту документа. Узлы связаны между собой иерархическими отношениями, подобными «родитель-потомок».

DOM представляет HTML-документ в виде иерархической структуры:

Документ (Document): Корневой узел всего DOM. В JavaScript это глобальный объект document.

Элементы (Elements): Каждый HTML-тег (например, <html>, <body>, <h1>, <p>, <div>) является узлом-элементом в DOM.

Атрибуты (Attributes): Атрибуты HTML-тегов (например, src в теге <img>, href в теге <a>, class или id) также являются узлами, но они обычно связаны с узлами-элементами.

Текстовые узлы (Text Nodes): Текст внутри HTML-элементов (например, “Привет, мир!” внутри тега <p>) также является узлом.

Пример HTML-структуры и ее DOM-представления:

html

<!DOCTYPE html>

<html lang="ru">

<head>

<meta charset="UTF-8">

<title>Пример DOM</title>

</head>

<body>

<h1>Заголовок</h1>

<p id="intro">Это первый параграф.</p>

<div class="content">

<p>Это второй параграф.</p>

</div>

</body>

</html>

DOM-представление этой структуры можно схематично показать так:

Document

└── <html>

├── <head>

│ ├── <meta charset="UTF-8">

│ └── <title> (Текстовый узел: "Пример DOM")

└── <body>

├── <h1> (Текстовый узел: "Заголовок")

├── <p id="intro"> (Текстовый узел: "Это первый параграф.")

└── <div class="content">

└── <p> (Текстовый узел: "Это второй параграф.")

7.2. Как найти элементы на странице

Чтобы манипулировать элементами, нам сначала нужно получить к ним доступ. JavaScript предоставляет несколько методов для поиска элементов в DOM.

А) Поиск по ID (getElementById)

Термин: ID (Identifier) – уникальный идентификатор элемента на странице. Каждый id должен быть уникален в пределах одного HTML-документа.

Метод document.getElementById(id) возвращает первый элемент, у которого указанный id.

javascript

// Предполагая, что на странице есть <p id="intro">…</p>

let introParagraph = document.getElementById("intro");

console.log(introParagraph); // Выведет объект <p id="intro">…</p>

// Теперь мы можем работать с этим элементом:

introParagraph.textContent = "Новый текст для параграфа."; // Изменяем текст

Б) Поиск по именам тегов (getElementsByTagName)

Метод document.getElementsByTagName(tagName) возвращает HTML-коллекцию (похожа на массив, но не совсем) всех элементов с указанным именем тега.

html

<ul>

<li>Элемент 1</li>

<li>Элемент 2</li>

</ul>

<p>Какой-то текст.</p>

<ul>

<li>Элемент 3</li>

</ul>

javascript

let allParagraphs = document.getElementsByTagName("p");

console.log(allParagraphs); // HTMLCollection [ <p>Какой-то текст.</p> ] (в данном случае)

let allLists = document.getElementsByTagName("ul");

console.log(allLists); // HTMLCollection [ <ul>…</ul>, <ul>…</ul> ]

// Доступ к элементам в коллекции по индексу

console.log(allLists[0]); // Первый <ul>

console.log(allLists[1]); // Второй <ul>

// Перебор коллекции (можно использовать for…of или обычный for)

for (let list of allLists) {

console.log(list);

}

Важно: getElementsByTagName возвращает живую коллекцию. Это значит, что если DOM изменится (например, добавятся новые элементы <li>), коллекция автоматически обновится.

В) Поиск по именам классов (getElementsByClassName)

Метод document.getElementsByClassName(className) возвращает HTML-коллекцию всех элементов, у которых есть указанный CSS-класс. Можно передавать несколько классов, разделенных пробелом, но это будет искать элементы, у которых есть ВСЕ перечисленные классы.

html

<div class="item important">Важный элемент 1</div>

<div class="item">Обычный элемент</div>

<div class="item important">Важный элемент 2</div>

javascript

let importantItems = document.getElementsByClassName("important");

console.log(importantItems); // HTMLCollection [ <div class="item important">…, <div class="item important">…</div> ]

let allItems = document.getElementsByClassName("item");

console.log(allItems); // HTMLCollection [ <div class="item important">…, <div class="item">…, <div class="item important">…</div> ]

// Поиск элементов с двумя классами (очень редко нужно)

let itemAndImportant = document.getElementsByClassName("item important");

console.log(itemAndImportant); // HTMLCollection [ <div class="item important">…</div> ]

getElementsByClassName также возвращает живую коллекцию.

Г) Современные и универсальные методы (querySelector, querySelectorAll)

Эти методы используют синтаксис CSS-селекторов, что делает их очень мощными и гибкими.

document.querySelector(selector): Возвращает первый элемент, который соответствует указанному CSS-селектору.javascript// Поиск по ID

let introPara = document.querySelector("#intro"); // То же, что getElementById("intro")

// Поиск по имени тега

let firstH1 = document.querySelector("h1");

// Поиск по классу

let firstImportantItem = document.querySelector(".important"); // Найдет первый элемент с классом "important"

// Поиск по имени тега внутри другого тега

let firstParagraphInDiv = document.querySelector("div p"); // Найдет первый <p> внутри <div>

// Поиск по комбинации селекторов

let specificDiv = document.querySelector("div.content p"); // Найдет <p> внутри <div class="content">

document.querySelectorAll(selector): Возвращает NodeList (статическую коллекцию, похожую на массив, но не живую) всех элементов, которые соответствуют указанному CSS-селектору.javascript// Получить все параграфы

let allParagraphs = document.querySelectorAll("p");

console.log(allParagraphs); // NodeList [ <p id="intro">…</p>, <p>…</p> ]

// Получить все элементы с классом "item"

let allItems = document.querySelectorAll(".item");

console.log(allItems);

// Получить все элементы h1, h2, h3

let headings = document.querySelectorAll("h1, h2, h3");

// Получить все элементы li внутри ul

let listItems = document.querySelectorAll("ul li");

// Перебор NodeList (можно использовать forEach, for…of)

listItems.forEach((item, index) => {

console.log(`List item ${index + 1}: ${item.textContent}`);

});

Когда использовать какой метод:

getElementById: Если у вас есть id и вам нужен один конкретный элемент. Очень быстро.

querySelector: Универсальный метод для поиска первого элемента по любому CSS-селектору. Гибкий и удобный.

querySelectorAll: Для поиска всех элементов, соответствующих CSS-селектору. Возвращает NodeList, который удобно перебирать.

getElementsByTagName, getElementsByClassName: Могут быть немного быстрее querySelectorAll в специфических случаях, но querySelectorAll часто предпочтительнее из-за его универсальности и поддержки CSS-селекторов.

7.3. Изменение содержимого и атрибутов элементов

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

А) Изменение текстового содержимого:

element.textContent: Устанавливает или возвращает текст внутри элемента. HTML-теги внутри текста будут отображаться как обычный текст.

element.innerHTML: Устанавливает или возвращает HTML-код внутри элемента. Это значит, что если вы присвоите сюда строку с HTML-тегами, они будут интерпретированы браузером. Используйте с осторожностью, чтобы избежать XSS-уязвимостей (если содержимое приходит от пользователя).

html

<p id="myParagraph">Старый текст.</p>

<div id="myDiv"></div>

javascript

let paragraph = document.getElementById("myParagraph");

// Изменение текста

paragraph.textContent = "Новый текст."; // Отобразится как обычный текст

console.log(paragraph.textContent); // "Новый текст."

// Изменение HTML-содержимого

let div = document.getElementById("myDiv");

div.innerHTML = "<strong>Жирный текст</strong> и <em>курсив</em>."; // HTML-теги будут обработаны

console.log(div.innerHTML); // "<strong>Жирный текст</strong> и <em>курсив</em>."

// Если использовать textContent вместо innerHTML с HTML-тегами

div.textContent = "<strong>Жирный текст</strong>"; // Отобразится как "<strong>Жирный текст</strong>" (буквально)

Б) Изменение атрибутов:

Прямой доступ (для стандартных атрибутов): Если атрибут является стандартным свойством элемента (например, id, src, href, class, value), вы можете получить или установить его напрямую.html<img id="myImage" src="image.jpg" alt="Описание">

<a href="https://example.com" id="myLink">Ссылка</a>

javascriptlet img = document.getElementById("myImage");

img.src = "new_image.png"; // Изменяем источник изображения

img.alt = "Новое описание"; // Изменяем атрибут alt

let link = document.getElementById("myLink");

link.href = "https://new-example.com"; // Изменяем ссылку

link.textContent = "Перейти на новый сайт"; // Изменяем текст ссылки

Методы getAttribute(), setAttribute(), removeAttribute(): Более универсальные методы, которые работают с любыми атрибутами, включая пользовательские (data-*).html<button data-action="save">Сохранить</button>

javascriptlet button = document.querySelector('button');

// Получить атрибут

console.log(button.getAttribute('data-action')); // "save"

console.log(button.getAttribute('disabled')); // null (если атрибута нет)

// Установить атрибут

button.setAttribute('disabled', ''); // Делаем кнопку неактивной (значение атрибута может быть пустым)

button.setAttribute('data-action', 'delete'); // Изменяем атрибут

// Удалить атрибут

button.removeAttribute('disabled');

В) Изменение стилей:

Стили элемента можно изменять через его свойство style. Каждый CSS-свойство, написанное через дефис (например, background-color), преобразуется в camelCase (например, backgroundColor) для использования в JavaScript.

html

<div id="myBox" style="width: 100px; height: 100px; background-color: lightblue;"></div>

javascript

let box = document.getElementById("myBox");

// Изменение стиля

box.style.width = "200px"; // Устанавливаем ширину

box.style.height = "150px"; // Устанавливаем высоту

box.style.backgroundColor = "lightgreen"; // Устанавливаем фоновый цвет

box.style.border = "2px solid red"; // Добавляем рамку

// Получение текущего стиля (может быть сложнее, чем кажется, если стили заданы в CSS-файлах, а не инлайново)

console.log(box.style.width); // "200px" (если был инлайново задан или последний измененный)

Работа с классами CSS:

Часто гораздо удобнее управлять стилями, добавляя или удаляя CSS-классы, чем менять отдельные свойства style. Это позволяет отделять логику JavaScript от стилизации.

element.classList: Это свойство возвращает объект DOMTokenList, который имеет удобные методы для управления классами:add(className): Добавляет класс.

remove(className): Удаляет класс.

toggle(className): Добавляет класс, если его нет, и удаляет, если он есть.

contains(className): Проверяет, есть ли у элемента указанный класс (возвращает true или false).

html

<div id="myDivWithClass" class="box normal"></div>

javascript

let divWithClass = document.getElementById("myDivWithClass");

// Добавить класс

divWithClass.classList.add("highlight"); // Класс "highlight" будет добавлен

console.log(divWithClass.className); // "box normal highlight"

// Удалить класс

divWithClass.classList.remove("normal"); // Класс "normal" будет удален

console.log(divWithClass.className); // "box highlight"

// Переключить класс

divWithClass.classList.toggle("active"); // Класс "active" будет добавлен

divWithClass.classList.toggle("active"); // Класс "active" будет удален

console.log(divWithClass.classList.contains("highlight")); // true

console.log(divWithClass.classList.contains("normal")); // false

CSS, который будет работать с этими классами:

css

.box {

width: 100px;

height: 50px;

border: 1px solid black;

}

.highlight {

background-color: yellow;

font-weight: bold;

}

.active {

border-color: blue;

transform: scale(1.1); /* Небольшое увеличение */

}

7.4. Создание и удаление элементов

JavaScript может не только изменять существующие элементы, но и создавать новые, а также удалять их из DOM.

Создание элемента:

document.createElement(tagName): Создает новый элемент с указанным именем тега. Этот элемент пока не находится на странице, он существует только в памяти.javascriptlet newParagraph = document.createElement("p"); // Создали элемент <p>

newParagraph.textContent = "Это новый параграф, созданный JavaScript.";

newParagraph.classList.add("info-text"); // Добавили класс

Добавление элемента на страницу:

Чтобы созданный элемент появился на странице, его нужно добавить к какому-либо существующему элементу.

parentElement.appendChild(childElement): Добавляет childElement как последнего потомка parentElement.javascript// Предположим, у нас есть <div id="container"></div>

let container = document.getElementById("container");

let newParagraph = document.createElement("p");

newParagraph.textContent = "Добавлен в конец контейнера.";

container.appendChild(newParagraph); // Параграф появится внутри div#container

parentElement.insertBefore(newElement, referenceElement): Вставляет newElement перед referenceElement внутри parentElement.javascript// Предположим, у нас есть <div id="container"> <p id="existingPara">Существующий</p> </div>

let container = document.getElementById("container");

let existingPara = document.getElementById("existingPara");

let newPara = document.createElement("p");

newPara.textContent = "Добавлен перед существующим.";

container.insertBefore(newPara, existingPara); // Новый параграф появится перед existingPara

Удаление элемента:

element.remove(): Удаляет элемент из DOM.javascriptlet elementToRemove = document.getElementById("itemToDelete");

if (elementToRemove) { // Важно проверять, существует ли элемент, прежде чем удалять

elementToRemove.remove();

}

parentElement.removeChild(childElement): Более старый метод, который удаляет childElement из parentElement.javascriptlet parent = document.getElementById("parent");

let child = document.getElementById("child");

if (parent && child) {

parent.removeChild(child);

}

7.5. Работа с классами CSS (краткое повторение)

Мы уже видели classList для добавления, удаления и переключения классов. Это наиболее рекомендуемый способ управления стилями элемента с помощью JavaScript.

Пример 7.5.1: Динамическое изменение стиля кнопки

html

<button id="myButton" class="btn">Нажми меня</button>

css

.btn {

padding: 10px 20px;

border: 1px solid gray;

background-color: lightgray;

cursor: pointer;

}

.btn.active { /* Стиль для активной кнопки */

background-color: dodgerblue;

color: white;

font-weight: bold;

}

javascript

let button = document.getElementById("myButton");

button.addEventListener("click", function() { // Добавим обработчик события клика (об этом позже)

button.classList.toggle("active"); // Переключит класс 'active' при каждом клике

});

При каждом нажатии на кнопку будет добавляться или удаляться класс active, что будет менять ее внешний вид согласно CSS-правилам.

Практическое задание:

Создайте простую HTML-страницу с <h1>, <p> и <ul>.Используйте document.getElementById() для доступа к <h1> и измените его текст.

Используйте document.querySelector() для доступа к первому <li> в <ul> и измените его textContent.

Используйте document.querySelectorAll() для получения всех <li> в <ul> и добавьте им класс list-item с помощью forEach() и element.classList.add().

Создайте кнопку <button id="changeColorBtn">Изменить цвет</button> и <div> с id myBox, который имеет начальный стиль (цвет фона, размеры) через CSS.При нажатии на кнопку, используя addEventListener (которое мы рассмотрим позже), измените цвет фона div на случайный. Для генерации случайного цвета используйте Math.random() для RGB-компонентов.

Напишите функцию createListItem(text), которая создает новый элемент <li> с заданным текстом и добавляет его в конец существующего <ul> на странице.

Создайте элемент <p> с текстом “Этот параграф будет удален”. Добавьте его на страницу. Затем найдите этот параграф по его textContent (это можно сделать, например, через document.querySelectorAll('p') и цикл, или если у него есть ID) и удалите его с помощью element.remove().

Эта глава заложила основу для взаимодействия JavaScript с веб-страницей. Мы научились находить элементы, изменять их содержимое, атрибуты и стили, а также создавать и удалять элементы. Это критически важный навык для любого веб-разработчика.

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

1
...