Презентации доступны по ссылке https://xrem.github.io/web/
Всемирная паутина (World Wide Web, или же просто Web) - распределенная система компьютеров, предоставляющая доступ к связанным между собой документам
Сейчас же веб используется для просмотра веб-страниц с контентом, таким как текст, видео, музыка и другое, но что же происходит, когда мы вводим адрес веб-сайта в строке браузера?
Для этого браузер, специальная программа для доступа и обработки веб-страниц, отправляет запрос DNS-серверу
В сети Интернет для непосредственного доступа к другому компьютеру не используются URL-адреса (Uniform Resource Locator), которыми мы пользуемся и которые мы легко понимаем, например itmo.ru. Вместо них используются IP-адреса (например, 247.207.70.150), которые присвоены каждому компьютеру в Интернете
Чтобы по URL-адресу получить IP-адрес, браузер делает запрос DNS-серверу, который возвращает IP-адрес сервера
Подробнее про DNS - https://pelmesh619.github.io/itmo_conspects/telecomm/telecomm_superconspect.html#-%D0%BB%D0%B5%D0%BA%D1%86%D0%B8%D1%8F-12-dns
Далее браузер делает запрос этому серверу на получение веб-страницы. Основным протоколом в обмене веб-страниц и совершении других запросов является HTTP, HyperText Transfer Protocol (Протокол передачи гипертекста). HTTP работает поверх протокола TCP. Сейчас использует более безопасная версия HTTPS (HTTP Secured). Для создания запроса браузер создает TCP-соединение между текущим компьютером и сервером. Для браузеры выбирается случайный TCP-порт больше 1024, а для сервера порт заранее известен: 80 для HTTP и 443 для HTTPS. Имя протокола указывается в начале полного адреса, например, https://itmo.ru
Веб-сервер, компьютер, принявший запрос, имеет специальное программное обеспечение, которое обрабатывает запрос. Основной функцией веб-сервера является хранение, обработка и передача веб-страниц. Имя страницы представляет собой путь после доменного имени и в простейшем случае может быть файлом на веб-сервере, например, https://itmo.ru/promo/itmo-logo-dark.svg
Если веб-страница по данному адресу существует, то сервер отправляет ее. Ответ содержит HTTP-код, например, 200, что означает успешный запрос. Если запрошенной страницы нет, то отправляется ответ с кодом 404 и страница-заглушка. Дополнительно, сервер может отправить код 304, что означает, что страница не изменилась, и браузер может загрузить ее из своего кеша
Далее TCP-соединение закрывается, и полученный контент обрабатывается. Сейчас все веб-страницы представляют из себя набор документов из HTML-файла, стилей и скриптов. Браузер обрабатывает HTML-код, обнаруживает дополнительные ресурсы, которые нужны для работы страницы, такие, как изображения, стили, скрипты и прочее, и загружает их, делая дополнительные запросы

После этого браузер дополнительно анализируется HTML-код, содержащий в себе структуру страницы. HTML-код, состоящий из тегов, парсится, создается документно-объектная модель страницы
Далее обрабатываются стили, задающие обертку блоков, форматирование текста и другую стилизацию страницы, после чего страница рендерится в окне браузера. Затем запускаются скрипты, написанные на языке JavaScript, которые делают страницу интерактивной
Основная задача фронтенд-разработчика - создать надежный, быстрый и удобный мост между бэкендом и пользователем, реализовав весь сложный интерфейс, который работает на стороне клиента, несмотря на то, какое устройство имеет пользователь, телефон с маленьким экраном и широкий дисплей
Структура веб-страницы создается с помощью языка верстки HTML (HyperText Markup Language, язык гипертекстовой разметки). В основе этого языка лежат теги в формате <tag></tag> или <tag>, которые указывают семантику содержимого внутри себя контента и содержат дополнительные атрибуты <tag key="value">
Каждый HTML-документ начинается с определения его типа, чтобы браузер мог понимать, как его обрабатывать:
<!DOCTYPE html>
В старой версии HTML до его общего принятия тип документа определялся так:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
Далее идет тег <html></html>. В нем указываются теги <head></head> и <body></body>. Тег head содержит служебную информацию о странице, такую как:
<link>, например, CSS-файлы со стилями или скрипты на JavaScript<title></title><!DOCTYPE html>
<html>
<head>
<!-- Заголовок -->
<title>Web программирование</title>
<!-- Подключение стилей -->
<link href="styles.css" rel="stylesheet">
<!-- ^ ссылка ^ типа файла (то есть таблица стилей) -->
<meta charset="utf-8">
<!-- ^ кодировка -->
<meta name="keywords" content="web, веб, программирование">
<!-- ^ ключевые слова -->
<meta name="description" content="краткое описание">
<!-- ^ описание -->
</head>
<body>
</body>
</html>
В общем случае теги в head не отображаются непосредственно на странице, но влияют на ее обработку в браузере и поиск в поисковых движках
Далее идет тег <body></body>. Он представляет тело страницы и обычно имеет такую структуру:
<body>
<header></header>
<main></main>
<footer></footer>
</body>
Тег main выделяет основное содержание страницы. На странице должен быть только один видимый main. Дополнительные блоки main возможны только если они скрыты (например, с помощью атрибута hidden) или не участвуют в рендеринге
Тег main окружается тегами header и footer. Первый предназначенный для вводной части страницы (так называемой шапки), где представлены логотип компании и главные ссылки на другие страницы (“Каталог”, “О нас” и прочее)
Тег footer предназначен для заключительной части (так называемого подвала), где представлены дополнительные ссылки (например, на юридические документы) и контактная информация
Примерная HTML-страница выглядит так:
<!DOCTYPE html>
<html>
<head>
<title>Рога и копыта</title>
</head>
<body>
<header>Компания "Рога и копыта"</header>
<main></main>
<footer>+7 (777) 77-77-77</footer>
</body>
</html>
Помимо них также существуют эти теги, обозначающие другие блоки страницы:
<section></section> - секция текста, то есть крупный фрагмент текста, схожий по смыслу, например, главы одной книги или “Каталог”. Если текст можно осмысленно назвать одним словосочетанием, которое не определяет его положение на странице или не является перечислением, то ему явно подходит тег section<nav></nav> (от navigation) - навигация страницы, то есть группа ссылок на другие страницы или на раздел в текущей<article></article> - статья, цельный и законченный фрагмент текста. В отличие от секции содержимое внутри article можно скопировать из этой страницы и вставить в другое место без потери смысла и контекста<h1></h1>, <h2></h2>, <h3></h3>, <h4></h4>, <h5></h5> и <h6></h6> обозначают заголовки (от heading) разных уровней. Заголовок первого уровня самый важный, и лучше всего, чтобы он был один на странице (обычно в шапке), тогда как шестого - очень не важный. На практике используются только h1, h2 и h3<p></p> обозначает параграф (от paragraph) или абзац. По умолчанию параграфы отделяются отступами сверху и снизу от другого контента. Текст может существовать и снаружи тегов p, но он позволяет явно выделять параграфы<ul></ul> обозначает неупорядоченный список (от unordered list), а <ol></ol> обозначает упорядоченный список (от ordered list). Внутри них элементы списка находятся в теге <li></li> (от list item)
Тег ol может иметь дополнительные атрибуты: reversed для смены направления нумерации, start="67" для установки стартового числа и type="1" для указания типа нумерации (1 для чисел, a и A для латинских букв, i и I для римских чисел)
Также списки можно вкладывать внутри других списков
Другой список <dl></dl> (от description list) позволяет перечислять термины в тегах <dt></dt> (от description term) и их определения в <dd></dd> (от description definition). Обычно определения переносятся на новую строку, и браузер добавляет к ним отступ слева
<pre></pre> обозначает предварительно отформатированный текст (preformatted text). На странице он отображается моноширинным шрифтом и с сохранением пробелов и переносов, что чаще всего используется для вставок с кодом
<code></code> обозначает вставку с кодом. На странице он отображается моноширинным шрифтом, но в отличии от pre в теге code пробелы схлопываются, а переносы игнорируются, поэтому теги используют вместе: <pre><code></code></pre>
Чтобы экранировать знаки меньше и больше во вставке с кодом, используются мнемоники - наборы символов, позволяющие в браузере отображать специальные символы, использующиеся в HTML. Так “<” - это <, “>” - это >, “&” - это &, “€” - это €, “©” - это © и так далее
В тег <q></q> (от quote) вкладывается цитата из стороннего источника. В атрибут cite указывается адрес цитаты: <q cite="https://ru.wikiquote.org/wiki/%D0%A3%D0%B8%D0%BD%D1%81%D1%82%D0%BE%D0%BD_%D0%A7%D0%B5%D1%80%D1%87%D0%B8%D0%BB%D0%BB%D1%8C">Сильный, молчаливый мужчина слишком часто лишь потому молчалив, что ему нечего сказать.</q>. Дополнительно в теге <cite> указывается автор: <cite>Уинстон Черчилль</cite>
В тег <blockquote></blockquote> тоже указывается цитата, но вместо текста как в теге q она представляет из себя отдельный блок
Тег <br> (от line BReak) обозначает перенос строки. По умолчанию новая строка в HTML-файле не рендерится в браузере, поэтому переносы создаются либо тегом <br>, либо тегами, которые автоматически переносят последующий контент на новую строку
Однако для разделения тексты на абзацы лучше использовать не его, а отдельные теги <p>
Теги <sub></sub> и <sup></sup> создают нижние и верхние индексы соответственно, например, в формулах: H<sub>2</sub>O - H2O
Тег <time></time> содержит в себе текст, означающий дату и время, но его атрибутом могут быть указаны дата и время в формате ISO 8601 для машинной обработки: <time datetime="2020-11-15T09:54">09:54 утра</time>
Теги форматирования текста:
<i></i> (от italic) и <em></em> (от emphasis), тег em используется для логического акцента, а i для визуального выделения без смыслового акцента (например, термины)<b></b> (от bold) и <strong></strong>, тегом strong выделяется семантически супер важный и серьезный текст<del></del> (от delete), используется для удаленного текста<ins></ins> (от insert) и <u></u> (от underline), ins используется преимущественно для выделения добавленного текстаНаконец, если подходящего тега с нужной семантикой нет, то используют два общих тега: <div></div> и <span></span>
Тег div используется для блока, а span - для текстовой фразы или ее фрагмента. Теги div и span используется только в тех случаях, в который не нашлось других тегов с подходящей семантикой и смыслом
Чтобы придать стиля веб-странице, используют таблицы стилей, написанные на языке CSS (Cascading Style Sheets, каскадные таблицы стилей). Обычно они пишутся в отдельном файле, подключаемом с помощью тега <link>, но также и могут находиться в исходной HTML-странице в теге <style></style>
Правила стилей состоят из селектора, который определяет теги, к которым применяются стили, и блок объявлений, состоящих из свойства и значения:
селектор {
свойство1: значение1;
свойство2: значение2;
свойство3: значение3;
/* ... */
}
Так селектором могут быть:
*, указывающий на все элементыp, div, h1<p id="my-paragraph"></p>, в таком случае селектор указывается #my-paragraph<p class="my-sections"></p>, в таком случае селектор указывается .my-sectionsДалее к ним применяются могут применяться особые условия:
div:first-child - первый ребенок внутри divdiv:last-child - последний ребенок внутри divdiv:nth-child(i) - i-ый ребенок внутри divp:hover указывает на параграф, на который наведен курсорp::first-letter применяются на первую букву параграфакомбинации селекторов:
div, p - все параграфы p и все блоки divdiv p - все параграфы p внутри блока divdiv > p - все параграфы p, которые являются непосредственными детьми блока divdiv + p - параграф p, которые стоит непосредственно после блока divdiv ~ p - все параграфы p, которые стоят после блока divdiv[foo="bar"] - блоки div с атрибутом foo="bar"CSS-стили могут выглядеть так:
/* для всех элементов цвет текста оранжевый */
* {
color: orange;
}
/* для всех блоков div фоновый цвет серый */
div {
background-color: gray;
}
/*
для параграфов с классами `mytext` и `quote`
цвет текста красный
*/
p.mytext.quote {
color: red;
}
/*
для элементов с идентификатором example
цвет текста зеленый
*/
#example {
color: green;
}
Приоритет правил определяется специфичностью селектора, а при равной специфичности - последним объявлением в коде.
Свойств и значений для них в CSS есть огромное множество. Хорошей практикой является использование классов для применения стилей к элементам, а не идентификаторов
По умолчанию, каждый браузер применяет свои собственные стили для элементов (например, использует свой шрифт), поэтому контент может по-разному отображаться в разных браузерах. В таком случае имеет смысл применять сброс CSS - специальный набор правил, которые упраздняют их. Один из самых популярных от Эрика Мейера выглядит так:
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
Также используется сброс стилей normilize.css
Каждому HTML-элементу соответствует прямоугольная область, или бокс. Боксы бывают блочными и строчными
Блочные бокс представляется собой крупную неразрывную область. Блочными боксами обладают теги <p>, <h1>, <h2>, <ul> и так далее. В частности, тег <div>, благодаря которому можно создать сетку элементов, обозначает простой прямоугольный контейнер и обладает блочным боксом
Блочные боксы:
Имеют задаваемые ширину, высоту, внутренние отступы (padding) и внешние отступы (margin)

Строчные боксы располагаются друг за другом в одной строке, могут разрываться и находиться на нескольких строках. Строчными боксами обладают такие элементы, как <span>, <a>, <i>, <em>, <b>, <strong>, и предназначены для оформления текста
Строчные боксы не имеют переносов до и после бокса, а также для строчных боксов можно задавать только горизонтальные отступы
При рендере HTML-страницы создает поток элементов - порядок их отображения на странице. Блочные элементы располагаются в поток сверху вниз, а строчные - слева направо. Элемент считается выпавшим из потока, если он не влияет на расположение последующих блоков
По умолчанию, блочные боксы имеют ширину родительского блока, а высота зависит от содержимого (если бокс пустой, то 0px). Ширина и высота боксов задаются с помощью свойств width и height, например:
selector {
width: 100px;
height: 100px;
}
Чтобы вернуть значения по умолчанию, можно воспользоваться значением auto:
selector {
width: auto;
height: auto;
}
Строчные боксы имеют ширину и высоту ровно столько, сколько занимает контент внутри, и не реагируют на свойства width и height
Внутренние отступы (или поля) задаются свойством padding:
selector {
padding: 10px; /* отступ 10 пикселей со всех сторон */
}
Есть 4 формы записи:
padding: 10px; - 10 пикселей со всех сторонpadding: 10px 50px; - 10 пикселей сверху и снизу, 50 пикселей слева и справаpadding: 10px 50px 30px; - 10 пикселей сверху, 30 пикселей слева и справа, 30 пикселей снизуpadding: 10px 50px 30px 80px; - 10 пикселей сверху, 50 пикселей справа, 30 пикселей снизу, 80 пикселей слева
Можно задать отступ для каждой стороны:
selector {
padding-left: 10px; /* слева */
padding-right: 10px; /* справа */
padding-top: 10px; /* сверху */
padding-bottom: 10px; /* снизу */
}
Аналогично задаются внешние отступы от границы с помощью свойств margin, margin-top, margin-bottom, margin-left, margin-right
Также для боксов можно указать границу (или рамку), а именно:
border-style, существуют сплошная solid, пунктирная dashed, точками dotted, двойная doubleborder-widthborder-colorУ этих свойств также есть конкретные для каждой стороны, например border-left-width. С помощью свойства border можно указать сокращенную запись этих параметров в любой порядке:
selector {
border: 20px solid red;
border-left: 20px dashed green;
border-bottom: 10px solid blue;
}

Также углы рамок можно скруглять под определенным радиусом с помощью свойств border-radius, border-top-radius, border-left-radius, border-right-radius, border-bottom-radius
Занимаемое место бокса на странице вычисляется как сумма
Вертикальные отступы двух соседних элементов имеют свойство схлопываться, тогда интервал между ними становится равным максимуму отступов этих элементов

Также внешние отступы схлопываются, если у родительского элемента есть внешний отступ, нет внутреннего, а у первого (или последнего) элемента есть внешний отступ, тогда этот отступ будет выпадать за пределы родительского элемента. Фиксится такой прикол при помощи внутреннего отступа у родителя с нужной стороны
Теперь можно научиться базовому приему центрирования элемента:
autoПолучаем:
div {
width: 70%;
margin: 0 auto;
}
Ширину для элемента можно указать в процентах от ширины родительского элемента. Нужно быть аккуратным, так как если родительский элемент имеет внутренний отступ слева и справа, то width: 100% может привести к выпаданию элемента за пределы
Кроме auto такое исправить можно с помощью изменения свойства box-sizing. По умолчанию, оно равно content-box, что определяет ширину и высоту для области с контентом. Значение border-box установит, что ширина и высота задана для области контента, внутренних отступов и границ

Тип бокса можно изменить с помощью свойства display
display: block; установит блочный тип боксаdisplay: inline; установит строчный тип боксаdisplay: inline-block; установит блочно-строчный тип бокса
Блочно-строчному типу бокса можно задавать размеры и отступы, их ширина зависит от содержания, но такой тип не порождает переносы строки до и после себя
display: none; сделает так, что элемент перестанет рендериться и не повлияет на расположение других (в отличии от visibility: hidden, при котором элемент не выпадает из потока)display: table; задает табличный бокс - он похож на блочный, только ширина зависит от контента внутриПозиционирование элемента также можно изменить. Тип позиционирования можно выбрать с помощью свойства position:
position: static; задает статичное позиционирование и стоит по умолчанию, при нем свойства top, left, right, bottom не оказывают эффектposition: relative задает позиционирование относительно исходного положения блокаposition: absolute задает позиционирование абсолютное, при этом блок выходит из потока, тем самым не влияя на расположение других, а свойства top, left, right, bottom указывают положение относительно ближайшего предка, имеющего position != staticposition: fixed задает положение относительно окна браузера, при этом блок выходит из потокаposition: sticky не меняет исходного положения, но при прокрутке вместо того, чтобы выйти за границы окна браузера, элемент остается на указанное расстоянииПозиционирование задается с помощью свойств top, left, right, bottom. Например, top: 10px; position: relative; сделает элемент на 10 пискелей ниже исходного положения, а right: 20px; position: relative; сделает его на 20 пикселей правее от исходного положения. Также можно применять отрицательные значения

Как можно заметить в примере, элементы перекрывают друг друга. Чтобы изменить порядок перекрытия, нужно указать свойства z-index для элемента. Чем больше число z-index, тем выше приоритет того, что элемент окажется выше других
Также элементу можно включить обтекание с помощью свойства float:
float: left; прижимает элемент к левому краю родителя, другие строчные элементы обтекают его справаfloat: right; - то же самое, но с правой стороныfloat: none; - отключает обтекание, по умолчаниюЕсли float указан у строчного, то его поведение проявляется как у блочного. Блоки с float выпадают их потока. Если у нескольких обтекаемых элементов нет места, то они переносятся на следующих строчки - таким образом создавались сетки элементов в 2000-ых до появления display: flex и display: grid. Сейчас для сеток лучше использовать flex или grid, так как поведение переноса обтекаемых элементов может быть неочевидным
Свойство clear: left запрещает обтекание слева, clear: right - справа, clear: both - с обеих сторон
Помимо display: block и display: inline для выравнивания можно использовать display: flex. Значение flex превращает блоки во флекс-контейнеры, внутри которых можно менять направление потока элементов
Внутри флекс-контейнера существует главная ось, вдоль которой следует поток, и которая задается с помощью свойства flex-direction:
flex-direction: row задает главную ось слева направо, что используется по умолчаниюflex-direction: row-reverse задает главную ось справа налевоflex-direction: column задает главную ось сверху внизflex-direction: column-reverse задает главную ось снизу вверхС помощью свойства justify-content можно выравнять элементы относительно главной оси:
justify-content: flex-start располагает все элементы в начале осиjustify-content: flex-end располагает все элементы в конце осиjustify-content: center располагает все элементы в центре осиjustify-content: space-between располагает все элементы равномерно, оставляя между ними равные промежуткиjustify-content: space-around располагает все элементы равномерно, оставляя между ними равные промежутки, но до первого и после последнего выделяется половина пространства от того, что уходит между элементамиjustify-content: space-evenly располагает равные промежутки между элементами, до первого элемента и после последнегоПоперечная ось расположена перпендикулярно и направлена вниз или направо в зависимости от направления главной. Вдоль поперечной оси работает выравнивание элементов с помощью свойства align-items:
align-items: flex-start располагает все элементы в начале осиalign-items: flex-end располагает все элементы в конце осиalign-items: center располагает все элементы в центре осиalign-items: stretch растягивает все элементы так, что бы вдоль поперечной оси не было пустого пространстваalign-items: baseline располагает все элементы так, что базовая линия текста элементов одинаковаЕсли нужно другое выравнивание по поперечной оси для отдельного элемента, для него применяют свойство align-self с теми же значениями, что и align-items
Верстка сайта - этап разработки, когда макет дизайна, на котором указаны расположения элементов на странице, становится функциональным, то есть превращается в HTML-структуру
Верстка включает также в себя подготовку изображений и прочей графики, подключение шрифтов, подключение JavaScript-библиотек, создание динамического поведения элементов и тестирования страницы на различных устройствах
Рассмотрим виды верстки:
Фреймовая верстка
Фрейм - специальный блок, который позволял рендерить в нем отдельный HTML-документ. С помощью них до версии HTML 5 создавались независимые секции из повторно используемых компонентов
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN">
<html>
<head><title>Website with Frames</title></head>
<body>
<frameset cols="25%,75%">
<frame src="navigation.html" name="nav">
<frame src="content.html" name="main">
</frameset>
<body>
</html>
Проблем с ними было много:
<frame src="content.html" name="main">Табличная верстка
Табличная верстка заключалась в том, что бы использовать невидимые таблицы для размещения контента. Такая таблица фактически представляла собой модульную сетку, в которой размещались отдельные элементы веб-страницы
Достоинства:
Недостатки:
Блочная верстка
В блочной верстки страница делится на блоки, которые содержат в себе контент. Они вкладываются друг в друга, таким образом уменьшается нагромождение кода, структура понятна, а править верстку легко
Сейчас преимущественно применяется блочная верстка

Далее верстка делится по степени адаптивности:
Статическая или фиксированная верстка (Static)
Ширина элементов задается жестко в пикселях, из-за чего сайт выглядит одинаково на любом мониторе. Если контент не влезает по горизонтали, то появляется полоса прокрутки

Резиновая (Liquid или Fluid)
Размеры элементов указываются в процентах от ширины родительского контейнера, поэтому контент растягивается или сжимается вместе с окном браузера, заполняя всё доступное пространство. Однако, если окно браузера слишком маленькое, контент ужимается в узкие полосы

Гибкая или адаптивная (Adaptive)
В адаптивной верстке используются медиа-запросы (через @media), что позволяет для разных устройств (смартфон, планшет, компьютера) создавать отдельные фиксированные макеты. При достижении определенной ширины дизайн переключается “скачком”

Отзывчивая (Responsive)
Отзывчивая верстка - это комбинация резиновой и адаптивной верстки. В ней сайт плавно меняет размеры элементов, а в ключевых точках меняет структуру макета, например, выстраивая блоки из нескольких колонок в одну
Верстка считается правильной, если соответствует нескольким критериям:
Хорошими практиками являются:
CSS-фреймворки могут помочь достичь хорошой верстки. Они содержат готовый набор стилей, компонентов и утилит. Разберем популярные:
Bootstrap - классический CSS-фреймворк с готовыми компонентами. В нем есть сетка на флекс-блоках, готовые компоненты и JavaScript-плагины
Лучше всего использовать для админ-панели, минимально жизнеспособного продукта или учебных проектов
Tailwind CSS. В нем почти нет готовых компонентов, и всё собирается из классов, поэтому тут полный контроль над дизайном, минимальный итоговый CSS, но HTML перегружен классами
Лучше использовать для одностраничных приложений, современных фронтенд-проектов и для кастомного дизайна
Framework7 - фреймворк для мобильных и прогрессивных веб-приложений. Создает стили iOS- или Android-приложений, имеет мобильные компоненты, работает с Vue, React, Svelte
Material Design - дизайн-система от Google, имеющая строгие правила дизайна, тени, уровни, анимации и консистентный опыт пользователя
Лучше использовать для Android-ориентированных продуктов и больших приложений
В CSS для указания длины есть разные единицы
Самая базовая из них - это пиксели px. Пиксели - абсолютная величина, браузер переводит все остальные единицы в пиксели, поэтому пиксели являются понятной величиной, однако с помощью пикселей трудно создать адаптивную веб-страницу
Другая величина em обозначает высоту текущего шрифта и определяются по текущему контексту. Например, в таком примере:
<div style="font-size:2em">
Арбуз
<div style="font-size:2em">Абрикос</div>
</div>
второе слово будет иметь шрифт высотой в два раза больше, чем первое
Проценты % - еще одна относительная единица. 1% - это одна сотая от ширины/высоты родительского элемента. Для свойства line-height, определяющего высоту пространства, занимаемого текстом, 1% - это одна сотая от размера шрифта. Однако, если position: fixed, то 1% - это одна сотая от ширины/высоты окна браузера
Другая величина rem (от root-em) задается как размер шрифта элемента html и не определяется другими родительскими тегами, поэтому ее пользоваться удобнее, чем другими
Наконец, есть величины vw и vh, которые зависят от ширины и высоты окна браузера соответственно. 1vw - это одна сотая от ширина окна и аналогично для vh. Таким образом, стало проще создавать верстку для мобильных устройств
В CSS свойства для одного элемента могут накладываться друг на друга. Допустим, что есть такой элемент:
<p class="bold dark">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
</p>
с такими стилями:
p {
font-size: 20px;
}
.bold {
font-weight: bold;
}
.dark {
color: #666666;
}
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Элемент имеет два класса, CSS-правила к нему применяются, поэтому в итоге свойства для элемента будут такими:
{
color: #666666;
font-weight: bold;
font-size: 20px;
}
Это и называется каскадностью правил. При этом у правил действует приоритет селекторов. Рассмотрим такой пример:
p {
color: green;
}
#blue {
color: blue;
}
.red {
color: red;
}
Цвет текста такого элемента:
<p class="red" id="blue">Бургер</p>
будет синим:
Бургер
Связано это с тем, что правила имеют следующий приоритет:
<p style="color: red;">*Если элемент имеет несколько классов, свойства для которых противоречат друг другу, то применяться будут те свойства, что стоят последними - браузер просто перезапишет правила, которые стояли перед ним:
.my-text {
color: blue;
}
.your-text {
color: red;
}
<p class="your-text my-text">Яблоко лежало на поляне</p>
Яблоко лежало на поляне
Если два правила, то приоритет вычисляет немного сложнее. Для этого составляется кортежи из 4 чисел:
Затем эти кортежи сравниваются. Так селектор p.my-text.bold-text#this-id (0, 1, 2, 1) имеет больший приоритет, чем p.your-text#that-id (0, 1, 1, 1)
Помимо этого существует еще один список приоритетов:
!important)!important)Важные правила в коде обозначаются с помощью !important:
p {
color: red !important;
}
Такие правила имеют больший приоритет, а наибольший имеют пользовательские важные правила, чтобы обеспечивать доступность для пользователей с ограничениями
Часто CSS-код разрастается до таких масштабов, что его становится трудно поддерживать. По этой причине начали создаваться методологии - практики организации кода для его лучшей читаемости и изменения
Методология БЭМ (Блок-Элемент-Модификатор) была создана в Яндексе. Основная ее концепция заключается в том, чтобы легко поддерживать проекты и повторно использовать компоненты
Для этого в БЭМ есть три понятия:
Блок - функционально независимый компонент страницы, который может быть повторно использован
Блоки можно использовать в любом месте на странице, поэтому в CSS-коде для блоков не должно быть никаких внешних отступов и правил позиционирования. При выборе имени нужно убедиться, что оно описывает назначение блока, а не его внешний вид или состояние
Элемент - составная часть блока, которая не может использоваться в отрыве от него
То есть элементы живут только в блоках, не могут принадлежать другим элементам. Также имена элементов описывают их назначение, а не внешний вид
Модификатор - сущность, определяющая внешний вид, состояние или поведение блока либо элемента
Примером модификатора может быть фиксация в верхней части страницы для шапки
Плюсы БЭМ:
Подробнее ознакомиться можно на сайте https://bem.info/
Методология SMACSS (Scalable and Modular Architecture for CSS) создана Джонатаном Снуком и основана на разделении правил на категории:
Для классов правил Layout добавляется префикс l-, grid- или layout-, а для правил State префиксы состояния is- или has-
Для модулей используются имена компонентов. Связанные элементы внутри модуля и вариации модуля должны использовать базовое имя модуля в качестве префикса
В отличие от БЭМ, SMACSS не предписывает слишком строгое соглашение об именовании
Преимущества SMACSS:
Подробнее о SMACSS: https://smacss.com/
Методология eCSS (Enduring CSS) была создана Беном Фрейном и состоит в изоляции компонентов, чтобы повторно их использовать
Это достигается за счет инкапсуляции всего кода (не только CSS), необходимых для постройки всех компонентов, в своих общих папках и создания совершенно нового компонента каждый раз, когда нужен компонент, похожий на уже существующий, но в котором будут некоторые незначительные изменения
eCSS отходит от таких методологий, как БЭМ и SMACSS, которые расширяют и абстрагируются от существующих компонентов, тем самым избегая или пытаясь как можно сильнее избежать повторения кода
Бен Фрейн пришел к выводу, что современные алгоритмы сжатия хорошо сжимают повторяющийся CSS-код, поэтому разница в весе файлов незначительна
Преимущества eCSS:
JavaScript - это
язык программирования, который реализует стандарт ECMAScript
Когда язык создавался в 1995 году для добавления интерактивности веб-страницам, у него было множество других названий. Остановились на JavaScript, так как в 90-ых был очень популярен язык Java
JavaScript нужен для добавления интерактивности веб-страницам, проверки ввода пользователя и отображения диалоговых окон, сообщений
JavaScript - язык интерпретируемый, поэтому для его исполнения нужен интерпретатор. В браузерах на движке Chromium используется интерпретатор V8, в Mozilla Firefox - интерпретатор SpiderMonkey, в Safari - JavaScriptCore
Интерпретаторы в большинстве современных браузеров поддерживают JIT-компиляцию (преобразования языка в машинные инструкции) и язык WebAssembly, который представляет низкоуровневый бинарный формат байткода, предназначенный для быстрого исполнения в браузере
Для добавления скрипта в HTML-страницу используется тег <script>:
<script>
window.alert("Boo");
</script>
<!-- или -->
<script src="./script.js"></script>
JavaScript имеет C-подобный синтаксис, является регистрозависимым
Переменные в JavaScript объявляются с помощью ключевых слов:
var, тогда имеет функциональную область видимостиlet, тогда переменная доступна только внутри блокаconst, тогда запрещается переназначение переменнойВ JavaScript всего существует 8 типов:
Число number. В JavaScript все числа - 64-битные числа с плавающей точкой по стандарту IEEE 754, поэтому появляются такие приколы:
> console.log(0.1 + 0.2);
0.30000000000000004
Чтобы избежать этого, можно хранить мантиссу и при чтении умножать на экспоненту, либо округлять
Так как number - число с плавающей точкой, то существуют NaN, Infinity и -Infinity
Строки string представляют собой последовательность 16-битных значений в Unicode. Строки являются иммутабельными, поэтому при конкатенации строк a + b создается новая. Строки объявляются в одинарных или двойных кавычках
Логический тип boolean, принимающий два значения true и false
Пустое значение null (при этом typeof null выдает "object")
Неопределенное значение undefined
Отличие undefined от null в том, что undefined предполагает, что значение не существует вообще, а null указывает на явное намеренное отсутствие значения
Символьное значение symbol
Символьное значение Symbol() используется как примитивный тип для создания уникальных идентификаторов, в том числе для создания скрытых полей
Целые числа произвольной длины bigint
Целые числа указываются с постфиксом n, например, 123456789n
Объект object
Объекты представляют из себя неупорядоченные коллекции типа “ключ-значение”:
var user = {
name: "Jeffrey",
age: 67,
account_ids: {
bank: 137921,
facebook: 2472374
}
}
Доступ к значениям осуществляется через квадратные скобки user['name'] или через точку user.name
Все сложные структуры (массивы, функции и так далее) реализованы с помощью объектов
Массивы же объявляются с помощью [] и имеют индексацию с 0:
var arr = [];
arr = ["Apple", "Orange", "Pear"];
console.log(arr[1]); // "Orange"
Для управления потоком исполнения используют следующие структуры:
Оператор if:
if (условие1) {
// код выполняется, если условие1 истинно
} else if (условие2) {
// код выполняется, если условие2 истинно
} else {
// код выполняется, если условия выше ложны
}
Для создания условий JavaScript имеет привычные операторы <, ==, !=, >, >=, <=, !, && и ||, но также и ===, !==, которые сравнивают и тип, и значение. Таким образом, "1" == 1 истинно, но "1" === 1 ложно
Оператор switch:
switch (переменная) {
case значение1:
// код выполняется, если переменная == значение1
break;
case значение2:
case значение3:
// код выполняется, если переменная == значение2
// или переменная == значение3
break;
default:
// код выполняется, если условия выше не подошли
}
Оператор for:
for (начало; условие; шаг) {
// код выполняется, пока условие истинно
}
Прерывание цикла осуществляется с помощью break, а переход на следующую итерацию с помощью continue
Для прохода по объекту существует другой синтаксис:
for (const key in person) {
console.log(`${key}: ${person[key]}`);
}
А для прохода по массиву такой:
for (const number of numbers) {
console.log(number);
}
Оператор while:
while (условие) {
// код выполняется, пока условие истинно
}
Оператор try:
try {
// код с потенциальными ошибками
}
catch (err) {
// обработка ошибки
// выполняется в случае ошибке в try
}
finally {
// выполняется в любом случае
}
Взаимодействие с пользователем осуществляется с помощью API:
console.log(сообщение) выводит сообщение в консоль отладкиwindow.alert(сообщение) создает диалоговое окно с сообщение с кнопкой “Ок”window.confirm(сообщение) создает диалоговое окно с сообщение с кнопками “Ок” и “Отмена”, возвращает булевое значение в зависимости от того, какая кнопка нажатаwindow.prompt(сообщение, пример_ввода) создает диалоговое окно с сообщение с кнопками “Ок” и “Отмена” и полем ввода текста, возвращает содержимое поле или nullФункцию в JavaScript можно объявить несколькими способами:
Объявление функции:
function sum(a, b) {
return a + b;
}
Так предпочтительнее всего объявлять функцию, так как она становится доступной для вызова в коде перед ней
Функциональное выражение:
const sum = function(a, b) {
return a + b;
};
Именованное выражение:
const factorial = function fact(n) {
if (n <= 1) return 1;
return n * fact(n - 1);
};
Стрелочная функция:
const sum = (a, b) => a + b;
Полезно для анонимных функций
JavaScript предлагает набор встроенных объектов:
Array предназначен для работы с массивамиDate предназначен для работы с датой и временемString предназначен для работы со строкамиMath предназначен для выполнения математических функций, например: Math.pow(x, y) возводит x в степень y, Math.sqrt(x) находит квадратный корень, а Math.sin(x) находит синусRegExp предназначен для работы с регулярными выражениямиПомимо этого браузер предлагает свои объекты:
window предназначен для работы с окном браузераdocument предназначен для работы с html-документамиhistory предназначен для работы с историейconsole предназначен для отладки программного кода
С помощью JavaScript можно изменять структуру HTML-документа, напрямую модифицируя объектную модель документа (DOM, Document Object Model). Объектная модель обеспечивает доступ ко всем тегам и их атрибутам, позволяет создавать, удалять и изменять элементы
HTML-элементы представляют из себя дерево, в котором узлы - это элементы. Вложенность HTML-элементов обозначается дочерними связями в дереве
Всего существует 12 типов узлов в объектной модели:
ELEMENT_NODE = 1ATTRIBUTE_NODE = 2TEXT_NODE = 3ELEMENT_NODE представляет элемент, который задается HTML-тегом, а TEXT_NODE - это узел с сырым текстом
Так <p>Это параграф</p> преобразуется в дерево с узлом p типа ELEMENT_NODE и дочерний текстовый узел Это параграф типа TEXT_NODE
Корень дерева в JavaScript представлен объектом document. От него можно получить непосредственно элемент html с помощью document.documentElement и элемент body с помощью document.body
В JavaScript есть 6 основных методов получения элементов из дерева объектной модели:
document.getElementById(id) возвращает элемент с идентификатором id (наиболее рекомендованный метод для работы с элементами)document.getElementsByTagName(tag) возвращает массив элементов с тегом tagdocument.getElementsByName(elementName) возвращает массив элементов, у которых атрибут name равен elementNamedocument.getElementsByClassName(elementClass) возвращает массив элементов с классом elementClassdocument.querySelector(selector) возвращает первый элемент, который соответствует селектору selectordocument.querySelector(selector) возвращает массив элементов, которые соответствуют селектору selectorДалее от них можно идти по дереву с помощью свойств:
node.parentNode - родительский узелnode.childNodes[i] - дочерние узлыnode.firstChild - первый дочерний узелnode.lastChild - последний дочерний узелnode.previousSibling - предыдущий соседний узел у того же родителяnode.nextSibling - следующий соседний узелС ними нужно быть осторожными, так как некоторые браузеры обрабатывают новую строку в HTML-документе как текстовый узел
Еще узлы имеют такие свойства:
node.nodeType - тип узла (число из перечислений, 1 для обычного узла, 3 для текстового)node.tagName - тег узлаnode.nodeName - имя узла. Если узел - это тег, то это имя тега, если текст, то #text, если комментарий, то #comment и так далееСодержимое элемента можно узнать или заменить с помощью:
node.innerText - текст, отображаемый в окне браузера, внутри тегаnode.outerText - текст, отображаемый в окне браузера, включая исходный тегnode.innerHTML - HTML-код внутри тегаnode.outerHTML - HTML-код, включая тегЗначения innerText и outerText равны, однако при записи строка принимается как экранированный текст, а не HTML-код, поэтому запись в outerText ведет к удалению исходного элемента
Запись свойств innerHTML и outerHTML ведет к парсингу нового значения и изменения объектной модели. Для изменения HTML-страницы предпочтительнее использовать не эти свойства, принимающие строки, а методы node.appendChild, node.insertBefore и другие
Также есть свойство node.textContent, которое показывает весь текст, включая скрытый с помощью display: none
Для редактирования дерева есть методы:
const elem = document.createElement(tagName) создает элемент с тегом tagName, но только на уровне интерпретатора JavaScriptconst elem = document.createTextNode(text) аналогично создает текстовый узелnode.appendChild(elem) добавляет этот элемент в дерево в качестве дочернего узлаnode.insertBefore(elem) добавляет этот элемент в дерево перед узлом nodenode.removeChild(childNode) удаляет дочерний элемент childNode из дереваnode.remove() удаляет этот элемент из дереваПри вызове таких методов происходит событие пересчета дерева (reflow event), который можно поймать в своем скрипте (об этом позже)
Для редактирования узлов есть такие методы:
node.createAttribute(name) создает атрибут namenode.createAttribute(name, value) создает атрибут name со значением valuenode.removeAttribute(name) удаляет атрибут namenode.getAttribute(name) возвращает значение атрибута nameНа жизненном цикле веб-страницы может происходить множество событий, например, нажатие кнопки или прокрутка колеса мыши
Событие - это сигнал браузера о том, что что-то произошло. Для них браузер предоставляет API
Так событие возникает для какого-то элемента, и браузер вызывает для этого события привязанные к элементу функции JavaScript
События можно добавлять:
onXYZ="myFunction()", где XYZ - тип события, например: <button onclick="alert('Клик!')">Клик!</buttonс помощью свойства:
elem.onclick = function() {
alert('Клик');
};
с помощью метода elem.addEventListener(eventType, callback):
elem.addEventListener("click", function() {
alert('Клик');
});
Такой метод является наиболее предпочтительным, потому что предыдущие ограничены только одной функцией, которые отвечают на событие. addEventListener позволяет добавить множество обработчиков
С помощью событий можно обрабатывать нажатия на элемент (click), наведение на элемент (mouseover), на правый клик мыши (contextmenu), на нажатие клавиатуры (keydown), на отправку формы (submit) и многое другое
Самое важное событие - DOMContentLoaded у document. Оно вызывается тогда, когда дерево объектной модели документа полностью загружено браузером, и над ним безопасно работать внутри JavaScript-кода. Поэтому весь код, требующий работы с элементами ограничивают в обрабатывающей функции:
document.addEventListener("DOMContentLoaded", function () {
const elem = document.getElementById("myid");
// ...
});
Классы в JavaScript появились в стандарте ECMAScript в 2015 году
В JavaScript наследование реализовано через прототипную модель, а классы являются синтаксическим сахаром над прототипами. Прототипное наследование - это механизм, где объекты могут заимствовать свойства и методы у других объектов через цепочку прототипов
Чтобы создать класс в JavaScript, есть несколько способов:
Выражение класса:
var Rectangle = class {
constructor(height, width) {
this.height = height;
this.width = width;
}
};
// или
class Rectangle2 {
constructor(height, width) {
this.height = height;
this.width = width;
}
};
Как можно заметить, класс может быть анонимным. Обратиться к объекту внутри методов можно с помощью слова this
Объект класса создается с помощью ключевого слова new: const rect = new Rectangle(5, 4)
Функция-конструктор:
function Human(firstName, lastName) {
this.firstName = firstName
this.lastName = lastName
this.getFullName = function () {
return firstName + ' ' + lastName
}
}
const chris = new Human("Terry", "Crews")
Фабричная функция - функция, возвращающая объект:
function Human(firstName, lastName) {
return {
firstName,
lastName
}
}
const chris = Human("Terry", "Crews")
Классы в JavaScript также поддерживают наследование:
class Person {
constructor(firstName, lastName) {
this.firstName = firstName
this.lastName = lastName
}
getFullName() {
return this.firstName + ' ' + this.lastName
}
}
class User extends Person {
constructor(firstName, lastName, email) {
// обязательно нужно вызвать super, конструктор класса-родителя
super(firstName, lastName)
this.email = email
}
getEmail() {
return this.email
}
}
Каждый объект в JavaScript имеет скрытую ссылку на другой объект - прототип. Новый объект создаётся со ссылкой на прототип, а не копирует его свойства
Когда происходит обращение к свойству, интерпретатор ищет его в самом объекте, если не находит - идёт в прототип, если и там его нет, то ищет в прототипе прототипа и так далее
Такая структура образует цепочку прототипов (prototype chain)
Например, массивы Array наследуются от Array.prototype, который включает методы:
акцессоры - они не изменяют исходный массив
Array.prototype.includes(e) - возвращает true, если массив содержит элемент e
Array.prototype.slice(i, j) - возвращает новый массив, который является срезом исходного
мутаторы - изменяют исходный массив
Array.prototype.push(e) - помещает элемент e в конец массива
Array.prototype.pop() - удаляет последний элемент массива
Array.prototype.splice(start, deleteCount) - извлекает срез массива от индекса start длиной deleteCount без сохранения исходного
и итераторы - они применяют функцию, переданную в качестве аргумента, на каждом элементе массива для создания нового
Array.prototype.map(f) - применяет функцию f на каждом элементе массива и создаёт новый массив с результатом вызова указанной функции
Array.prototype.filter(f) - создаёт новый массив со всеми элементами, прошедшими проверку, задаваемую в функции f
Array.prototype.forEach(f) - применяет функцию f на каждом элементе массива
Чтобы расширить такой массив, можно добавить новое поле в его прототип:
Array.prototype.partition = function(pred) {
let passed = []
let failed = []
for (let i = 0; i < this.length; i++) {
if (pred(this[i])) {
passed.push(this[i])
} else {
failed.push(this[i])
}
}
return [passed, failed]
}
И тогда его можно будет вызвать из массива:
[1, 2, 3, 4, 5].partition(a => a < 4) // [[1, 2, 3], [4, 5]]
Расширять встроенные прототипы (например, Array.prototype) в реальных проектах не рекомендуется, так как это может привести к конфликтам
Ранее приватных полей в JavaScript не было. Вместо них, как в Python, можно обозначить нижним подчеркиванием спереди:
class User {
constructor(firstName, lastName, email) {
this._firstName = firstName
this._lastName = lastName
this._email = email
}
}
Однако в новых версиях со стандарта ES2022 появилась возможность определять приватные поля:
class ClassWithPrivate {
#privateField;
#privateFieldWithInitializer = 42;
#privateMethod() {
// …
}
}
Приватные поля должны иметь уникальное имя внутри класса, а также конструктор нельзя сделать приватным
Также в качестве инкапсуляции может выступать замыкание функции:
function outside() {
const food = "Hamburger"
return function inside() {
console.log(food)
}
}
С замыканием нужно быть осторожным, потому что значение this меняется от того, в каком контексте слово было использовано. Значение this определяется:
thisnew создаёт новый контекстcall/apply/bind явно задают thisJavaScript позволяет расширять функциональность методов родителей:
class Developer extends Human {
sayHello() {
// Вызываем родительский метод
super.sayHello()
// Создаем новый метод
console.log(`I'm a developer`)
}
}
const chris = new Developer('Walter', 'White')
chris.sayHello()
Вместо наследования можно применить функциональную композицию:
function Developer (firstName, lastName) {
const human = Human(firstName, lastName)
return Object.assign({}, human, {
sayHello() {
// Вызываем родительский метод
human.sayHello()
// Создаем новый метод
console.log(`I'm a developer`)
}
})
}
const chris = new Developer('Walter', 'White')
chris.sayHello()
Современные веб-браузеры поддерживают несколько способов хранения данных из веб-сайтов на компьютере пользователя, чтобы потом получать их, когда это необходимо. Это позволяет долгосрочно хранить данные, сохранять сайты или документы для использования без подключения к сети, сохранять пользовательские настройки для сайта и многое другое
Сайты можно разделить:
На статические - в них верстка и контент строго зафиксированы
Статические сайты полезны, если количество контента небольшое, и контент одинаков для каждого пользователя, однако плохи тем, что изменения в верстке приводят к изменению каждой страницы
На динамические - в них сервер генерирует свой контент на странице в зависимости от конкретного URL-адреса, пользователя, данных в базе данных и других условий
Серверный код сайта может не только возвращать HTML-фрагменты и файлы в ответе, а динамически создавать и возвращать другие типы файлов (например, текст, PDF, CSV) или даже данные (JSON, XML)
Совсем недавно стал популярен подход “одностраничных приложений”, где весь сайт написан с одним HTML-файлом, который динамически обновляется по мере необходимости
Большинство современных веб-сайтов являются динамическими - они хранят данные на сервере, используя базу данных, а затем запускают код на стороне сервера чтобы извлечь данные, вставить их в HTML-шаблоны и отправить сформированный HTML клиенту для отображения в браузере пользователя
Хранилище на стороне клиента работает по схожим принципам, но используется по-другому. Оно состоит из API, который позволяет хранить данные на клиенте, а затем извлекать их при необходимости, например:
Часто, хранилища на сторонах клиента и сервера используются совместно
С первых дней Интернета, использовали куки (cookies) для хранения информации, чтобы персонализировать пользовательский опыт на веб-сайтах - это самая ранняя форма хранилища на стороне клиента
Куки представляют небольшую порцию текстовых данных, хранящихся в браузере. Всякий раз при открытии страницы браузер соответствующего сайта пересылает сохранённые куки обратно веб-серверу через HTTP-заголовки:
// Создание куки
document.cookie = "username=Chris; max-age=3600; path=/";
// Чтение куки
console.log(document.cookie);
// Удаление куки
document.cookie = "username=Chris; max-age=0";
Как можно заметить, у куки есть время жизни. Также куки могут хранить 4 Кб данных
Современные браузеры имеют гораздо более простые и эффективные API для хранения данных на стороне клиента, чем при использовании куки:
Web Storage API обеспечивает очень простой синтаксис для хранения и извлечения данных, состоящих из пар ключ-значение, где значение - это строка
Это полезно, когда просто нужно сохранить некоторые простые данные, такие как имя пользователя, вошёл ли он в систему и тому подобное
IndexedDB API обеспечивает браузер полной базой данных для хранения сложных данных
Это может быть использовано для хранения полных наборов записей клиентов и даже до сложных типов данных, таких как аудио или видео файлы
Веб-хранилища предоставляет механизмы, при помощи которых браузеры могут безопасно хранить пары ключ-значение в более интуитивно понятной манере, чем куки. В качестве веб-хранилища может выступать один из двух вариантов имплементации механизма:
Локальное хранилище (Local Storage) хранит данные для конкретного домена (а точнее тройки - протокол, домен и порт) и даже в случае, если браузер закрыт
Обычно браузеры выделяют около 5–10 МБ для хранилища на один домен страницы, точный объём зависит от браузера
// Сохранить
localStorage.setItem("theme", "dark");
// Получить
const theme = localStorage.getItem("theme");
console.log(theme);
// Удалить ключ
localStorage.removeItem("theme");
// Очистить всё
localStorage.clear();
// Аналогично для сессионного хранилища
sessionStorage.setItem("token", "12345");
console.log(sessionStorage.getItem("token"));
Так же как и куки, объект локального хранилища будет одинаковый для всех открытых вкладок во всех окнах браузера в рамках одного домена, но только в пределах одного браузера. Важно подметить, что пароли и другую чувствительную информацию нельзя хранить в локальном хранилище
Помимо обычного хранения данных хранилища часто используют для того чтобы реализовывать общение между несколькими вкладками веб-приложения с помощью события StorageEvent (например, смена музыки во второй вкладке приводит к ее воспроизведению в первой):
window.addEventListener("storage", (event) => {
console.log("Изменение:", event.key);
console.log("Новое значение:", event.newValue);
});
Важно заметить, что событие не сработает в той вкладке, где было изменено хранилище
IndexedDB API (или IDB) - это полноценная база данных, доступная в браузере, в которой можно хранить сложные связанные данные, типы которых не ограничиваются простыми значениями, такими как строки или числа
Использование IndexedDB является более сложным. Перед тем как мы сможем добавлять или изменять какие-либо данные, нужно сначала открыть базу данных и создать хранилища (аналогичные таблицам в базе данных)
const request = indexedDB.open("MyDatabase", 1);
request.onupgradeneeded = function (event) {
const db = event.target.result;
db.createObjectStore("users", { keyPath: "id" });
};
request.onsuccess = function (event) {
const db = event.target.result;
const transaction = db.transaction("users", "readwrite");
const store = transaction.objectStore("users");
store.add({ id: 1, name: "Chris" });
};
Другие преимущества IndexedDB:
IndexedDB может хранить любые сериализуемые объекты напрямую
В хранилищах же сложные объекты можно хранить в виде JSON-объектов. JSON (JavaScript Object Notation) - формат обмена данными. Его синтаксис и типы схожи на те, что есть в JavaScript
JSON-объект представляет собой множество пар ключ-значение. Ключ в JSON-объекте всегда является строкой. Для работы с JSON внутри JavaScript существуют методы JSON.parse() и JSON.stringify(obj):
const user = {
name: "Chris",
age: 35
};
// Сохраняем
localStorage.setItem("user", JSON.stringify(user));
// Получаем
const savedUser = JSON.parse(localStorage.getItem("user"));
console.log(savedUser.name);
События в JavaScript могут возникать не только по очереди, но и множество одновременно. Возможно и такое, что во время обработки одного события возникают другие
В каждом окне выполняется только один главный поток, который занимается выполнением скриптов JavaScript, отрисовкой и работой с DOM. Этот поток выполняет команды последовательно, может делать только одно дело одновременно и блокируется при выводе модальных окон, с помощью таких методов как window.alert()
Если главный поток прямо сейчас занят, то он не может срочно выйти из середины одной функции и прыгнуть в другую. Отладка при этом могла бы превратиться в кошмар, потому что пришлось бы разбираться с совместным состоянием нескольких функций сразу. Поэтому, когда происходит событие, оно попадает в очередь
Внутри браузера непрерывно работает внутренний событийный цикл (Event Loop), который следит за состоянием очереди и обрабатывает события, запускает соответствующие обработчики. В нем движок JavaScript ожидает задачи, исполняет их и снова ожидает появления новых
Иногда события добавляются в очередь сразу набором, например, при клике на элементе генерируется несколько событий - mousedown при нажатии и mouseup при отпуске кнопки
Но в тех случаях, когда событие инициируется не посетителем, а кодом, то оно, как правило, обрабатывается синхронно, то есть прямо сейчас. Например, когда посетитель фокусируется на элементе, возникает событие onfocus, например, при нажатии на поле ввода. Но ту же фокусировку можно вызвать и явно методом elem.focus()
Движок JavaScript большую часть времени ничего не делает и работает, только если требуется исполнить скрипт или обработать событие. Может так случиться, что задача поступает, когда движок занят чем-то другим, тогда она ставится в очередь. Очередь, которую формируют такие задачи, называют очередью макрозадач (Macrotask Queue)
При этом:
Допустим, у нас есть задача, требующая значительных ресурсов процессора. Пока движок JavaScript занят этой задачей, он не может делать ничего, связанного с DOM, не может обрабатывать пользовательские события и так далее. Можно избежать этого, разбив задачу на части
Помимо макрозадач существуют микрозадачи. Микрозадачи приходят только из кода и обычно они создаются объектами обещаний (или промисами, от promise). Сразу после каждой макрозадачи движок исполняет все задачи из очереди микрозадач перед тем, как выполнить следующую макрозадачу или отобразить изменения на странице
Объект обещания Promise в JavaScript - это объект, представляющий результат асинхронной операции, которая еще не завершилась. Они нужны для управления асинхронным кодом - вместо того чтобы ждать ответа (например, от сервера), программа продолжает работу, а объект Promise “обещает” вернуть результат позже:
const fetchData = new Promise((resolve, reject) => {
setTimeout(() => {
const success = true;
if (success) resolve("Данные получены ✅");
else reject("Ошибка ❌");
}, 1000);
});
fetchData
.then(result => console.log(result))
.catch(error => console.error(error))
.finally(() => console.log('Конец'))
Promise принимает функцию от двух аргументов - других функций, которые вызываются в случае успеха или неудачи выполнения
Ключевые слова async и await являются синтаксическим сахаром над Promise и делают код более читаемым:
async function loadData() {
try {
const result = await fetchData;
console.log(result);
} catch (error) {
console.error(error);
}
}
Слово await приостанавливает выполнение текущей функции до завершения выполнения Promise
У объекта Promise есть внутренние свойства:
state - состояние. Состояние может быть pending (ожидание), fulfilled (удовлетворено) и rejected (отклонено)result - результат. Вначале result имеет значение undefined, далее он изменяется на value при вызове resolve(value) или на error при вызове reject(error)Также у Promise есть продвинутые статические методы:
// Ждёт завершения всех функций
Promise.all([fetch('/a'), fetch('/b')]).then(([a, b]) => ...)
// Возвращает первый завершившийся
Promise.race([slowRequest, timeout]).then(...)
Все микрозадачи завершаются до обработки каких-либо событий или рендеринга, или перехода к другой макрозадаче. Это важно, так как гарантирует, что общее окружение остаётся одним и тем же между микрозадачами - то есть, например, не изменены координаты мыши, не получены новые данные по сети и так далее
Если мы хотим запустить функцию асинхронно (после текущего кода), но до отображения изменений и до новых событий, то можно запланировать это через queueMicrotask и через MutationObserver

Fetch API — это современный браузерный интерфейс для выполнения HTTP-запросов к серверу. Он пришёл на смену устаревшему объекту XMLHttpRequest и предоставляет более удобный синтаксис, основанный на Promise
fetch() возвращает Promise, который разрешается в объект Response:
fetch(url, options)
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Ошибка:', error));
fetch() принимает адрес запроса url и опции options (метод, заголовки, тело и так далее)
GET используется по умолчанию, если не указать метод явно, например:
fetch('https://api.example.com/users')
.then(response => {
if (!response.ok) {
throw new Error('Ошибка: ' + response.status);
}
return response.json();
})
.then(users => console.log(users))
.catch(err => console.error(err));
POST-запрос:
fetch('https://api.example.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'Иван',
age: 25
})
})
.then(response => response.json())
.then(data => console.log('Создан:', data));
После получения ответа нужно его распарсить с помощью нужного метода:
| Метод | Описание |
|---|---|
response.json() |
Парсит тело как JSON → возвращает объект |
response.text() |
Возвращает тело как строку |
response.blob() |
Возвращает бинарные данные (файлы, изображения) |
response.formData() |
Парсит тело как FormData |
response.arrayBuffer() |
Возвращает сырые бинарные данные |
Иногда нужно отменить запрос, например при смене страницы. Тогда можно воспользоваться AbortController:
const controller = new AbortController();
fetch('/api/data', { signal: controller.signal })
.then(res => res.json())
.then(data => console.log(data))
.catch(err => {
if (err.name === 'AbortError') {
console.log('Запрос отменён');
}
});
// Отмена запроса через 3 секунды
setTimeout(() => controller.abort(), 3000);