## Работа с DOM в браузере
Слайды доступны по ссылке
frontend.tech-mail.ru

## Дисклеймер

Все практические примеры заливаются на git. Практика на лекциях демонстрирует самый простой подход к решению задач. Вам необходимо думать головой и пытаться сделать лучше, чем у нас

Мы всегда готовы помочь и дать совет, как это сделать наиболее хорошо

## Итак, начнём!

## Глобальные объекты и глобальная
область видимости

> Глобальными называют переменные и функции, которые не находятся внутри какой-то функции или блока

В JavaScript все глобальные переменные и функции являются свойствами специального объекта, который называется «глобальный объект» (global object). Присваивая или читая глобальную переменную, мы, фактически, работаем со свойствами глобального объекта.

## Глобальные объекты

В браузере это объект `window`

    		var foo = 42;
    		console.log(window.foo); // 42

    		const bar = 'top kek';
    		console.log(window.bar); // undefined

    		window.baz = 'JavaScript <3';
    		console.log(baz); // JavaScript <3

## Глобальные объекты

В Node.js тоже есть глобальный объект `global`

    		console.log(global);

    		var foo = 42;
    		console.log(global.foo); // 42

## Глобальные объекты

В глобальной области видимости `this` ссылается на глобальный объект

    		// в браузере
    		console.log(this === window); // true

## Глобальные объекты

В Node.js не всё так просто (попробуйте разобраться почему)

    		// script.js
    		console.log(this === global);

    		node script.js // false
    		node -p 'console.log(this === global)' // true

## Объекты в JavaScript

- Нативные объекты
- Хост объекты
- Пользовательские объекты

## Нативные объекты

> Нативными (native object) объектами в JS называют объекты, свойства и поведение которых описаны в спецификации языка JavaScript. Их наличие не зависит от того окружения, где запускается код

Например `Object`, `Array`, `Infinity`, `Date`, `Math`, `parseInt`, `eval`...

## Хост объекты

> Хост (host object) объектами в JS называют объекты, которые предоставляются окружением (зависят от того, где работает код)

Например, для браузеров это будут `document`, `location`, `history`, `XMLHttpRequest`, `setTimeout`, `setInterval`... Для Node.js хост-объекты будут другими, например `process`

## BOM — Browser Object Model

## DOM — Document Object Model

> Объектная Модель Документа (DOM) — это программный интерфейс (API) для HTML и XML документов. DOM предоставляет структурированное представление документа и то как эта структура может быть доступна из программ. По существу представление DOM соединяет веб страницу с языком JavaScript, позволяя изменять содержимое, стиль и структуру документа

## DOM-дерево

## DOM — Document Object Model

DOM нужен для того, чтобы манипулировать страницей — читать информацию из HTML, создавать и изменять элементы

    		document // хост-объект, который даёт доступ к DOM

    		document.documentElement // узел HTML
    		document.head // узел HEAD
    		document.body // узел BODY

## Перебор узлов DOM

    		element.parentNode // родительская нода

    		element.childNodes // дочерние ноды
    		element.firstChild // первый ребёнок
    		element.lastChild // последний ребёнок

    		document.previousSibling // предыдущий "сосед"
    		document.nextSibling // следующий "сосед"

## Перебор элементов DOM

    		element.parentElement // родитель-элемент
    		element.children // только дочерние узлы-элементы

    		// первый и последний дети-элементы
    		element.firstElementChild и element.lastElementChild

    		// соседи-элементы
    		document.previousElementSibling и document.nextElementSibling

## Дополнительные возможности

У конкретных элементов DOM могут быть свои дополнительные ссылки для большего удобства навигации

    		const elements = formElement.elements // все поля формы
    		elements[1] // выбор поля по порядку
    		elements['username'] // выбор поля по имени

## Дополнительные возможности

У конкретных элементов DOM могут быть свои дополнительные ссылки для большего удобства навигации

    		tableElement.rows // коллекция строк TR таблицы
    		tableElement.caption // элемент CAPTION таблицы
    		tableElement.tBodies // коллекция элементов таблицы TBODY

    		tableRowElement.cells // коллекция ячеек строки таблицы
    		tableRowElement.rowIndex // номер строки в таблице

## Поиск элементов по ID/name

    		// вернёт элемент с ID 'my-header'
    		document.getElementById('my-header')

    		// вернёт все элементы с атрибутом name="username"
    		document.getElementsByName('username')

## Поиск элементов по имени тега

    		// во всём документе
    		document.getElementsByTagName('h1')

    		// среди потомков какого-либо элемента
    		element.getElementsByTagName('h1')
    		element.getElementsByTagName('*') // все элементы

## Поиск элементов по имени класса

    		// во всём документе
    		document.getElementsByClassName('red-button')

    		// среди потомков какого-либо элемента
    		element.getElementsByClassName('red-button')

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

    		// во всём документе
    		document.querySelectorAll('input[type=button].red-button')

    		// среди потомков какого-либо элемента
    		element.querySelectorAll('input[type=button].red-button')

    		// ищет только первый элемент
    		element.querySelector('input[type=button].red-button')

## Методы `getElementsBy*(...)`
возвращают живую коллекцию

Pезультат запросов `getElementsBy*` — это не массив (`Array`), а специальный объект, имеющий тип `NodeList` или `HTMLCollection`. Он похож на массив, так как имеет нумерованные элементы и длину, но внутри это не готовая коллекция, а «живой поисковой запрос»

Собственно поиск выполняется только при обращении к элементам коллекции или к её длине

## Классы, иерархия DOM

## Свойства элементов

    		<h1>Ссылка: <a href="/">тут</a></h1>

    		element.tagName // имя тега
    		h1.tagName // "H1"

    		element.innerHTML // Внутреннее содержимое узла-элемента в виде HTML
    		h1.innerHTML // "Ссылка: <a href="/">тут</a>"

## Свойства элементов

    		<h1>Ссылка: <a href="/">тут</a></h1>

    		element.outerHTML // Полный HTML узла-элемента
    		h1.outerHTML // "<h1>Ссылка: <a href="/">тут</a></h1>"

    		element.textContent // Содержит только текст внутри элемента,
    		                    // за вычетом всех тегов
    		h1.textContent // "Ссылка: "

## Свойства элементов

    		<h1>Ссылка: <a href="/">тут</a></h1>

    		element.hidden // Атрибут, отвечающий за видимость элемента
    		h1.hidden = true // скроет заголовок

    		link.href // Адрес ссылки
    		input.value // Значение, введённое в текстовое поле

## Атрибуты элементов

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

    		elem.attributes // получить все атрибуты

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

    		document.createElement(tag) // создаёт элемент с тегом tag
    		document.createTextNode(value) // создает текстовый узел
    		element.cloneNode(deep) // клонирует элемент

    		parent.appendChild(el) // вставляет узел в конец
    		parent.removeChild(el) // удаляет узел
    		parent.replaceChild(newEl, oldEl) // заменяет узел
    		parent.insertBefore(elem, nextSibling) // вставляет узел

## Браузерные события

## События

Для реакции на действия посетителя и внутреннего взаимодействия скриптов существуют события

> Событие — это сигнал от браузера о том, что что-то произошло. Существует много видов событий

## События

События мыши:

- `click` – происходит, когда кликнули на элемент левой кнопкой мыши
- `contextmenu` – происходит, когда кликнули на элемент правой кнопкой мыши
- `mouseover` – возникает, когда на элемент наводится мышь
- `mousemove` – при движении мыши

## События

События на элементах управления:

- `submit` – посетитель отправил форму <form>
- `reset` – посетитель сбросил форму <form>
- `focus` – посетитель фокусируется на элементе, например нажимает на <input>

Клавиатурные события:

- `keydown` – когда посетитель нажимает клавишу
- `keyup` – когда посетитель отпускает клавишу

Список всех браузерных событий (~200 штук)

## Обработчики событий

> Событию можно назначить обработчик, то есть функцию, которая сработает, как только событие произошло. Именно благодаря обработчикам JavaScript-код может реагировать на действия посетителя

## Добавление обработчиков
через атрибут

    		<button onclick="this.parentElement.innerHTML+=
    		    <button onclick="this.parentElement.innerHTML+='<span>click</span>'">Нажми меня</button>

## Добавление обработчиков
через свойства DOM-объекта

    		let count = 0;
    		const element = document.getElementsByTagName('BUTTON')[0];
    		element.onclick = function() {
    		    element.innerHTML = `Кликнуто ${++count} раз(а)`;
    		}

## `addEventListener` и `removeEventListener`

    		// добавление обработчика
    		element.addEventListener(event, handler);

    		// удаление обработчика
    		// нужно передать те же аргументы, что были у addEventListener
    		element.removeEventListener(event, handler);

Подробнее про `addEventListener`

## Объект события `event`

    		element.addEventListener('click', function (event) {
    		    console.log(`${event.type} на ${event.currentTarget}`);
    		    console.log(`${event.clientX}:${event.clientY}`);
    		});

## Распространение событий
(events propagation)

## Фазы обработки событий

Строго говоря, стандарт выделяет целых три стадии прохода события:

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

## Перехват события на определённой
стадии

Используем третий аргумент `addEventListener`

    		// добавление обработчика
    		element.addEventListener(event, handler, phase);

    		// phase === true - событие будет перехвачено по дороге вниз
    		// phase === false - событие будет поймано при всплытии

    		// при удалении нужно передать те же аргументы,
    		// что были у addEventListener
    		element.removeEventListener(event, handler, phase);

## Предотвращение обработки
событий

    		event.preventDefault(); // предотвращает поведение по умолчанию
    		event.stopPropagation(); // предотвращает всплывание события
    		                         // (все обработчики на этом элементе выполнятся)

    		// останавливает обработку событий на текущем элементе
    		// (все остальные обработчики на этом элементе не выполнятся)
    		event.stopImmediatePropagation();

## Более гибкое добавление
обработчиков

В современных браузерах в третьем аргументе `addEventListener` можно передавать объект с настройками

    		// добавление обработчика
    		element.addEventListener(event, handler, options);

    		// options.capture - перехват события по дороге вниз
    		// options.once - обработчик будет вызван только один раз
    		// options.passive - говорит браузеру, что внутри обработчика не будет
    		//                   вызова preventDefault

    		// при удалении нужно передать те же аргументы
    		element.removeEventListener(event, handler, options);

## SPA — Single Page Application

> Одностраничное приложение (SPA) — это веб-приложение, использующее единственный HTML-документ как оболочку для всех веб-страниц и организующий взаимодействие с пользователем через динамически подгружаемые ресурсы и данные, обычно посредством AJAX

    		<html>
    		    <head> ... </head>
    		    <body>
    		        <div id="application"></html>
    		    </body>
    		</html>

## Практика!

## Работа с сетью

## Протокол HTTP

> HTTP (HyperText Transfer Protocol — «протокол передачи гипертекста») — протокол прикладного уровня передачи данных (изначально — в виде гипертекстовых документов в формате «HTML»), в настоящий момент используется для передачи произвольных данных

HTTP — текстовый протокол (имеются в виду версии протокола до версии HTTP/2)

## Протокол HTTP

## HTTP запрос

    		// METHOD URI HTTP/VERSION <= стартовая строка
    		GET /lib/slides.css HTTP/1.1
    		Host: frontend.tech-mail.ru
    		Content-Type: text/css; charset=utf-8
    		другие заголовки...

    		        тело запроса (опционально)

## HTTP ответ

    		// HTTP/VERSION STATUS_CODE REASON_PHRASE <= стартовая строка
    		HTTP/1.0 200 OK
    		Host: frontend.tech-mail.ru
    		Content-Type: text/css; charset=utf-8
    		другие заголовки...

    		        тело ответа (опционально)

## Работа с XMLHttpRequest

    		// 1. Создаём новый объект XMLHttpRequest
    		const xhr = new XMLHttpRequest();
    		// 2. Конфигурируем его: GET-запрос на URL '/lib/slides.css'
    		xhr.open('GET', '/lib/slides.css', false);

    		// 3. Отсылаем запрос
    		xhr.send();

## Работа с XMLHttpRequest

    		// 4. Если код ответа сервера не 200, то это ошибка
    		console.log(`Ответ от сервера: ${xhr.status} ${xhr.statusText}`);
    		if (xhr.status === 200) {
    		    console.log(xhr.responseText);
    		} else {
    		    console.error('Ошибка!')
    		}

## Пример POST-запроса

    		const xhr = new XMLHttpRequest();
    		xhr.open('POST', '/post', false);

    		// Выставляем заголовок
    		xhr.setRequestHeader('Content-Type', 'text/plain; charset=utf-8');
    		xhr.send('Request payload');
    		// ждём ответа

## Отправка json в теле запроса

    		const xhr = new XMLHttpRequest();
    		xhr.open('POST', '/post', false);

    		// Выставляем заголовок
    		xhr.setRequestHeader('Content-Type', 'application/json; charset=utf-8');
    		xhr.send(JSON.stringify({name: 'Alex'});
    		// ждём ответа

## Отправка бинарных данных в
теле запроса

    		const xhr = new XMLHttpRequest();
    		xhr.open('POST', '/post', false);

    		const binary = new Uint8Array(1024 * 1024); // 1 MB данных
    		const blob = new Blob([binary], {type: 'application/octet-stream'});
    		xhr.send(blob);
    		// ждём ответа

## Отправка файлов в теле запроса

    		const xhr = new XMLHttpRequest();
    		xhr.open('POST', '/post', false);

    		const fileInput = document.querySelector('input[type=file]');
    		const file = fileInput.files[0].file;
    		const formdata = new FormData();
    		formdata.append('file', file);
    		xhr.send(formdata);
    		// ждём ответа

## Асинхронные запросы

    		const xhr = new XMLHttpRequest();
    		xhr.open('GET', '/lib/slides.css', true);

    		xhr.onreadystatechange = function() {
    		    if (xhr.readyState !== 4) return;
    		    console.log(`Ответ от сервера: ${xhr.status} ${xhr.statusText}`);
    		    console.log(xhr.responseText);
    		}
    		xhr.timeout = 5000; // 5 секунд (в миллисекундах)
    		xhr.send();

## Событие `readystatechange`

    		// Значения readyState
    		const unsigned short UNSENT = 0; // начальное состояние
    		const unsigned short OPENED = 1; // вызван open
    		const unsigned short HEADERS_RECEIVED = 2; // получены заголовки
    		const unsigned short LOADING = 3; // загружается тело
    		const unsigned short DONE = 4; // запрос завершён

## Другие события

- `loadstart` — запрос начат
- `progress` — браузер получил очередной пакет данных
- `abort` — запрос был отменён вызовом `xhr.abort()`
- `error` — произошла ошибка
- `load` — запрос был успешно (без ошибок) завершён
- `loadend` — запрос был прекращён по таймауту
- `readystatechange` — запрос был завершён (успешно или неуспешно)

## Cookies

## HTTP is a stateless protocol

## Cookies

> Куки (cookies) — небольшой фрагмент данных, отправленный веб-сервером и хранимый на компьютере пользователя. Веб-клиент (обычно веб-браузер) всякий раз при попытке открыть страницу соответствующего сайта пересылает этот фрагмент данных веб-серверу в виде HTTP-запроса

## Спецификация cookies

- Браузер должен хранить как минимум 4096 байт кук
- Минимум 20 шт. на домен
- Минимум 300 шт. всего
- Имена нечувствительны к регистру

## 1 Клиент → Сервер

    		GET / HTTP/1.1
    		Host: example.com

## 2 Клиент ← Сервер

    		HTTP/1.1 200 OK
    		Set-Cookie: name=value
    		Content-Type: text/html

## 3 Клиент → Сервер

    		GET / HTTP/1.1
    		Host: example.com
    		Cookie: name=value

## 4 Клиент ← Сервер

    		HTTP/1.1 200 OK
    		Set-Cookie: name=value2
    		Content-Type: text/html

## Использование

- Аутентификация пользователей
- Хранение настроек пользователей
- Отслеживание сеанса (сессия)
- Сбор статистики
- Проведение экспериментов
- etc...

## Set-Cookie

    		Set-Cookie: value
    		                 [;X: Expires=date]
    		                 [;X: Max-Age=age]
    		                 [;X: Domain=domain]
    		                 [;X: Path=path]
    		                 [;X: Secure]
    		                 [;X: HttpOnly]

## Cookie ID

    		Set-Cookie: name=v1; domain=.example.com; path=/
    		Set-Cookie: name=v2; domain=www.example.com; path=/
    		Set-Cookie: name=v3; domain=.example.com; path=/archive

    		Cookie: name=v1; name=v2; name=v3

## JavaScript

    		document.cookie; /* 'name1=value1' */
    		document.cookie = 'name2=value2';
    		document.cookie; /* 'name1=value1; name2=value2' */

## Credentials вместе с XHR

    		const xhr = new XMLHttpRequest();
    		xhr.open('GET', '/auth', true);

    		xhr.withCredentials = true; // отправляем cookies

    		xhr.send();

## Итого: авторизация через куки

- Клиент не работает с куками
- При запуске приложения, отправляем запрос за текущим пользователем
- Если на бекенд приходит запрос без кук — значит пользователь не авторизован, возвращаем 401
- Если на бекенд приходит запрос c куками — значит пользователь авторизован, возвращаем 200
- На клиенте, в зависимости от ответа сервера, рисуем либо профиль пользователя, либо форму авторизации

## Практика!

## Полезные ссылки

- Разница между хост-объектами и нативными объектами описана здесь
- Описание всех нативных глобальных объектов здесь
- "Что такое DOM?" — статья на Frontender Magazine
- Статья на MDN про DOM
- Подробнее про события в JS

## Всем спасибо!