DOM

Мангин Александр

Провакационный вопрос.

Зачем нужен JS?

Оглянемся назад...

  • Мигающая реклама
  • Скрытие элементов
  • И другие мелочи...

Сейчас

  • Исполнения кода не только в браузере
  • Запуск js на сервере
  • Firefox Os
  • Phonegap
  • ...

Но какая основная цель JS?

Создание динамических страниц.

Сегодня в лекции:

  • Запуск кода в браузере
  • Что такое DOM?
  • Как искать что-то в html-документе?
  • Как извлекать информацию из html-документа?
  • Как изменить html-документ?
  • Как реагировать на действия пользователя?

Запуск кода в браузере

  • Через консоль разработчика
  • Через сервисы-песочницы
  • Через html-документ

Через консоль разработки

  • Запускаем Яндекс.Браузер
  • Идем в Settings
  • Идем в Advanced
  • Идем в More Tools
  • Идем в Developer Tools
  • Затем идем в таб Console
  • Пишем в строке код 2+2
  • Жмем enter // 4
P.S. Для запуска Developer Tools есть хоткей

Через сервис песочницу

  • Выбираем сервис песочницу...
  • Идем на сайт jsfiddle.net
  • Во вкладке js пишем: console.log(2+2)
  • Жмем кнопку run
  • Идем в Developer tools

Запуск кода через html-документ


<html>
    <body>
        ваша верстка
        <scrip src='app.js'></script>
    </body>
</html>
            

DOM(Document object model)

Что такое DOM?

  • для браузера
  • для разработчика

DOM для браузера


<html>
  <body>
    <form>
      <input type='text' placeholder='Имя'/>
      <input type='text' placeholder='Фамилия'/>
      <button>Отправить</button>
    </form>
  </body>
</html>
    

Узел(Node)

  1. Тип узла
  2. Набор атрибутов
  3. Ссылка на левого/правого соседа
  4. Ссылка на родительский узел
  5. Ссылка на массив 'детей'

Типы узлов

  1. ELEMENT_NODE - элемент
  2. TEXT_NODE - текст
  3. COMMENT_NODE - комментарий
  4. DOCUMENT_NODE - документ
  5. DOCUMENT_TYPE_DODE - doctype
  6. DOCUMENT_FRAGMENT_NODE - фрагмент

Сколько узлов?


<html>
  <body>
    <form>
      <input type='text' placeholder='Имя'/>
      <input type='text' placeholder='Фамилия'/>
      <button>Отправить</button>
    </form>
  </body>
</html>
    
  • 6 - элементов
  • 1 - текст
  • 6 - элементов
  • 1 + 8 - текст

Что помнит DOM?

  1. Содержимое элементов
  2. Положение элементов
  3. Переводы строк
  4. Комментарии

Минификация DOM


<html><body><form><input type='text' placehol...
    
  • Экономия трафика
  • Меньший размер DOM
  • Более очевидные левые и правые соседи

DOM для разработчика

это API
  1. Поиск по документу
  2. Чтение данных из документа
  3. Изменение документа
  4. Реакция на действия пользователя

Поиск в DOM

Точка входа


window.document; // document
document.nodeType === document.DOCUMENT_NODE;
    

Поиск в DOM

<html>
  <body>
    <form>
      <input type='text' placeholder='Имя'/>
      <input type='text' placeholder='Фамилия'/>
      <button>Отправить</button>
    </form>
  </body>
</html>
    

Поиск по ID

<html>
 <body>
  <form id='form'>
   <input id='name' type='text'/>
   <input id='surname' type='text'/>
   <button id='submit'>Отправить</button>
  </form>
 </body>
</html>
    

var form = document.getElementById('form');
var name = document.getElementById('name');
    

Плюсы/Минусы

  • (+-)Подходит для поиска уникальных элементов
  • (+)Очень быстрый

Поиск по тэгам

<html>
  <body>
    <form id='form'>
      <input type='text' placeholder='Имя'/>
      <input type='text' placeholder='Фамилия'/>
      <button>Отправить</button>
    </form>
  </body>
</html>
var forms = document.getElementsByTagName('form');
var inputs = document.getElementsByTagName('input');

Плюсы/Минусы

  • (+) Поиск пачкой
  • (-) Метод медленнее, чем getElementById
  • (-) Редко нужно искать по тэгам

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

<html>
  <body>
    <form class='person-form'>
      <input class='name' type='text'/>
      <input class='surname' type='text'/>
      <button class='submit'>Отправить</button>
    </form>
  </body>
</html>
var formName = document
  .getElementsByClassName('name')[0];

formName.value = 'Alex';

Плюсы/Минусы

  • (+) Довольно гибкий
  • (+) Можно искать пачкой
  • (-) Метод медленнее, чем getElementById

Поиск по css-селектору

<html>
  <body>
    <form class='person-form'>
      <input class='name' type='text'/>
      <input class='surname' type='text'/>
      <button class='submit'>Отправить</button>
    </form>
  </body>
</html>
var formName = document
    .querySelector('.form .name');
var formInputs = document
    .querySelectorAll('.form input');

Плюсы/Минусы

  • (++) Самый гибкий спосооб
  • (-) Самый медленный способ

Поиск. Примеры.

Пример

Поиск в контексте

Пример

NodeList/HTMLCollection

В чем разница?
  • NodeList - может содержать произвольные узлы
  • HTMLCollection - может содержать только элементы
  • HTMLCollection - live-collection
  • NodeList - бывает live-collection

live-collection

  • Помнит 'запрос'
  • Кэширует поиск до изменения DOM
  • Автоматически обновляется при изменении DOM
  • Не содержит методов массива
Пример

live-collection -> array


var inputs = document.querySelectorAll('input');
inputs = [].slice.call(inputs);
    

Относительный поиск в DOM

Поиск по элементам vs поиск по узлам


<button></button> <button></button>
    

Поиск соседей


<button></button> <button></button>
    

var button = document.querySelector();

var nextButton = button.nextElementSibling;
var prevButton = button.previousElementSibling;
    

Поиск соседей


<button></button> <button></button>
    

var button = document.querySelector();

var nextText = button.nextSibling;
var prevButton = button.previousSibling;
    

Поиск ближайшего отца


<form><button></button></form>
    
var button = document.querySelector('button');

var form = button.parentElement;
// button.parentNode;

Поиск конекретного отца


<form>
    <button>
        <span> Купить </span>
    </button>
</form>
    
var span = document.querySelector('span');

var form = span.closest('form');

Поиск детей


var body = document.body;

var first = body.firstElementChild; // firstChild
var last = body.lastElementChild; // lastChild
var children = body.children; // childNodes
    

Matches


<form>
    <button class='name'>
        <span> Купить </span>
    </button>
</form>
    

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

button.matches('button.name'); // true
button.matches('button.surname'); // false
    

Работа с аттрибутами

  • настройка нативных контролов
  • передача данных на клиент

Чтение аттрибутов


<a href='yandex.ru' class='name'>Купить</a>
    
var button = document.querySelector('button');

button.getAttribute('href'); // 'yandex.ru'

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


<a class='name'>Купить</a>
    
var button = document.querySelector('a');

button.setAttribute('href', 'yandex.ru');

Удаление аттрибутов


<a class='name'>Купить</a>
    

var button = document.querySelector('a');

button.removeAttribute('href');
    

свойства vs аттрибуты


<input class='name'/>
    

var name = document.querySelector('input');

name.placeholder;
name.className;
name.id;
    

Важные свойства и аттрибуты

  • id
  • className/class
  • style
  • value
  • href
  • и другие

Пользовательские аттрибуты


<div data-serve-time='2015/09/01'></div>
    
var div = document.querySelector('div');

div.dataset.serverTime;

Пользовательские аттрибуты

Пример

Измененеие class

  • Изменение аттрибута
  • Изменение свойства
  • Работа с classList

Измененеие аттрибута class


var track = document.querySelector('.track');

track.setAttribute('class', 'track selected');
    

Измененеие свойства class


var track = document.querySelector('.track');

track.className = 'track selected';
    

Работа с ClassList

Добавление class


var track = document.querySelector('.track');

track.classList.add('selected');
track.className; // 'track selected'
    

Удаление class


var track = document.querySelector('.track');

track.classList.remove('selected');
track.className; // 'track'
    

Замена class


var track = document.querySelector('.track');

track.classList.toggle('selected', 'removed');
track.className; // 'track removed'
    

Изменение DOM

Создание новых элементов


var track = document.createElement('div');

track.className = 'track';
    

Создание текста


var text = document.createTextNode('Послушать');
    

Добавление элемента

Способ 1.

var trackList = document
    .querySelector('.trackList');

var track = document.createElement('div');
var text = document.createTextNode('Послушать');

track.appendChild(text);
trackList.appendChild(track);
    

Добавление элемента

Способ 2.

var trackList = document
    .querySelector('.trackList');

var track = document.createElement('div');
var label = document.createElement('div')
var duration = document.createElement('div');

track.appendChild(duration);
track.insertBefore(label, duration);
trackList.appendChild(track);
    

Добавление элемента

Способ 3.

var trackList = document
    .querySelector('.trackList');
var lastTrack = trackList.lastElementChild;

var track = document.createElement('div');
var label = document.createElement('div')
var duration = document.createElement('div');

track.appendChild(duration);
track.insertBefore(label, duration);
trackList.replaceChild(lastTrack, track);
    

Добавление элемента

Способ 4.

var container = document
    .querySelector('.trackList');

container.innerHTML = '<div class="track"></div>';
    

Добавление элемента

Способ 5.

var container = document
    .querySelector('.trackList');

container.innerText = 'Какой-то текст';
    

Добавление элемента

Способ 6.
  • Идем на сайт garann.github.io/template-chooser/
  • Выбираем шаблонизатор
  • И пользуемся

Добавление элемента

Способ 7.

Пишем свой шаблонизатор!

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


var trackList = document
    .querySelector('.trackList');

trackLIst.parentElement.removeChild(trackList);
    

Добавление нескольких элементов

Добавление нескольких элементов

Способ 1

var trackList = document
    .querySelector('.trackList');

for(var i =0; i < 10; i++) {
  var track = document.createElement('a');
  trackList.appendChild(track);
}
    

Добавление нескольких элементов

Способ 2

var trackList = document.querySelector('.trackList');
var container = document.createElement('div');

for(var i =0; i < 10; i ++) {
  var track = document.createElement('a');
  container.appendChild(track);
}

trackList.appendChild(container);
    

Добавление нескольких элементов

Способ 3

var trackList = document.querySelector('.trackList');
var container = document.createDocumentFragment();

for(var i =0; i < 10; i ++) {
  var track = document.createElement('a');
  container.appendChild(track);
}
trackList.appendChild(container);
    

События

  • Клик по элементу
  • Скролл
  • Ввод
  • Нажатие клавиши
  • ...

Подписка на события

Подписка на события через аттрибут


<a class='name' onclick=buy()>Купить</a>
    

function buy() {
    console.log('Купить!');
}
    

Подписка на события через свойство


<a class='buy'>Купить</a>
    

var buyButton = document.querySelector('.buy');

buyButton.onclick = function () {
    console.log('Купить!');
}
    

Подписка на события через addEventListener


<a class='buy'>Купить</a>
    

var buyButton = document.querySelector('.buy');

buyButton.addEventLister('click', function () {
    console.log('Купить!');
}, false);
    

Отписка от событий через removeEventListener


<a class='buy'>Купить</a>
    

var buyButton = document.querySelector('.buy');
var handler = function () {
    console.log('Купить!');
}

buyButton.addEventLister('click', handler, false);
// что-то делаем
buyButton.removeEventLister('click', handler, false);
    

Объект event

  • target
  • currentTarget
  • type
  • и другие

Механизм обработки событий

  • Кто-то создал событие
  • Берем спискок родителей
  • Начинаем стадию захвата
  • Бежим в цикле по родителям начиная с самого дальнего
  • - Вызываем обработчики, которые подписаны на стадию захвата
  • Начинаем стадию цели
  • Вызываем все обработчики, которые подписаны не важно на какую стадию
  • Начинаем стадию всплытия
  • Бежим в цикле по родителям начиная с самого ближнего
  • - Вызываем обработчики, которые подписаны на стадию всплытия

Пример с квадратами

Пример

Остановка распространения события


<div class='container'>
    <a class='buy'>Купить</a>
</div>
    

var container = document.querySelector('.container');
var buyButton = document.querySelector('.buyButton');

buyButton.addEventListener('click', function (e) {
    console.log('buy!');
    e.stopPropagation();
}, false);

container.addEventListener('click', function () {
    console.log('click  по контейнеру');
}, false);
    

Отмена нативного поведения


<a href='yandex.ru' class='buy'>Купить</a>
    

var buyButton = document.querySelector('.buy');

buyButton.addEventListener('click', function (e) {
    console.log('buy!');
    e.preventDefault();
}, false);
    

Делегирование событий

Делегирование событий

  • Найти родителя
  • Подписаться на нужное событие
  • В обработчике отсеять лишнии элементы

Делегирование событий


<div class='container'>
    <a class='buy'>Купить</a>
    <a class='buy'>Купить</a>
    <a class='buy'>Купить</a>
</div>
    

var container = document.querySelector('.container');
container.addEventListener('click', function (e) {
    if (e.target.className !== 'buy') {
        return;
    }
    console.log('buy!');
}, false);
    

Делегирование событий


    <div class='container'>
    <a class='buy'><span>Купить</span></a>
    <a class='buy'><span>Купить</span></a>
    <a class='buy'><span>Купить</span></a>
    </div>

var container = document.querySelector('.container');
container.addEventListener('click', function (e) {
    if (!e.target.closest('.buy')) {
      return;
    }
    console.log('buy!');
}, false);

Делегирование событий +-

  • Не надо отписываться от события
  • На надо подписывать на события
  • Один подписчик
  • Инициализацияи приложение происходит чуть быстрее
  • Обработчик становится сложнее

Домашнее задание.