Part 2
DOM (Document Object Model)
Document Object Model, сокращённо DOM – объектная модель документа, которая представляет все содержимое страницы в виде объектов, которые можно менять.
document.body.style.background = "red"
<!DOCTYPE html>
<html>
<body>
<p id="p1">Hello World!</p>
<p id="p2">Hello World!</p>
<script>
document.getElementById("p2").style.color = "blue";
document.getElementById("p2").style.fontFamily = "Arial";
document.getElementById("p2").style.fontSize = "larger";
</script>
<p>The paragraph above was changed by a script.</p>
</body>
</html>
BOM (Browser Object Model)
Объектная модель браузера (Browser Object Model, BOM) – это дополнительные объекты, предоставляемые браузером (окружением), чтобы работать со всем, кроме документа.
window.location.href returns the href (URL) of the current page
window.location.hostname returns the domain name of the web host
window.location.pathname returns the path and filename of the current page
window.location.protocol returns the web protocol used (http: or https:)
window.location.assign loads a new document
window.setTimeout(function, milliseconds);
window.clearTimeout(timeoutVariable);
window.setInterval(function, milliseconds);
window.clearInterval(timerVariable);
Task
<div id="elem">
<div id="elem-content">Element</div>
</div>
<script>
// нужно для elem поменять фон background
</script>
querySelectorAll
<ul>
<li>Этот</li>
<li>тест</li>
</ul>
<ul>
<li>полностью</li>
<li>пройден</li>
</ul>
<script>
let elements = document.querySelectorAll('ul');
console.log(elements); // массив из ul
</script>
Самый универсальный метод поиска – это document.querySelectorAll(css), он возвращает все элементы внутри document, удовлетворяющие данному CSS-селектору.
querySelectorAll
<ul>
<li>Этот</li>
<li>тест</li>
</ul>
<ul>
<li>полностью</li>
<li>пройден</li>
</ul>
<script>
let elements = document.querySelectorAll('ul > li:last-child');
for (let elem of elements) {
alert(elem.innerHTML); // "тест", "пройден"
}
</script>
querySelector
<ul>
<li>Этот</li>
<li>тест</li>
</ul>
<ul>
<li>полностью</li>
<li>пройден</li>
</ul>
<script>
let element = document.querySelector('ul > li:last-child');
console.log(element) // тест
</script>
Метод document.querySelector(css) возвращает первый элемент, соответствующий данному CSS-селектору.
closest
<h1>Содержание</h1>
<div class="contents">
<ul class="book">
<li class="chapter">Глава 1</li>
<li class="chapter">Глава 2</li>
</ul>
</div>
<script>
let chapter = document.querySelector('.chapter'); // LI
console.log(chapter.closest('.book')); // UL
console.log(chapter.closest('.contents')); // DIV
console.log(chapter.closest('h1')); // null (потому что h1 - не предок)
</script>
Метод elem.closest(css) ищет ближайшего предка, который соответствует CSS-селектору. Сам элемент также включается в поиск.
Метод closest поднимается вверх от элемента и проверяет каждого из родителей. Если он соответствует селектору, поиск прекращается. Метод возвращает либо предка, либо null, если такой элемент не найден.
getElementsBy*
Task by getElementsBy*
<table id="table">
<tr>
<td>Ваш возраст:</td>
<td>
<label>
<input type="radio" name="age" value="young" checked> младше 18
</label>
<label>
<input type="radio" name="age" value="mature"> от 18 до 50
</label>
<label>
<input type="radio" name="age" value="senior"> старше 60
</label>
</td>
</tr>
</table>
<script>
// найти все input
</script>
Attributes and DOM properties
document.body.myData = {
name: 'Пётр',
familyName: 'Петрович'
};
console.log( document.body.myData.name ); // Пётр
Узел DOM – это объект, поэтому, как и любой объект в JavaScript, он может содержать пользовательские свойства и методы.
Нестандартные свойства и методы видны только в JavaScript и никак не влияют на отображение.
Пользовательские DOM-свойства:
Attributes
Элементам DOM, с другой стороны, соответствуют HTML-теги, у которых есть текстовые атрибуты.
Attributes
<body>
<div id="elem" about="Elephant"></div>
<script>
console.log( elem.getAttribute('About') ); // (1) 'Elephant', атрибут получен
elem.setAttribute('Test', 123); // (2) атрибут Test установлен
console.log( document.body.innerHTML ); // (3) в HTML видны все атрибуты!
const attrs = elem.attributes; // (4) можно получить коллекцию атрибутов
console.log(attrs)
</script>
</body>
dataset and data-attributes
<div id="elem" data-about="Elephant" data-user-location="street">
По улице прошёлся слон. Весьма красив и толст был он.
</div>
<script>
console.log( elem.getAttribute('data-about') ); // Elephant
console.log( elem.getAttribute('data-user-location') ); // street
</script>
С помощью нестандартных атрибутов можно привязать к элементу данные, которые будут доступны в JavaScript.
Как правило, это делается при помощи атрибутов с названиями, начинающимися на data-, например:
Стандарт HTML5 специально разрешает атрибуты data-* и резервирует их для пользовательских данных.
dataset and data-attributes
<div id="elem" data-about="Elephant" data-user-location="street">
По улице прошёлся слон. Весьма красив и толст был он.
</div>
<script>
console.log( elem.dataset.about ); // Elephant
console.log( elem.dataset.userLocation ); // street
</script>
При этом во всех браузерах, кроме IE10-, к таким атрибутам можно обратиться не только как к атрибутам, но и как к свойствам, при помощи специального свойства dataset:
Change document
let div = document.createElement('div');
div.className = "alert";
div.innerHTML = "<strong>Всем привет!</strong> Вы прочитали важное сообщение.";
Модификации DOM – это ключ к созданию «живых» страниц.
Создание сообщения:
Вставка:
document.body.append(div)
Change document
Вот методы для различных вариантов вставки:
Change document
Change document
DocumentFragment является специальным DOM-узлом, который служит обёрткой для передачи списков узлов.
Мы можем добавить к нему другие узлы, но когда мы вставляем его куда-то, он «исчезает», вместо него вставляется его содержимое.
<ul id="ul"></ul>
<script>
function getListContent() {
let fragment = new DocumentFragment();
for(let i=1; i<=3; i++) {
let li = document.createElement('li');
li.append(i);
fragment.append(li);
}
return fragment;
}
const ul = document.querySelector('#ul');
ul.append(getListContent()); // (*)
</script>
Bubbling and Capturing
Bubbling (всплытие)
Основной принцип всплытия:
При наступлении события обработчики сначала срабатывают на самом вложенном элементе, затем на его родителе, затем выше и так далее, вверх по цепочке вложенности.
<style>
body * {
margin: 10px;
border: 1px solid blue;
}
</style>
<form onclick="console.log('form')">FORM
<div onclick="console.log('div')">DIV
<p onclick="console.log('p')">P</p>
</div>
</form>
Bubbling and Capturing
<style>
body * {
margin: 10px;
border: 1px solid blue;
}
</style>
<form onclick="console.log('form')">FORM
<div onclick="console.log('div')">DIV
<p onclick="console.log('p')">P</p>
</div>
</form>
Всплытие гарантирует, что клик по внутреннему <p> вызовет обработчик onclick (если есть) сначала на самом <p>, затем на элементе <div> далее на элементе <form>, и так далее вверх по цепочке родителей до самого document.
Bubbling and Capturing
event.target
Самый глубокий элемент, который вызывает событие, называется «целевым» или «исходным» элементом и доступен как event.target.
Отличия от this (=event.currentTarget):
stop bubbling
Всплытие идёт прямо наверх. Обычно событие будет всплывать наверх и наверх, до элемента <html>, а затем до document, а иногда даже до window, вызывая все обработчики на своём пути.
Но любой промежуточный обработчик может решить, что событие полностью обработано, и остановить всплытие.
Для остановки всплытия нужно вызвать метод event.stopPropagation().
stop bubbling
Для того, чтобы полностью остановить обработку, современные браузеры поддерживают метод event.stopImmediatePropagation(). Он не только предотвращает всплытие, но и останавливает обработку событий на текущем элементе.
<div id="test">
<p>Hello</p>
<button>click me!</button>
</div>
let button = document.getElementsByTagName('button')
let div = document.getElementById('test')
div.addEventListener('click', (event) => {
console.log('hello');
});
button[0].addEventListener('click', (event) => {
event.stopPropagation();
});
capturing (погружение)
Cтандарт выделяет целых три стадии прохода события:
capturing (погружение)
Чтобы поймать событие на стадии перехвата, нужно использовать третий аргумент addEventListener: