DOM

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*

  • document.getElementsByTagName(tag) ищет элементы с данным тегом и возвращает их коллекцию. Передав "*" вместо тега, можно получить всех потомков.
  • document.getElementsByClassName(className) возвращает элементы, которые имеют данный CSS-класс.
  • document.getElementsByName(name) возвращает элементы с заданным атрибутом name. Очень редко используется.

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-свойства:

  • Могут иметь любое значение.
  • Названия свойств чувствительны к регистру.
  • Работают за счёт того, что DOM-узлы являются объектами JavaScript.

Attributes

Элементам DOM, с другой стороны, соответствуют HTML-теги, у которых есть текстовые атрибуты.

  • elem.hasAttribute(name) – проверяет наличие атрибута
  • elem.getAttribute(name) – получает значение атрибута
  • elem.setAttribute(name, value) – устанавливает атрибут
  • elem.removeAttribute(name) – удаляет атрибут

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

Вот методы для различных вариантов вставки:

  • node.append(...nodes or strings) – добавляет узлы или строки в конец node,
  • node.prepend(...nodes or strings) – вставляет узлы или строки в начало node,
  • node.before(...nodes or strings) – вставляет узлы или строки до node,
  • node.after(...nodes or strings) – вставляет узлы или строки после node,
  • node.replaceWith(...nodes or strings) – заменяет node заданными узлами или строками.

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):

  • event.target – это исходный элемент, на котором произошло событие, в процессе всплытия он неизменен.
  • this – это текущий элемент, до которого дошло всплытие, на нём сейчас выполняется обработчик.

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тандарт выделяет целых три стадии прохода события:

  1. Событие сначала идёт сверху вниз. Эта стадия называется «стадия перехвата» (capturing stage).
  2. Событие достигло целевого элемента. Это – «стадия цели» (target stage).
  3. После этого событие начинает всплывать. Это – «стадия всплытия» (bubbling stage).

capturing (погружение)

Чтобы поймать событие на стадии перехвата, нужно использовать третий аргумент addEventListener:

  • Если аргумент true, то событие будет перехвачено по дороге вниз.
  • Если аргумент false, то событие будет поймано при всплытии.

DOM part 2

By Oleg Rovenskyi