Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как мы работаем с DOM?
DOM (Document Object Model) - это представление HTML документа в виде дерева объектов, которым браузер позволяет манипулировать через JavaScript. Это фундаментальный навык фронтенд-разработчика. Я часто работаю с DOM, и знаю как делать это эффективно.
Основы: выбор элементов
// 1. getElementById - самый быстрый способ
const header = document.getElementById('header');
// 2. querySelector/querySelectorAll - универсальный способ
const mainDiv = document.querySelector('.main');
const buttons = document.querySelectorAll('button');
// 3. getElementsByClassName/getElementsByTagName - медленнее
const items = document.getElementsByClassName('item');
const paragraphs = document.getElementsByTagName('p');
Важное различие:
getElementById,querySelector,querySelectorAllвозвращают статический результатgetElementsByClassName,getElementsByTagNameвозвращают живую коллекцию (обновляется при изменении DOM)
// Живая коллекция - опасно!
const items = document.getElementsByClassName('item');
// Если добавить элемент с классом item - коллекция обновится
// Статическая коллекция - безопаснее
const items = document.querySelectorAll('.item');
// Коллекция остаётся неизменной
Создание и удаление элементов
// Создание элемента
const newDiv = document.createElement('div');
newDiv.className = 'box';
newDiv.textContent = 'Hello';
// Добавление в DOM
const container = document.querySelector('.container');
container.appendChild(newDiv);
// Или более модерный способ
container.insertAdjacentHTML('beforeend', '<div class="box">Hello</div>');
// Удаление элемента
newDiv.remove();
// или старый способ
newDiv.parentElement.removeChild(newDiv);
Изменение свойств и атрибутов
const element = document.querySelector('.button');
// Свойства (properties)
element.textContent = 'Click me'; // текст
element.innerHTML = '<span>Click</span>'; // HTML
element.className = 'btn primary'; // CSS класс
element.style.color = 'red'; // встроенный стиль
// Атрибуты (attributes)
element.setAttribute('data-id', '123');
element.getAttribute('data-id'); // '123'
element.removeAttribute('disabled');
element.hasAttribute('disabled'); // false
// Или напрямую доступ к свойствам
element.id = 'my-button';
element.disabled = false;
element.dataset.id = '123'; // data-id="123"
Навигация по DOM дереву
const element = document.querySelector('.item');
// Родители
element.parentElement; // прямой родитель
element.parentNode; // может быть текстовый узел
element.closest('.container'); // ближайший родитель с классом container
// Дети
element.children; // только элементы
element.childNodes; // элементы и текстовые узлы
element.firstElementChild; // первый дочерний элемент
element.lastElementChild; // последний дочерний элемент
// Соседи
element.nextElementSibling; // следующий сосед
element.previousElementSibling; // предыдущий сосед
Работа с CSS классами
const button = document.querySelector('button');
// classList API (рекомендуется)
button.classList.add('active'); // добавить класс
button.classList.remove('disabled'); // удалить класс
button.classList.toggle('highlight'); // переключить класс
button.classList.contains('active'); // проверить наличие
// Несколько классов одновременно
button.classList.add('active', 'primary', 'large');
// Замена класса
button.classList.replace('old-class', 'new-class');
Обработка событий
const button = document.querySelector('button');
// Добавление слушателя
button.addEventListener('click', (event) => {
console.log('Clicked!', event);
});
// Обработка разных событий
button.addEventListener('mouseover', (e) => {
button.style.backgroundColor = 'blue';
});
button.addEventListener('mouseout', (e) => {
button.style.backgroundColor = '';
});
// Удаление слушателя
const handleClick = (event) => {
console.log('Click');
};
button.addEventListener('click', handleClick);
button.removeEventListener('click', handleClick);
// Делегирование событий (важно!)
const list = document.querySelector('.item-list');
list.addEventListener('click', (event) => {
if (event.target.classList.contains('delete-btn')) {
const item = event.target.closest('.item');
item.remove();
}
});
Практический пример: интерактивный список
class TodoList {
constructor() {
this.list = document.querySelector('.todo-list');
this.input = document.querySelector('.todo-input');
this.addBtn = document.querySelector('.add-btn');
this.todos = [];
this.setupListeners();
}
setupListeners() {
this.addBtn.addEventListener('click', () => this.addTodo());
this.input.addEventListener('keypress', (e) => {
if (e.key === 'Enter') this.addTodo();
});
// Делегирование
this.list.addEventListener('click', (e) => {
if (e.target.classList.contains('delete')) {
const id = e.target.closest('.todo-item').dataset.id;
this.deleteTodo(id);
}
});
}
addTodo() {
const text = this.input.value.trim();
if (!text) return;
const id = Date.now();
const todo = { id, text, completed: false };
this.todos.push(todo);
const li = document.createElement('li');
li.className = 'todo-item';
li.dataset.id = id;
li.innerHTML = `
<span class="text">${text}</span>
<button class="delete">Delete</button>
`;
this.list.appendChild(li);
this.input.value = '';
}
deleteTodo(id) {
this.todos = this.todos.filter(t => t.id !== id);
const element = document.querySelector(`[data-id="${id}"]`);
element.remove();
}
}
const todoApp = new TodoList();
Производительность: batch обновления
// ПЛОХО - перерендер DOM много раз
for (let i = 0; i < 1000; i++) {
const div = document.createElement('div');
div.textContent = `Item ${i}`;
document.body.appendChild(div); // Репринт каждый раз!
}
// ХОРОШО - один репринт
const fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
const div = document.createElement('div');
div.textContent = `Item ${i}`;
fragment.appendChild(div);
}
document.body.appendChild(fragment); // Один репринт!
// ИЛИ использовать innerHTML
let html = '';
for (let i = 0; i < 1000; i++) {
html += `<div>Item ${i}</div>`;
}
document.body.innerHTML = html;
Работа с стилями
const box = document.querySelector('.box');
// Установка стилей (избегай, это неэффективно)
box.style.width = '100px';
box.style.height = '100px';
box.style.backgroundColor = 'red';
// ЛУЧШЕ: используй CSS классы
box.classList.add('styled-box');
// CSS
/*
.styled-box {
width: 100px;
height: 100px;
background-color: red;
}
*/
// Получение вычисленных стилей
const computedStyle = window.getComputedStyle(box);
console.log(computedStyle.backgroundColor);
console.log(computedStyle.width);
Условия содержимого (но в React это не нужно)
// Проверка наличия класса
if (button.classList.contains('active')) {
console.log('Button is active');
}
// Проверка содержимого
if (button.textContent.includes('Submit')) {
console.log('This is submit button');
}
// Проверка атрибута
if (button.hasAttribute('disabled')) {
console.log('Button is disabled');
}
В современном фронтенде (React, Vue)
Важно: В React и Vue работа с DOM абстрагирована:
// React - не работаем с DOM напрямую
function MyButton() {
const [isActive, setIsActive] = useState(false);
return (
<button
className={isActive ? 'active' : ''}
onClick={() => setIsActive(!isActive)}
>
Click me
</button>
);
}
// React сам управляет DOM, нам не нужно вызывать
// document.querySelector, appendChild и т.д.
Когда нужен прямой доступ к DOM:
// useRef для прямого доступа к элементу
function TextInput() {
const inputRef = useRef(null);
const focusInput = () => {
inputRef.current.focus(); // Прямой доступ к DOM
};
return (
<>
<input ref={inputRef} />
<button onClick={focusInput}>Focus</button>
</>
);
}
Частые ошибки
// ОШИБКА 1: использование innerHTML для пользовательского ввода (XSS!)
element.innerHTML = userInput; // Опасно!
// ИСПРАВИТЬ:
element.textContent = userInput; // Безопасно
// или санитизировать перед вставкой
// ОШИБКА 2: забыть что DOM живой
const items = document.getElementsByClassName('item');
for (let item of items) {
item.remove(); // Может пропустить элементы!
}
// ИСПРАВИТЬ:
const items = Array.from(document.querySelectorAll('.item'));
for (let item of items) {
item.remove();
}
// ОШИБКА 3: частые обращения в цикле
for (let i = 0; i < 1000; i++) {
document.body.innerHTML += `<div>Item ${i}</div>`; // Медленно!
}
// ИСПРАВИТЬ: батч обновления
let html = '';
for (let i = 0; i < 1000; i++) {
html += `<div>Item ${i}</div>`;
}
document.body.innerHTML = html; // Быстро
Итог
Я работаю с DOM через:
- querySelector/querySelectorAll - выбор элементов
- classList API - управление классами
- addEventListener - обработка событий
- Делегирование событий - для эффективности
- DocumentFragment/innerHTML - для батч обновлений
- В React/Vue - через state и refs, минимизируя прямой доступ
Самое важное - помнить, что DOM операции дорогие (вызывают repaint/reflow), поэтому их нужно минимизировать и батчить при возможности.