Технопарк, весна, 2019 г.
Все практические примеры заливаются на 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
Нативными (native object) объектами в JS называют объекты, свойства и поведение которых описаны в спецификации языка JavaScript. Их наличие не зависит от того окружения, где запускается код
Например Object
, Array
, Infinity
, Date
, Math
, parseInt
, eval
...
Хост (host object) объектами в JS называют объекты, которые предоставляются окружением (зависят от того, где работает код)
Например, для браузеров это будут document
, location
, history
, XMLHttpRequest
, setTimeout
, setInterval
... Для Node.js хост-объекты будут другими, например process
Объектная Модель Документа (DOM) — это программный интерфейс (API) для HTML и XML документов. DOM предоставляет структурированное представление документа и то как эта структура может быть доступна из программ. По существу представление DOM соединяет веб страницу с языком JavaScript, позволяя изменять содержимое, стиль и структуру документа
DOM нужен для того, чтобы манипулировать страницей — читать информацию из HTML, создавать и изменять элементы
document // хост-объект, который даёт доступ к DOM
document.documentElement // узел HTML
document.head // узел HEAD
document.body // узел BODY
element.parentNode // родительская нода
element.childNodes // дочерние ноды
element.firstChild // первый ребёнок
element.lastChild // последний ребёнок
document.previousSibling // предыдущий "сосед"
document.nextSibling // следующий "сосед"
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 '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')
// во всём документе
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
. Он похож на массив, так как имеет нумерованные элементы и длину, но внутри это не готовая коллекция, а «живой поисковой запрос»
Собственно поиск выполняется только при обращении к элементам коллекции или к её длине
<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
– когда посетитель отпускает клавишуСобытию можно назначить обработчик, то есть функцию, которая сработает, как только событие произошло. Именно благодаря обработчикам JavaScript-код может реагировать на действия посетителя
<button onclick="this.parentElement.innerHTML+=
<button onclick="this.parentElement.innerHTML+='<span>click</span>'">Нажми меня</button>
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);
event
element.addEventListener('click', function (event) {
console.log(`${event.type} на ${event.currentTarget}`);
console.log(`${event.clientX}:${event.clientY}`);
});
Строго говоря, стандарт выделяет целых три стадии прохода события:
Используем третий аргумент 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) — это веб-приложение, использующее единственный HTML-документ как оболочку для всех веб-страниц и организующий взаимодействие с пользователем через динамически подгружаемые ресурсы и данные, обычно посредством AJAX
<html>
<head> ... </head>
<body>
<div id="application"></html>
</body>
</html>
HTTP (HyperText Transfer Protocol — «протокол передачи гипертекста») — протокол прикладного уровня передачи данных (изначально — в виде гипертекстовых документов в формате «HTML»), в настоящий момент используется для передачи произвольных данных
HTTP — текстовый протокол (имеются в виду версии протокола до версии HTTP/2)
// METHOD URI HTTP/VERSION <= стартовая строка
GET /lib/slides.css HTTP/1.1
Host: frontend-park-mailru.firebaseapp.com
Content-Type: text/css; charset=utf-8
другие заголовки...
тело запроса (опционально)
// HTTP/VERSION STATUS_CODE REASON_PHRASE <= стартовая строка
HTTP/1.0 200 OK
Host: frontend-park-mailru.firebaseapp.com
Content-Type: text/css; charset=utf-8
другие заголовки...
тело ответа (опционально)
// 1. Создаём новый объект XMLHttpRequest
const xhr = new XMLHttpRequest();
// 2. Конфигурируем его: GET-запрос на URL '/lib/slides.css'
xhr.open('GET', '/lib/slides.css', false);
// 3. Отсылаем запрос
xhr.send();
// 4. Если код ответа сервера не 200, то это ошибка
console.log(`Ответ от сервера: ${xhr.status} ${xhr.statusText}`);
if (xhr.status === 200) {
console.log(xhr.responseText);
} else {
console.error('Ошибка!')
}
const xhr = new XMLHttpRequest();
xhr.open('POST', '/post', false);
// Выставляем заголовок
xhr.setRequestHeader('Content-Type', 'text/plain; charset=utf-8');
xhr.send('Request payload');
// ждём ответа
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-запроса
GET / HTTP/1.1
Host: example.com
HTTP/1.1 200 OK
Set-Cookie: name=value
Content-Type: text/html
GET / HTTP/1.1
Host: example.com
Cookie: name=value
HTTP/1.1 200 OK
Set-Cookie: name=value2
Content-Type: text/html
Set-Cookie: value
[;X: Expires=date]
[;X: Max-Age=age]
[;X: Domain=domain]
[;X: Path=path]
[;X: Secure]
[;X: HttpOnly]
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
document.cookie; /* 'name1=value1' */
document.cookie = 'name2=value2';
document.cookie; /* 'name1=value1; name2=value2' */
const xhr = new XMLHttpRequest();
xhr.open('GET', '/auth', true);
xhr.withCredentials = true; // отправляем cookies
xhr.send();