Это вторая из двух историй, в которой вы узнаете все, что вам нужно знать, чтобы начать программировать с использованием Javascript.

Как и в Части 1, я рекомендую следить за этой историей, используя что-то вроде PlayCode.io, чтобы вы могли действительно понять все, через что мы проходим.

В этой истории мы рассмотрим работу с DOM и использование AJAX для асинхронного выполнения сетевых запросов. Мы начнем с того, что посмотрим, что такое DOM на самом деле.

ДОМ

DOM или объектная модель документа - это в значительной степени то, что написано на жестяной банке: она берет ваш XML или HTML-документ и моделирует его. Что я имею в виду, говоря о моделировании? DOM берет все ваши элементы и превращает их в узлы на дереве. Каждый узел - это объект, которым можно управлять. Например, простой HTML-файл может выглядеть примерно так:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title>Hello World</title>
    </head>
    <body>
        <h1>Hello World</h1>
    </body>
</html>

А с DOM это выглядит примерно так:

Document
Root Element: <html> --- Attribute: lang
Element: <head>
            Element: <meta> --- Attribute: charset
            Element: <title> --- Text: "Hello World"
Element: <body>
            Element: <h1> --- Text: "Hello World"

Здесь вы можете видеть, что у нас есть несколько ветвей, каждая из которых заканчивается узлом. Вы также можете посмотреть на дерево точно так же, как при доступе к стандартным объектам в Javascript. Например, давайте посмотрим, как получить текст заголовка с помощью псевдокода.

document.html.head.title.text

Здесь мы начинаем с ствола, или, другими словами, с документа. Затем мы переходим в ветвь <html>, затем в ветвь <head>, затем в ветвь <title> и, наконец, попадаем в узел text, который мы ищем.

Модель DOM хороша тем, что позволяет нам динамически изменять структуру, стиль и содержимое нашей веб-страницы. Итак, как мы манипулируем DOM? Как и в псевдокоде, объект document - это ответ на наши нужды. Однако, прежде чем мы чем-либо манипулируем, нам нужно сначала узнать, как получить доступ к нашим HTML-элементам.

Доступ к DOM

У нас есть три способа получить доступ к элементам в DOM:

document.getElementById()
document.getElementByTagName()
document.getElementByClassName()

Начнем с getElementById(), поскольку это самый простой метод. Используя этот метод, мы можем получить доступ к любому элементу HTML, имеющему атрибут id. Например, если у нас есть следующий HTML-код в нашем теле:

<div id="container"></div>

Чтобы получить доступ к этому div, мы можем сделать следующее:

let container = document.getElementById('container');

Здесь наша контейнерная переменная теперь содержит объект DOM для нашего HTML-блока. С помощью метода getElementById() вы можете получить доступ только к одному элементу, потому что идентификатор элемента должен быть уникальным. Однако нам может не понадобиться только один элемент. Мы можем захотеть получить доступ к нескольким, здесь в игру вступают два других метода. Как и следовало ожидать, getElementByTagName() получает элементы путем поиска тега, а getElementByClassName() получает элементы путем поиска класса. Давайте обновим наш HTML, чтобы он выглядел так:

<ul id="vehiclesList">
    <li class="land-vehicle">Car</li>
    <li class="land-vehicle">Truck</li>
    <li class="sky-vehicle">Plane</li>
    <li class="sky-vehicle">Helicopter</li>
</ul>

Если мы хотим получить все элементы в нашем списке, мы можем использовать getElementByTagName():

let listItems = document.getElementsByTagName('li');

Если мы зарегистрируем listItems, то увидим, что это объект HTMLCollection, это в основном массив найденных HTML-элементов. Если мы зарегистрируем listItems.length, то увидим, что в массиве четыре элемента, это наши четыре <li> элемента.

Теперь предположим, что нам нужны только наземные транспортные средства, для этого мы будем использовать getElementByClassName():

let landVehicles = document.getElementsByClassName('land-vehicle');

Если мы сейчас зарегистрируем landVehicles.length, мы увидим, что были найдены два элемента, это наши <li> элементы, которые имеют класс land-vehicle. Теперь, если мы изменим один из элементов списка на div, тогда в нашем массиве все равно будет два элемента, потому что getElementsByClassName() не заботится о том, какой элемент он найдет, пока в нем есть класс.

Отлично, теперь мы знаем, как получить доступ к элементам в DOM, это означает, что мы можем начать манипулировать им.

Манипуляции с DOM

Прежде чем мы начнем, давайте быстро напишем код для <ul>, а чтобы получить все элементы <li>, мы сохраним HTML прежним.

<ul id="vehiclesList">
    <li class="land-vehicle">Car</li>
    <li class="land-vehicle">Truck</li>
    <li class="sky-vehicle">Plane</li>
    <li class="sky-vehicle">Helicopter</li>
</ul>
let vehiclesList = document.getElementById('vehiclesList');
let vehicles = document.getElementsByTagName('li');

Начнем с того, что мы хотим сделать все транспортные средства прописными. Для этого нам нужно изменить содержимое элементов <li>. Чтобы изменить содержимое, мы можем использовать свойство innerHTML.

let vehiclesList = document.getElementById('vehiclesList');
let vehicles = document.getElementsByTagName('li');
for (vehicle of vehicles) {
  vehicle.innerHTML = vehicle.innerHTML.toUpperCase();
}

Здесь вы можете видеть, что свойство innerHTML может использоваться как для установки, так и для получения содержимого элемента. Если вы запустите код, все содержимое внутри <li> элементов теперь будет в верхнем регистре. Теперь мы знаем, как манипулировать содержанием, поэтому давайте посмотрим, как манипулировать стилем, давайте сделаем весь текст красным.

let vehiclesList = document.getElementById('vehiclesList');
let vehicles = document.getElementsByTagName('li');
for (vehicle of vehicles) {
  vehicle.innerHTML = vehicle.innerHTML.toUpperCase();
  vehicle.style.color = "#FF0000";
}

Если вы запустите код, весь текст будет красным и прописным. Мы можем использовать свойство style для изменения любого значения CSS, которое захотим.

Наконец, мы также можем установить атрибуты для наших элементов. Например, предположим, что мы хотим изменить имена классов, чтобы все они были одинаковыми.

let vehiclesList = document.getElementById('vehiclesList');
let vehicles = document.getElementsByTagName('li');
for (vehicle of vehicles) {
  vehicle.innerHTML = vehicle.innerHTML.toUpperCase();
  vehicle.style.color = "#FF0000";
  vehicle.setAttribute('class', 'vehicle');
}

Чтобы проверить, работает ли это, давайте добавим следующий код, чтобы все элементы с классом vehicle стали синими.

let vehiclesList = document.getElementById('vehiclesList');
let vehicles = document.getElementsByTagName('li');
for (vehicle of vehicles) {
  vehicle.innerHTML = vehicle.innerHTML.toUpperCase();
  vehicle.style.color = "#FF0000";
  vehicle.setAttribute('class', 'vehicle');
}
let updatedVehicles = document.getElementsByClassName('vehicle');
for (vehicle of updatedVehicles) {
  vehicle.style.color = "#0000FF";
}

Отлично, весь наш контент теперь синий. Теперь у нас есть базовое понимание того, как манипулировать нашими HTML-элементами. Давайте сделаем еще один шаг и создадим несколько элементов.

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

Давайте создадим еще один список, но на этот раз мы будем отображать цвета вместо транспортных средств. Для создания элементов мы используем метод createElement().

let vehiclesList = document.getElementById('vehiclesList');
let vehicles = document.getElementsByTagName('li');
for (vehicle of vehicles) {
  vehicle.innerHTML = vehicle.innerHTML.toUpperCase();
  vehicle.style.color = "#FF0000";
  vehicle.setAttribute('class', 'vehicle');
}
let coloursList = document.createElement('ul');

Здесь мы создали наш неупорядоченный список, но сейчас он бесполезен для нас, так как у него нет содержимого, поэтому давайте создадим несколько элементов списка.

let vehiclesList = document.getElementById('vehiclesList');
let vehicles = document.getElementsByTagName('li');
for (vehicle of vehicles) {
  vehicle.innerHTML = vehicle.innerHTML.toUpperCase();
  vehicle.style.color = "#FF0000";
  vehicle.setAttribute('class', 'vehicle');
}
let coloursList = document.createElement('ul');
let redItem = document.createElement('li');
let greenItem = document.createElement('li');
let blueItem = document.createElement('li');

Отлично, теперь у нас есть несколько пунктов списка. Тем не менее, они бесполезны для нас, потому что в них нет контента и их нет в нашем неупорядоченном списке. Давайте добавим контент в элементы списка.

let vehiclesList = document.getElementById('vehiclesList');
let vehicles = document.getElementsByTagName('li');
for (vehicle of vehicles) {
  vehicle.innerHTML = vehicle.innerHTML.toUpperCase();
  vehicle.style.color = "#FF0000";
  vehicle.setAttribute('class', 'vehicle');
}
let coloursList = document.createElement('ul');
let redItem = document.createElement('li');
let greenItem = document.createElement('li');
let blueItem = document.createElement('li');
redItem.innerHTML = "Red";
greenItem.innerHTML = "Green";
blueItem.innerHTML = "Blue";

Теперь, когда мы добавили контент, нам нужно добавить элементы списка в наш неупорядоченный список. Чтобы добавить элемент к другому элементу, мы используем метод appendChild().

let vehiclesList = document.getElementById('vehiclesList');
let vehicles = document.getElementsByTagName('li');
for (vehicle of vehicles) {
  vehicle.innerHTML = vehicle.innerHTML.toUpperCase();
  vehicle.style.color = "#FF0000";
  vehicle.setAttribute('class', 'vehicle');
}
let coloursList = document.createElement('ul');
let redItem = document.createElement('li');
let greenItem = document.createElement('li');
let blueItem = document.createElement('li');
redItem.innerHTML = "Red";
greenItem.innerHTML = "Green";
blueItem.innerHTML = "Blue";
coloursList.appendChild(redItem);
coloursList.appendChild(greenItem);
coloursList.appendChild(blueItem);

Отлично, теперь у нас есть полный список, но почему мы его не видим? Наш список также нужно добавить к элементу, в нашем случае к элементу <body>.

let vehiclesList = document.getElementById('vehiclesList');
let vehicles = document.getElementsByTagName('li');
for (vehicle of vehicles) {
  vehicle.innerHTML = vehicle.innerHTML.toUpperCase();
  vehicle.style.color = "#FF0000";
  vehicle.setAttribute('class', 'vehicle');
}
let coloursList = document.createElement('ul');
let redItem = document.createElement('li');
let greenItem = document.createElement('li');
let blueItem = document.createElement('li');
redItem.innerHTML = "Red";
greenItem.innerHTML = "Green";
blueItem.innerHTML = "Blue";
coloursList.appendChild(redItem);
coloursList.appendChild(greenItem);
coloursList.appendChild(blueItem);
document.getElementsByTagName('body')[0].appendChild(coloursList);

Здесь мы добавили наш coloursList к первому экземпляру тега <body>. Если мы запустим код, мы должны увидеть и наш список транспортных средств, и наш список цветов.

Возможно, вместо того, чтобы иметь оба списка, мы хотим заменить наш список транспортных средств списком цветов. Для замены элементов мы можем использовать метод replaceChild(new, old).

let vehiclesList = document.getElementById('vehiclesList');
let vehicles = document.getElementsByTagName('li');
for (vehicle of vehicles) {
  vehicle.innerHTML = vehicle.innerHTML.toUpperCase();
  vehicle.style.color = "#FF0000";
  vehicle.setAttribute('class', 'vehicle');
}
let coloursList = document.createElement('ul');
let redItem = document.createElement('li');
let greenItem = document.createElement('li');
let blueItem = document.createElement('li');
redItem.innerHTML = "Red";
greenItem.innerHTML = "Green";
blueItem.innerHTML = "Blue";
coloursList.appendChild(redItem);
coloursList.appendChild(greenItem);
coloursList.appendChild(blueItem);
//document.getElementsByTagName('body')[0].appendChild(coloursList);
document.getElementsByTagName('body')[0].replaceChild(coloursList, vehiclesList);

Здесь мы заменили метод appendChild() на метод replaceChild(), если мы запустим этот код, вы увидите только наш список цветов.

Отлично, теперь мы можем создавать, добавлять и заменять элементы в DOM. Давайте теперь посмотрим, как мы удаляем элементы, для этого мы используем метод removeChild(). Удалим blueItem из нашего списка цветов.

let vehiclesList = document.getElementById('vehiclesList');
let vehicles = document.getElementsByTagName('li');
for (vehicle of vehicles) {
  vehicle.innerHTML = vehicle.innerHTML.toUpperCase();
  vehicle.style.color = "#FF0000";
  vehicle.setAttribute('class', 'vehicle');
}
let coloursList = document.createElement('ul');
let redItem = document.createElement('li');
let greenItem = document.createElement('li');
let blueItem = document.createElement('li');
redItem.innerHTML = "Red";
greenItem.innerHTML = "Green";
blueItem.innerHTML = "Blue";
coloursList.appendChild(redItem);
coloursList.appendChild(greenItem);
coloursList.appendChild(blueItem);
document.getElementsByTagName('body')[0].replaceChild(coloursList, vehiclesList);
coloursList.removeChild(blueItem);

Теперь, если мы запустим этот код, наш список цветов будет содержать только красный и зеленый элементы.

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

Хорошо, перейдем к Ajax.

AJAX

Во-первых, что такое AJAX? AJAX - одно из величайших дополнений к Javascript, созданное в 1999 году (через 4 года после создания Javascript). AJAX позволяет нам делать асинхронные сетевые запросы. Другими словами, мы можем запрашивать данные с серверов после загрузки страницы и обновлять нашу веб-страницу без необходимости перезагружать страницу. Мы также можем отправлять данные на серверы в фоновом режиме без перезагрузки страницы.

В AJAX мы используем объект XMLHttpRequest, давайте посмотрим, как это работает. Первое, что нам нужно сделать, это создать новый экземпляр объекта.

let xhr = new XMLHttpRequest();

После того, как мы создали наш экземпляр объекта, мы можем открыть соединение, для этого мы используем метод open. Метод open принимает пять параметров. Это метод, URL, асинхронный режим, пользователь и пароль. В этой истории мы будем использовать только первые три параметра.

let xhr = new XMLHttpRequest();
xhr.open("GET", "https://5cd182d9d4a78300147bec19.mockapi.io/api/users", true);

Здесь мы используем метод GET, он используется для простого получения данных с сервера. URL-адрес предназначен для фиктивного API, который я создал для этой истории, но это может быть любой URL-адрес API, который вы хотите. Наконец, мы устанавливаем параметр async как true, чтобы запрос выполнялся асинхронно.

Далее мы собираемся использовать параметр onreadystatechange, это позволяет нам проверить состояние готовности запроса, то есть, другими словами, текущий этап запроса. Есть пять состояний готовности:

// This is from the official documentation
0 - UNSENT - Client has been created. open() not called yet.
1 - OPENED - open() has been called.
2 - HEADERS_RECEIVED - send() has been called, and headers and status are available
3 - LOADING - Downloading; responseText holds partial data.
4 - DONE - The operation is complete.

Мы установим функцию для свойства onreadystatechange, так мы сможем проверить изменение состояния. Мы также проверим, выполнен ли запрос, проверив состояние готовности четыре.

let xhr = new XMLHttpRequest();
xhr.open("GET", "https://5cd182d9d4a78300147bec19.mockapi.io/api/users", true);
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
// Our request is done
}
}

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

let xhr = new XMLHttpRequest();
xhr.open("GET", "https://5cd182d9d4a78300147bec19.mockapi.io/api/users", true);
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
// The request was successful
} else {
// There was an error
}
}
}

Здесь мы проверяем, имеет ли статус 200, это означает, что все прошло нормально. Есть довольно много кодов статуса HTTP, которые вы можете проверить, вот полный список. Итак, теперь мы знаем, когда запрос завершился и был ли он успешным, теперь давайте получим ответ, а также отобразим ошибку, если она была.

let xhr = new XMLHttpRequest();
// Right let's change our endpoint back to users, and have a look at how we access the response.
xhr.open("GET", "https://5cd182d9d4a78300147bec19.mockapi.io/api/users", true);
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
let response = xhr.responseText;
      response = JSON.parse(response);
response.forEach((item) => {
        console.log(item);
      });
} else {
console.log(`Error with request: ${xhr.status}`);
      console.log(xhr.responseText);
}
}
}

Мы используем responseText, чтобы получить значение ответа на запрос, в нашем случае ответ находится в формате JSON, поэтому мы также анализируем эти данные перед их записью. Если есть ошибка, мы регистрируем статус HTTP и responseText.

Осталось сделать только одно, теперь нам нужно отправить запрос. Для отправки запроса используем метод send().

let xhr = new XMLHttpRequest();
// Right let's change our endpoint back to users, and have a look at how we access the response.
xhr.open("GET", "https://5cd182d9d4a78300147bec19.mockapi.io/api/users", true);
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
let response = xhr.responseText;
      response = JSON.parse(response);
response.forEach((item) => {
        console.log(item);
      });
} else {
console.log(`Error with request: ${xhr.status}`);
      console.log(xhr.responseText);
}
}
}
xhr.send();

Если вы запустите этот код, вы должны увидеть несколько журналов, в которых отображаются сведения о фиктивных пользователях. Получить данные с сервера с помощью Ajax так просто, давайте теперь изменим код, чтобы вместо этого отправлять данные на сервер.

let xhr = new XMLHttpRequest();
// Right let's change our endpoint back to users, and have a look at how we access the response.
xhr.open("POST", "https://5cd182d9d4a78300147bec19.mockapi.io/api/users", true);
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
if (xhr.status === 200 || xhr.status === 201) {
let response = xhr.responseText;
      response = JSON.parse(response);
console.log(response);
} else {
console.log(`Error with request: ${xhr.status}`);
      console.log(xhr.responseText);
}
}
}
xhr.setRequestHeader("Content-Type", "application/json");
let data = {
  name: "John Smith",
  email: "[email protected]"
};
xhr.send(JSON.stringify(data));

Сначала мы меняем параметр метода для open() метода на "POST". Затем мы добавляем HTTP-статус 201 в наш оператор if, где мы проверяем, был ли запрос успешным или нет. Код состояния HTTP 201 означает, что ресурс был успешно создан, что в нашем случае означает, что был создан новый пользователь. После обновления проверки статуса мы удаляем цикл forEach и просто регистрируем ответ, потому что я знаю, что ответ будет от одного пользователя, пользователя, которого мы создаем. Затем мы используем метод setRequestHeader(), чтобы сообщить серверу, что мы отправляем JSON. После установки заголовка мы создаем данные для отправки, а затем передаем их в метод send() как JSON.

Теперь, если мы запустим этот код, все должно работать, и мы увидим ответ с новым пользователем, которого мы создали.

Заключение

Управлять DOM и использовать Ajax для сетевых запросов очень просто. Теперь вы знаете все, что вам нужно знать, чтобы начать разработку веб-сайтов и веб-приложений с помощью Javascript. Чем больше вы будете использовать Javascript, тем лучше поймете, как он работает и что с ним можно делать. Как я часто говорю, лучшее, что можно сделать сейчас, - это начать экспериментировать. Может быть, попробуйте составить список дел или приложение погоды, неважно, что вы создаете, если вы что-то создаете и продвигаете свои знания вперед.