JavaScript: DOM, события и запросы к серверу

Поиск и изменение элементов страницы, обработка событий и делегирование, работа с массивами объектов, асинхронные запросы через fetch, валидация формы.

JavaScript в курсовой почти всегда сводится к трём задачам: найти элементы страницы, отреагировать на действия пользователя и обменяться данными с сервером. Разберём все три.

Поиск и изменение элементов

// Поиск: querySelector принимает любой CSS-селектор
const form  = document.querySelector('#order-form');
const items = document.querySelectorAll('.item');   // NodeList

// Изменение содержимого
const title = document.querySelector('h1');
title.textContent = 'Новый заголовок';      // безопасно: только текст
title.innerHTML   = '<em>с разметкой</em>'; // осторожно: возможен XSS

// Классы и стили
title.classList.add('active');
title.classList.toggle('hidden');
title.classList.contains('active');   // true

// Атрибуты и data-атрибуты
const btn = document.querySelector('button');
btn.disabled = true;
btn.dataset.productId = '42';         // data-product-id="42"

// Создание элемента
const li = document.createElement('li');
li.textContent = 'Новая позиция';
document.querySelector('ul').append(li);
innerHTML с данными от пользователя — прямая дорога к XSS-уязвимости: введённый тег script выполнится. Для текста всегда textContent. В разделе безопасности курсовой это стоит отметить отдельно.

События

const button = document.querySelector('#add');

button.addEventListener('click', (event) => {
  console.log('нажата кнопка', event.target);
});

// Отправка формы: отменяем перезагрузку страницы
form.addEventListener('submit', (event) => {
  event.preventDefault();
  const data = new FormData(form);
  console.log(Object.fromEntries(data));
});

// Делегирование: один обработчик на весь список
document.querySelector('#list').addEventListener('click', (event) => {
  const btn = event.target.closest('.delete');
  if (!btn) return;                       // клик мимо кнопки
  btn.closest('li').remove();
});

// Ввод с задержкой, чтобы не дёргать сервер на каждую букву
let timer;
searchInput.addEventListener('input', (e) => {
  clearTimeout(timer);
  timer = setTimeout(() => search(e.target.value), 300);
});

Делегирование решает главную проблему динамических списков: элементы, добавленные после навешивания обработчиков, всё равно работают, потому что слушатель висит на неизменном родителе.

Массивы объектов

const products = [
  { id: 1, name: 'Резистор',    price: 5.5,  qty: 100 },
  { id: 2, name: 'Конденсатор', price: 12,   qty: 50  },
  { id: 3, name: 'Транзистор',  price: 45,   qty: 20  },
];

const expensive = products.filter(p => p.price > 10);
const names     = products.map(p => p.name);
const total     = products.reduce((sum, p) => sum + p.price * p.qty, 0);
const found     = products.find(p => p.id === 2);
const sorted    = [...products].sort((a, b) => b.price - a.price);

// Рендер таблицы
function render(list) {
  const tbody = document.querySelector('tbody');
  tbody.innerHTML = '';
  for (const p of list) {
    const tr = document.createElement('tr');
    tr.innerHTML = `<td></td><td></td><td></td>`;
    tr.children[0].textContent = p.name;
    tr.children[1].textContent = p.price.toFixed(2);
    tr.children[2].textContent = p.qty;
    tbody.append(tr);
  }
}
sort изменяет исходный массив. Копия через [...products] сохраняет оригинал — без неё порядок данных «поедет» после первой сортировки, и найти причину бывает непросто.

Запросы к серверу

async function loadProducts() {
  try {
    const response = await fetch('/api/products');
    if (!response.ok) throw new Error(`HTTP ${response.status}`);
    const data = await response.json();
    render(data);
  } catch (err) {
    showError('Не удалось загрузить данные: ' + err.message);
  }
}

async function sendOrder(payload) {
  const response = await fetch('/api/orders', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(payload),
  });
  return response.json();
}

Важная деталь: fetch не выбрасывает исключение на код 404 или 500 — он считает ответ полученным. Проверка response.ok обязательна, иначе ошибка сервера пройдёт незамеченной.

Валидация формы

function validate(form) {
  const errors = [];
  const name  = form.name.value.trim();
  const email = form.email.value.trim();
  const qty   = Number(form.qty.value);

  if (name.length < 2) errors.push('Укажите имя');
  if (!/^[^@\s]+@[^@\s]+\.[a-z]{2,}$/i.test(email)) errors.push('Некорректный e-mail');
  if (!Number.isInteger(qty) || qty < 1) errors.push('Количество — целое число от 1');

  return errors;
}

form.addEventListener('submit', (e) => {
  e.preventDefault();
  const errors = validate(form);
  if (errors.length) {
    showError(errors.join('. '));
    return;
  }
  sendOrder(Object.fromEntries(new FormData(form)));
});
Проверка в браузере — удобство для пользователя, а не защита. Данные всё равно проверяют на сервере: запрос легко отправить в обход страницы. В курсовой по веб-разработке за отсутствие серверной проверки снимают баллы.

Хранение данных в браузере

// localStorage хранит только строки
localStorage.setItem('cart', JSON.stringify(products));
const cart = JSON.parse(localStorage.getItem('cart') || '[]');
localStorage.removeItem('cart');

// sessionStorage живёт до закрытия вкладки

Частые ошибки

Частые вопросы

Чем отличается querySelector от getElementById?

querySelector принимает любой CSS-селектор и возвращает первое совпадение, getElementById ищет только по id и работает немного быстрее. На практике разница в скорости незаметна, поэтому обычно берут querySelector.

Почему нужен defer у скрипта?

Без него браузер выполняет скрипт сразу при встрече, до построения остальной страницы, и обращения к элементам возвращают null. defer откладывает выполнение до полной загрузки разметки.

Что лучше: fetch или XMLHttpRequest?

fetch: он основан на промисах, работает с async/await и читается заметно короче. XMLHttpRequest остаётся в старом коде и там, где нужно отслеживать прогресс загрузки файла.

Читайте также

Сделаем работу по этой теме

Опишите задачу — ответим в течение 15 минут в личных сообщениях ВКонтакте, назовём срок и цену. Предоплаты за оценку нет.

  • Оценка заявки бесплатно
  • Правки по замечаниям преподавателя
  • Работы по всем техническим и IT-дисциплинам

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