Технопарк, весна, 2019 г.
Все практические примеры заливаются на git. Практика на лекциях демонстрирует самый простой подход к решению задач. Вам необходимо думать головой и пытаться сделать лучше, чем у нас
Мы всегда готовы помочь и дать совет, как это сделать наиболее хорошо
Глобальными называют переменные и функции, которые не находятся внутри какой-то функции или блока
В JavaScript все глобальные переменные и функции являются свойствами специального объекта, который называется «глобальный объект» (global object). Присваивая или читая глобальную переменную, мы, фактически, работаем со свойствами глобального объекта.
В браузере это объект window
var foo = 42;console.log(window.foo); // 42const bar = 'top kek';console.log(window.bar); // undefinedwindow.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.jsconsole.log(this === global);
node script.js // falsenode -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 // хост-объект, который даёт доступ к DOMdocument.documentElement // узел HTMLdocument.head // узел HEADdocument.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 // коллекция элементов таблицы TBODYtableRowElement.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 // Внутреннее содержимое узла-элемента в виде HTMLh1.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) // создаёт элемент с тегом tagdocument.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);// удаление обработчика// нужно передать те же аргументы, что были у addEventListenerelement.removeEventListener(event, handler);
eventelement.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 - событие будет поймано при всплытии// при удалении нужно передать те же аргументы,// что были у addEventListenerelement.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.1Host: frontend-park-mailru.firebaseapp.comContent-Type: text/css; charset=utf-8другие заголовки...тело запроса (опционально)
// HTTP/VERSION STATUS_CODE REASON_PHRASE <= стартовая строкаHTTP/1.0 200 OKHost: frontend-park-mailru.firebaseapp.comContent-Type: text/css; charset=utf-8другие заголовки...тело ответа (опционально)
// 1. Создаём новый объект XMLHttpRequestconst 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// Значения readyStateconst unsigned short UNSENT = 0; // начальное состояниеconst unsigned short OPENED = 1; // вызван openconst 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.1Host: example.com
HTTP/1.1 200 OKSet-Cookie: name=valueContent-Type: text/html
GET / HTTP/1.1Host: example.comCookie: name=value
HTTP/1.1 200 OKSet-Cookie: name=value2Content-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; // отправляем cookiesxhr.send();