Зарегистрируйтесь, чтобы продолжить обучение

AJAX JS: DOM API

Простые приложения в браузере, например калькулятор, могут работать сами по себе без взаимодействия с внешним миром. Все что требуется для их реализации, это написать js-код, который рисует интерфейс и обновляет DOM реагируя на события ввода чисел и клика по кнопкам.

Calculator в браузере

Но такие приложения редко встречаются в реальной жизни, так как с их помощью можно решить ограниченный набор задач. Гораздо чаще, код в браузере взаимодействует с внешним миром за пределами браузера. Такие приложения состоят из двух компонентов: клиентской части (client-side) и серверной (backend), где клиент и сервер общаются друг с другом посредством HTTP-запросов. HTTP-запросы с клиентской стороны делаются с помощью механизма, который долгое время назывался Ajax, сейчас так говорят реже, но мы для простоты оставим это название. Но иногда вы будете видеть названия XHR или Fetch.

Возьмем для примера редактор Code Basics. Когда пользователь нажимает кнопку "проверить" в редакторе, то клиентский код выполняет запрос на серверную часть приложения, которая берет переданный код и запускает для него тесты. Результаты этих тестов возвращаются снова в браузер и Code Basics показывает результат выполнения этих тестов.

Все это происходит не моментально, поэтому во время проверки кнопка "проверить" блокируется и появляется спиннер. Дальше код на клиенте ждет пока придет ответ, причем предсказать время ответа невозможно. HTTP это сеть, а сеть это задержки, разрывы и плавающая скорость. Что-то может пойти не так на сервере, у клиента на машине или по пути между ними. Поэтому работа с сетью требует дополнительных усилий, особенно в области обработки ошибок.

Code Basics Ajax-запрос

Обратите внимание на нижнюю часть скриншота. На нем выбрана вкладка Network в DevTools. На этой вкладке отображаются все запросы к серверу, включая Ajax-запросы. Для удобства их можно отфильтровать, чтобы не мешать с другими запросами выбрав XHR/Fetch. Эта логика работает похожим образом во всех браузерах.

Попробуйте сейчас открыть в отдельной вкладке code-basics.com зайти в любой урок любого курса (регистрация не требуется) и запустить проверку, предварительно открыв вкладку Network. В конце списка вы увидите запрос на сервер. Попробуйте нажать на него и изучить.

На запрос можно нажать, тогда вы сможете увидеть более подробную информацию о нем. Какой был выполнен запрос, куда, с какими заголовками и данными. Там же есть вся информация об ответе. Если запрос вернул ошибку, то браузеры для удобства выделяют их красным цветом.

Пример Ajax-запроса на Code Basics

Технически HTTP-запросы из браузера делаются с помощью Fetch API представленного одной единственной функцией fetch(), которая доступна глобально. Она реализована во всех современных браузерах, поэтому ее использовать безопасно.

Fetch

fetch() асинхронная функция, которая выполняет запрос HTTP-запрос по указанному адресу с нужными заголовками и данными. Посмотрите пример вызова прямо в браузере на сайте code-basics.com:

const response = await fetch("/pages/about");
console.log(response.status); // => 200
console.log(response.headers.get("Content-Type"));
const data = await response.text();

Попробуйте скопировать эти строки и выполнить запрос в консоли браузера.

Вызов fetch в браузере

Несколько наблюдений:

  • По умолчанию используется метод GET.
  • Если домен не указан, то запрос идет на тот же домен, где происходит вызов, в нашем примере это https://code-basics.com.
  • Данные ответа не лежат в готовом виде внутри response, их нужно так же извлекать с помощью асинхронного вызова. Это сделано на случай использования стриминга (streaming), когда данные с сервера отдаются не сразу, а по частям.
const data = await response.text();

Метод text() используется в том случае, если запрос возвращает данные не в структурированном виде, например в виде HTML. Для работы с JSON понадобится другой метод - json(). В таком случае данные сразу вернутся в виде готового объекта.

const response = await fetch("some/api/call");
// Тут будет объект полученный через JSON.parse(body), где body - тело ответа
const data = await response.json();

С помощью fetch() можно не только запрашивать данные, но и отправлять их на сервер. Типичный пример это отправка формы. Для этого вторым параметром в вызов fetch() передается объект, где указывается нужный метод, заголовки и данные. Для отправки данных в формате JSON, нужно добавить правильный заголовок Content-Type и выполнить преобразование данных с помощью JSON.stringify().

const url = /* урл запроса */
const data = /* Данные формы */
fetch(url, {
  method: 'POST', // *GET, POST, PUT, DELETE, etc.
  headers: {
    'Content-Type': 'application/json',
    // 'Content-Type': 'application/x-www-form-urlencoded',
  },
  body: JSON.stringify(data), // тип должен соответствовать заголовку "Content-Type"
});

Полный пример отправки формы

Соберем все вместе и посмотрим на то, как можно реализовать отправку формы используя Ajax. Допустим у нас есть форма регистрации с полями для ввода имени и email:

<form action="/users/new" id="myForm">
  <label for="name">Имя:</label>
  <input type="text" id="name" name="name" required />

  <label for="email">Email:</label>
  <input type="email" id="email" name="email" required />

  <button type="submit">Отправить</button>
</form>

<p id="result"></p>

Тогда для реализации отправки нам понадобится выполнить следующие шаги:

  1. Повесить асинхронный обработчик на событие "отправка формы".
  2. В обработчике извлечь данные формы с помощью formData.
  3. Выполнить отправку данных на /users/new с помощью fetch().
  4. Сообщить пользователю о результатах отправки.
const formElement = document.getElementById("searchForm");
// Обработчик стал асинхронным
formElement.addEventListener("submit", async function (event) {
  event.preventDefault(); // Предотвращаем перезагрузку страницы

  const form = event.target;
  const formData = new FormData(form); // Собираем данные формы
  const jsonData = Object.fromEntries(formData.entries()); // Преобразуем FormData в объект

  const response = await fetch(form.action, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify(jsonData),
  });

  const result = await response.json();

  // В реальном приложении тут может быть много логики в том числе редирект
  document.getElementById("result").textContent = `Результат: ${JSON.stringify(result)}`;
});

fetch() — это довольно низкоуровневый механизм. Например, при работе с JSON нам приходится самостоятельно выставлять заголовки и сериализовывать данные с помощью JSON.stringify(). Из-за этого широкую популярность приобрела библиотека axios, которая работает в базовом варианте практически идентично, но автоматизирует рутину.

Работа с параметрами запроса (query params)

Если ваш запрос на сервер содержит параметры запроса, то их придется добавлять в адрес запроса самостоятельно, fetch() не умеет с ними работать.

const repsonse = await fetch("/blog_posts?page=2");

Но это не ошибка или недоработка дизайна функции. Так и было задумано, чтобы не перегружать fetch(), той функциональностью, которая уже добавлена в браузере: URL и URLSearchParams.

// https://code-basics.com/ru/blog_posts?page=2
const url = new URL(window.location.href);
console.log(url.searchParams.get("page")); // => 2
url.searchParams.set("page", 1);
url.searchParams.set("per", 2);
console.log(url.searchParams.get("page")); // => 1
console.log(url.toString()); // => https://code-basics.com/ru/blog_posts/?page=2&per=2

fetch() умеет работать с объектом URL напрямую, для этого достаточно передать его первым параметром вместо строки:

const response = await fetch(url);

HTTP access control (CORS)

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


Дополнительные материалы

  1. Документация fetch
  2. Документация URL

Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы
профессия
Верстка на HTML5 и CSS3, Программирование на JavaScript в браузере, разработка клиентских приложений используя React
10 месяцев
с нуля
Старт 3 апреля
профессия
Программирование на JavaScript в браузере и на сервере (Node.js), разработка бекендов на Fastify и фронтенда на React
16 месяцев
с нуля
Старт 3 апреля
профессия
новый
Git, JavaScript, Playwright, бэкенд-тесты, юнит-тесты, API-тесты, UI-тесты, Github Actions, HTTP/HTTPS, API, Docker, SQL
8 месяцев
c опытом
Старт 3 апреля
профессия
новый
Разработка фронтенд- и бэкенд-компонентов для веб-приложений на Spring Boot и React
16 месяцев
с нуля
Старт 3 апреля
профессия
новый
16 месяцев
с нуля
Старт 3 апреля

Используйте Хекслет по-максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»