5 нативных способов заменить jQuery ready в современном JavaScript
Для кого эта статья:
- Веб-разработчики, желающие улучшить свои навыки в использовании JavaScript
- Специалисты, сталкивающиеся с производительностью веб-приложений и кроссбраузерностью
Люди, интересующиеся современными подходами к разработке и оптимизации frontend-кода
Помните, как jQuery когда-то вошла в нашу жизнь и решила множество проблем кроссбраузерности? Знаменитый
$(document).ready()был как глоток свежего воздуха для разработчиков. Но времена меняются, браузеры эволюционируют, а производительность становится ключевым фактором. Сегодня зависимость от jQuery в 33 килобайта ради одной функции — непозволительная роскошь. К счастью, современный JavaScript предлагает нам элегантные нативные решения, которые не только легче, но и работают быстрее. Рассмотрим 5 способов, которые позволят вам забыть о jQuery.ready навсегда. 🚀
Если вы стремитесь перейти от jQuery к современному JavaScript и прокачать свои навыки веб-разработки, обратите внимание на обучение веб-разработке от Skypro. Программа включает углубленное изучение нативного JavaScript, современных фреймворков и оптимизации frontend-производительности. Вы научитесь создавать быстрые, отзывчивые приложения без лишних зависимостей и устаревших подходов — навыки, которые высоко ценятся на рынке труда.
Почему стоит отказаться от
jQuery был создан в 2006 году, когда разрыв между реализациями JavaScript в разных браузерах был огромен. Он решал критическую проблему: обеспечивал единообразный API для работы с DOM и событиями. Однако современные браузеры значительно сократили этот разрыв, и многие возможности, которые jQuery привнес в веб-разработку, теперь доступны нативно.
Рассмотрим ключевые причины для отказа от jQuery в пользу чистого JavaScript:
- Размер библиотеки: Минимизированная версия jQuery 3.6 весит около 33 КБ (сжатая gzip). Это может показаться незначительным, но для оптимизированных приложений каждый килобайт на счету.
- Производительность: Нативные методы JavaScript выполняются быстрее, чем обертки jQuery. Это особенно заметно на мобильных устройствах с ограниченными ресурсами.
- Поддержка устаревших браузеров: Один из основных аргументов в пользу jQuery — поддержка IE8 и старше — уже не актуален. Доля этих браузеров на рынке стремится к нулю.
- Чрезмерная абстракция: jQuery вводит дополнительный слой абстракции, который скрывает важные концепции JavaScript, что может затруднить понимание основ языка.
Михаил Ковалев, технический директор
Я присоединился к проекту, который использовал jQuery для всего, включая базовый document.ready. Сайт загружался медленно, особенно на мобильных устройствах. Первым шагом к оптимизации стала замена
$(document).readyна нативный DOMContentLoaded. Это было просто:JSСкопировать код$(document).ready(function() { // jQuery код });превратился в:
JSСкопировать кодdocument.addEventListener('DOMContentLoaded', function() { // Тот же функционал, но на чистом JS });Это небольшое изменение, вместе с другими оптимизациями, привело к снижению времени загрузки на 28% и улучшило показатели Core Web Vitals. Клиент был впечатлен тем, как такие простые изменения могут значительно улучшить пользовательский опыт.
Чтобы оценить разницу в производительности между jQuery и нативным JavaScript, рассмотрим следующие показатели:
| Характеристика | jQuery | Нативный JavaScript |
|---|---|---|
| Размер кода | 33 КБ (минимизированный) | 0 КБ (встроен в браузер) |
| Время выполнения (относительное) | 1x (базовый) | До 10x быстрее |
| Потребление памяти | Высокое | Низкое |
| Время инициализации | 200-300 мс | < 5 мс |
Эти цифры говорят сами за себя. Теперь рассмотрим конкретные способы замены $(document).ready нативными методами JavaScript. 🔄

DOMContentLoaded: надежный способ замены jQuery ready
Событие DOMContentLoaded — это нативный и наиболее близкий эквивалент jQuery $(document).ready(). Оно срабатывает, когда исходный HTML-документ полностью загружен и разобран, не дожидаясь загрузки стилей, изображений и других внешних ресурсов.
В отличие от jQuery, которая абстрагирует различные способы определения загрузки DOM, в чистом JavaScript мы используем стандартизированный метод addEventListener:
document.addEventListener('DOMContentLoaded', function() {
console.log('DOM полностью загружен и разобран');
// Здесь ваш код для взаимодействия с DOM
});
Этот подход имеет несколько преимуществ:
- Стандартизация: DOMContentLoaded является частью спецификации W3C и поддерживается всеми современными браузерами.
- Производительность: Не требует дополнительной библиотеки, что ускоряет загрузку страницы.
- Чёткая семантика: Название события точно отражает то, что оно делает, улучшая читаемость кода.
- Возможность удаления: В отличие от jQuery.ready, можно легко удалить обработчик при необходимости.
Для лучшего понимания работы DOMContentLoaded и его отличия от jQuery.ready, рассмотрим типичные случаи использования:
// jQuery способ
$(document).ready(function() {
$('#myButton').click(function() {
alert('Кнопка нажата!');
});
});
// Эквивалент на чистом JavaScript
document.addEventListener('DOMContentLoaded', function() {
document.getElementById('myButton').addEventListener('click', function() {
alert('Кнопка нажата!');
});
});
Важно отметить ключевую разницу: DOMContentLoaded не ждет загрузки изображений и стилей, в то время как jQuery может иметь небольшую задержку из-за внутренней логики и проверок. Это делает нативное решение более предсказуемым и часто более быстрым.
Для более сложных сценариев, DOMContentLoaded можно комбинировать с другими событиями и API:
// Комбинированный подход для прогрессивного улучшения
document.addEventListener('DOMContentLoaded', function() {
// Инициализация базового функционала
initBaseFunctionality();
// Регистрация обработчика для полной загрузки страницы
window.addEventListener('load', function() {
// Инициализация функций, требующих загрузки всех ресурсов
initAdvancedFeatures();
});
});
Такой подход позволяет создавать отзывчивые интерфейсы, которые начинают работать сразу после разбора DOM, а затем улучшаются при полной загрузке всех ресурсов. 🔍
Проверка document.readyState для обратной совместимости
Несмотря на широкую поддержку DOMContentLoaded, иногда требуется учитывать более сложные сценарии, особенно при разработке библиотек или компонентов, которые должны работать в различных контекстах. В таких случаях проверка document.readyState предоставляет мощный и гибкий механизм.
Свойство document.readyState может иметь одно из трех значений:
- loading — документ загружается;
- interactive — документ загружен и разобран, но ресурсы, такие как изображения, все еще загружаются (эквивалент момента срабатывания DOMContentLoaded);
- complete — документ и все ресурсы полностью загружены (эквивалент момента срабатывания window.onload).
Используя это свойство, можно создать универсальный метод, который гарантирует выполнение кода в нужный момент, независимо от того, на каком этапе находится загрузка документа:
function docReady(callback) {
// Если документ уже в состоянии interactive или complete
if (document.readyState === 'interactive' || document.readyState === 'complete') {
// Выполнить callback асинхронно
setTimeout(callback, 0);
} else {
// Иначе, ждем DOMContentLoaded
document.addEventListener('DOMContentLoaded', callback);
}
}
// Использование
docReady(function() {
console.log('DOM готов к взаимодействию!');
});
Этот подход особенно полезен в следующих ситуациях:
- Когда скрипт может быть загружен как синхронно, так и асинхронно;
- Для библиотек и компонентов, которые могут быть подключены на разных этапах загрузки страницы;
- При создании полифиллов для поддержки старых браузеров;
- В системах динамической загрузки контента.
Дополнительно, вы можете использовать событие readystatechange для отслеживания изменений состояния загрузки документа:
document.addEventListener('readystatechange', function() {
console.log('Состояние документа: ' + document.readyState);
if (document.readyState === 'interactive') {
console.log('DOM доступен для взаимодействия');
}
if (document.readyState === 'complete') {
console.log('Страница полностью загружена');
}
});
Для полного понимания различий между методами, рассмотрим сравнительную таблицу:
| Подход | Совместимость | Когда срабатывает | Особенности |
|---|---|---|---|
$(document).ready() | Требует jQuery | После разбора DOM | Абстрагирует различные механизмы |
| DOMContentLoaded | IE9+, все современные браузеры | После разбора DOM | Чистый, стандартизированный подход |
| document.readyState | Все браузеры | Зависит от проверяемого состояния | Гибкий, подходит для библиотек |
| readystatechange | Все браузеры | При изменении readyState | Позволяет отслеживать разные этапы |
Елена Смирнова, lead frontend-разработчик
На одном из моих проектов мы столкнулись с необычной проблемой. У нас была библиотека визуализации данных, которая должна была инициализироваться после загрузки DOM, но мы не могли контролировать момент подключения скрипта. Иногда он загружался до загрузки DOM, иногда после.
Использование только DOMContentLoaded приводило к непредсказуемым результатам. Переписав код с использованием document.readyState, мы обеспечили стабильную работу вне зависимости от времени подключения:
JSСкопировать кодfunction initVisualizer() { if (document.readyState !== 'loading') { initializeCharts(); } else { document.addEventListener('DOMContentLoaded', initializeCharts); } }Этот простой паттерн стал нашим стандартом для всех компонентов, которые требуют доступа к DOM. Он работает надежно во всех браузерах, и мы больше не беспокоимся о последовательности загрузки скриптов.
window.onload и его отличия от DOMContentLoaded
Событие window.onload является еще одной альтернативой для $(document).ready(), но с существенным отличием: оно срабатывает только после того, как все ресурсы страницы (изображения, стили, скрипты, iframe и т.д.) полностью загружены. Это делает его более похожим на $(window).load() в jQuery, чем на $(document).ready().
Существуют два способа использования этого события:
// Способ 1: Свойство onload
window.onload = function() {
console.log('Страница полностью загружена, включая все ресурсы');
};
// Способ 2: Слушатель событий
window.addEventListener('load', function() {
console.log('Страница полностью загружена, включая все ресурсы');
});
Второй способ предпочтительнее, так как позволяет назначить несколько обработчиков для одного события, в то время как первый способ перезаписывает любой ранее назначенный обработчик.
Давайте рассмотрим ключевые различия между DOMContentLoaded и window.onload:
- Время срабатывания: DOMContentLoaded срабатывает, когда HTML полностью загружен и разобран, даже если стили, изображения и подфреймы ещё не загружены. window.onload срабатывает только после загрузки всех ресурсов.
- Применение: DOMContentLoaded идеален для большинства операций с DOM, не требующих доступа к изображениям. window.onload необходим, когда требуется доступ к размерам изображений или другим внешним ресурсам.
- Производительность: DOMContentLoaded обычно срабатывает намного раньше, особенно на страницах с большим количеством изображений или внешних ресурсов.
Для понимания практической разницы во времени срабатывания, рассмотрим типичный сценарий:
console.time('DOM Loading Time');
document.addEventListener('DOMContentLoaded', function() {
console.timeEnd('DOM Loading Time');
console.time('Full Page Load Time');
});
window.addEventListener('load', function() {
console.timeEnd('Full Page Load Time');
});
На страницах с большим количеством изображений разница между DOMContentLoaded и load может составлять секунды или даже десятки секунд.
Примеры задач, для которых подходит каждый из методов:
Для DOMContentLoaded:
- Инициализация UI-компонентов
- Установка обработчиков событий
- Валидация форм
- Начальная анимация
- Загрузка данных AJAX
Для window.onload:
- Манипуляции с размерами изображений
- Вычисление общего размера страницы
- Инициализация компонентов, требующих всех ресурсов
- Ленивая загрузка дополнительного контента
- Аналитика производительности полной загрузки страницы
В современной разработке наблюдается тенденция к использованию DOMContentLoaded вместо window.onload для большинства задач, так как это обеспечивает более быстрый отклик интерфейса. Событие load обычно используется только для специфических случаев, требующих информации о загруженных ресурсах. 🖼️
Современные альтернативы: async/defer и модуль-паттерн
Помимо традиционных методов обработки загрузки DOM, современный JavaScript предлагает продвинутые подходы, которые во многих случаях полностью устраняют необходимость в эквиваленте $(document).ready(). Эти подходы не только упрощают код, но и значительно улучшают производительность загрузки страницы. 💡
1. Атрибуты async и defer для скриптов
Атрибуты async и defer в теге script изменяют стандартное поведение загрузки и выполнения скриптов:
<!-- Обычный синхронный скрипт – блокирует парсинг HTML -->
<script src="script.js"></script>
<!-- Асинхронная загрузка – не блокирует HTML, выполняется как только загружен -->
<script async src="script-async.js"></script>
<!-- Отложенное выполнение – не блокирует HTML, выполняется после разбора DOM -->
<script defer src="script-defer.js"></script>
Особенности атрибута defer:
- Скрипты загружаются параллельно с разбором HTML
- Выполнение откладывается до завершения разбора DOM (перед DOMContentLoaded)
- Сохраняется порядок выполнения скриптов
- По сути, эмулирует поведение
$(document).ready()без необходимости явно его использовать
Особенности атрибута async:
- Скрипты загружаются параллельно с разбором HTML
- Выполняются сразу после загрузки, потенциально до завершения разбора DOM
- Не гарантирует порядок выполнения
- Лучше для независимых скриптов (аналитика, трекинг)
Использование defer практически полностью устраняет необходимость в $(document).ready() или DOMContentLoaded, так как скрипты с этим атрибутом автоматически выполняются в нужный момент:
<!-- С defer скрипт выполнится после разбора DOM -->
<script defer src="app.js"></script>
<!-- В самом скрипте уже не нужен DOMContentLoaded -->
<script>
// app.js
const button = document.getElementById('myButton');
button.addEventListener('click', () => {
alert('Кнопка работает!');
});
</script>
2. JavaScript модули (ES Modules)
Современные JavaScript модули (с атрибутом type="module") имеют встроенное поведение defer:
<script type="module" src="module.js"></script>
Это означает, что все модули:
- Загружаются асинхронно, не блокируя HTML-парсер
- Выполняются только после полного разбора HTML
- Выполняются в порядке их появления в документе
- Не требуют обертки в DOMContentLoaded или ready
3. Самовыполняющиеся функции (IIFE) и модуль-паттерн
Для инкапсуляции кода и создания приватной области видимости используется паттерн модуля с помощью IIFE (Immediately Invoked Function Expression):
// Размещение этого скрипта внизу страницы или с defer
// устраняет необходимость в проверке готовности DOM
(function() {
// Приватная область видимости
const privateVariable = 'Я не доступен извне';
// Инициализация модуля
function init() {
const button = document.getElementById('myButton');
button.addEventListener('click', handleClick);
}
function handleClick() {
console.log('Кнопка нажата');
}
// Автоинициализация
init();
})();
Сравнение эффективности различных подходов:
| Метод | Производительность | Блокировка парсинга | Удобство использования |
|---|---|---|---|
$(document).ready() | Низкая (требует jQuery) | Зависит от размещения | Простое, но устаревшее |
| DOMContentLoaded | Высокая | Зависит от размещения | Чистый JavaScript |
script[defer] | Очень высокая | Нет | Просто добавить атрибут |
| ES Modules | Очень высокая | Нет (встроенный defer) | Современно, с преимуществами модульности |
| Скрипт в конце body | Высокая | Минимальная | Простое, без дополнительного кода |
Для максимальной производительности рекомендуется комбинированный подход:
- Используйте атрибут defer для большинства скриптов
- Для модульного кода применяйте ES Modules (type="module")
- Для скриптов, независимых от DOM, рассмотрите async
- Размещайте критические инициализирующие скрипты перед закрывающим
</body>
Эти современные подходы не только заменяют $(document).ready(), но и обеспечивают лучшую производительность загрузки страницы, оптимизируя критический путь рендеринга и сокращая время до интерактивности (TTI). 🚀
Переход от jQuery к нативному JavaScript не просто тренд — это необходимый шаг эволюции для разработчика. Пять рассмотренных способов замены
$(document).ready()предлагают различные уровни гибкости, производительности и контроля. Для большинства современных проектов оптимальной стратегией будет комбинация атрибута defer для скриптов и ES Modules для модульного кода. Это обеспечивает не только производительность, но и поддерживаемость кода в долгосрочной перспективе. Отказ от jQuery в пользу нативных решений — инвестиция в будущее вашего проекта и вашу ценность как разработчика.