Управление выполнением скриптов при загрузке страницы в JavaScript
Пошаговое руководство по управлению несколькими скриптами при загрузке страницы в JavaScript. Использование DOMContentLoaded, async, defer и других методов.
Как правильно управлять выполнением нескольких скриптов при загрузке страницы в чистом JavaScript? Как обеспечить выполнение второго скрипта после полной загрузки DOM, если событие DOMContentLoaded срабатывает только один раз? Какие есть альтернативы повторной отправке события DOMContentLoaded для избежания рекурсии?
Для управления выполнением нескольких скриптов при загрузке страницы в чистом JavaScript следует использовать событие DOMContentLoaded и различные подходы к синхронизации. Основной принцип заключается в том, что событие DOMContentLoaded срабатывает только один раз, поэтому для выполнения второго скрипта после полной загрузки DOM можно добавить обработчики к тому же событию, использовать Promise или атрибуты defer и async в тегах скриптов.
Содержание
- Основные события загрузки страницы
- Управление несколькими скриптами с помощью DOMContentLoaded
- Альтернативы повторной отправке DOMContentLoaded
- Атрибуты async и defer для управления загрузкой
- Пользовательские события и Promise для синхронизации скриптов
- Практические примеры реализации
- Источники
- Заключение
Основные события загрузки страницы
При работе с JavaScript на веб-странице важно понимать ключевые события жизненного цикла загрузки. Основные события события загрузки, которые используются для управления выполнением скриптов:
DOMContentLoaded— срабатывает, когда HTML-документ полностью загружен и обработан, но внешние ресурсы (изображения, стили, фреймы) могут еще не загруженыload— срабатывает, когда вся страница вместе со всеми ресурсами полностью загруженаreadystatechange— событие для отслеживания состояния загрузки документа
Для управления загрузкой скрипта в JavaScript важно понимать, что браузер выполняет скрипты последовательно по умолчанию, что может блокировать рендеринг страницы. Оптимальное решение — использовать современные подходы к управлению выполнением скриптов.
Управление несколькими скриптами с помощью DOMContentLoaded
Событие DOMContentLoaded срабатывает только один раз, поэтому для управления несколькими скриптами необходимо использовать другие подходы. Вот основные методы:
Множественные обработчики одного события
Вы можете добавить несколько обработчиков к одному событию DOMContentLoaded:
document.addEventListener("DOMContentLoaded", () => {
// Первый скрипт
initFirstScript();
});
document.addEventListener("DOMContentLoaded", () => {
// Второй скрипт
initSecondScript();
});
Проверка состояния документа
Если скрипт может загружаться после того, как событие уже произошло, можно проверить document.readyState:
function runAfterDOM(fn) {
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', fn);
} else {
fn();
}
}
runAfterDOM(() => {
// Код, который должен выполниться после DOM готов
});
Использование Promise
Для более сложной синхронизации можно использовать Promise:
const domReady = new Promise(resolve => {
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", resolve);
} else {
resolve();
}
});
domReady.then(() => {
// Код, который должен выполниться после DOM готов
});
Альтернативы повторной отправке DOMContentLoaded
Чтобы избежать рекурсии и дублирования событий, существуют альтернативные подходы к управлению выполнением нескольких javascript скрипт:
Пользовательские события
Создайте собственное событие, которое вы можете триггерить после выполнения первого скрипта:
// Первый скрипт
document.addEventListener('DOMContentLoaded', () => {
initFirstScript();
// Триггерим пользовательское событие
const event = new CustomEvent('firstScriptLoaded', { detail: {} });
document.dispatchEvent(event);
});
// Второй скрипт
document.addEventListener('firstScriptLoaded', () => {
initSecondScript();
});
Прямое вызов функций
Просто вызывайте второй скрипт из первого:
document.addEventListener('DOMContentLoaded', () => {
initFirstScript();
initSecondScript(); // Выполняется сразу после первого
});
Использование флагов
let domReady = false;
document.addEventListener('DOMContentLoaded', () => {
domReady = true;
initFirstScript();
initSecondScript();
});
// Если скрипт загружается позже
if (domReady) {
initSecondScript();
}
Эти подходы позволяют избежать ошибки загрузки скрипта, связанной с повторной отправкой DOMContentLoaded.
Атрибуты async и defer для управления загрузкой
Для оптимальной отложенная загрузка скриптов существуют атрибуты async и defer:
Атрибут async
<script async src="first.js"></script>
<script async src="second.js"></script>
Скрипты с async загружаются параллельно и выполняются по завершении загрузки, независимо от порядка. Подходит для независимых скриптов.
Атрибут defer
<script defer src="first.js"></script>
<script defer src="second.js"></script>
Скрипты с defer загружаются параллельно, но выполняются последовательно после полной загрузки DOM и в порядке их расположения в документе. Идеально для скриптов, зависящих от DOM.
Сравнение атрибутов
| Атрибут | Загрузка | Выполнение | Порядок | Блокировка рендеринга |
|---|---|---|---|---|
| Без атрибута | Последовательно | По порядку | Да | Да |
| async | Параллельно | По завершении загрузки | Нет | Нет |
| defer | Параллельно | После DOM, по порядку | Да | Нет |
Эти атрибуты позволяют эффективно управлять загрузкой файла скрипт без сложного JavaScript-кода.
Пользовательские события и Promise для синхронизации скриптов
Для сложных сценариев синхронизации нескольких загрузка скриптов javascript можно использовать продвинутые подходы:
Синхронизация с Promise
function loadScript(url) {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = url;
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
}
// Использование
loadScript('first.js').then(() => {
console.log('Первый скрипт загружен');
return loadScript('second.js');
}).then(() => {
console.log('Второй скрипт загружен');
});
Манипуляция событиями загрузки
Для запуск скрипта при загрузке можно комбинировать различные подходы:
// Создаем глобальный объект для управления скриптами
const ScriptLoader = {
scripts: [],
load(url) {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = url;
script.onload = () => {
this.scripts.push(url);
resolve();
};
script.onerror = reject;
document.head.appendChild(script);
});
},
loadSequential(urls) {
return urls.reduce((promise, url) =>
promise.then(() => this.load(url)), Promise.resolve());
}
};
// Использование
ScriptLoader.loadSequential(['first.js', 'second.js'])
.then(() => console.log('Все скрипты загружены'));
Практические примеры реализации
Пример 1: Базовая реализация с DOMContentLoaded
// Основной скрипт
document.addEventListener('DOMContentLoaded', () => {
console.log('DOM загружен');
initializeApp();
});
// Если нужно выполнить код после полной загрузки страницы
window.addEventListener('load', () => {
console.log('Все ресурсы загружены');
initializeAnalytics();
});
Пример 2: Управление зависимостями скриптов
// Функция для загрузки скриптов с зависимостями
function loadScriptWithDependencies(url, dependencies = []) {
return new Promise((resolve, reject) => {
// Проверяем зависимости
Promise.all(dependencies.map(dep =>
window[dep] ? Promise.resolve() : Promise.reject(`Зависимость ${dep} не найдена`)
)).then(() => {
const script = document.createElement('script');
script.src = url;
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
}).catch(reject);
});
}
// Использование
loadScriptWithDependencies('app.js', ['jquery', 'lodash'])
.then(() => console.log('Приложение загружено'))
.catch(error => console.error('Ошибка загрузки:', error));
Пример 3: Отложенная загрузка скриптов
// Функция для отложенной загрузки скрипта
function loadScriptDeferred(url) {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = url;
script.defer = true;
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
}
// Использование для **загрузка скрипта после загрузки страницы**
if (document.readyState === 'complete') {
loadScriptDeferred('analytics.js');
} else {
window.addEventListener('load', () => {
loadScriptDeferred('analytics.js');
});
}
Эти примеры показывают различные подходы к управлению скрипт загрузка страницы в зависимости от требований проекта.
Источники
- MDN Web Docs — Документация по событию DOMContentLoaded: https://developer.mozilla.org/en-US/docs/Web/API/Document/DOMContentLoaded_event
- javascript.info — Обработка событий загрузки страницы: https://javascript.info/onload-ondomcontentloaded
- MDN Web Docs — Документация по тегу script: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script
Заключение
Управление выполнением нескольких скриптов при загрузке страницы в чистом JavaScript требует понимания жизненного цикла загрузки и использования различных подходов. Основные методы включают множественные обработчики одного события, проверку состояния документа, использование Promise, пользовательские события и атрибуты async и defer.
Ключевое правило — избегайте повторной отправки события DOMContentLoaded, так как оно срабатывает только один раз. Вместо этого используйте описанные альтернативы для синхронизации скриптов. Для оптимальной производительности и лучшего пользовательского опыта рекомендуется использовать атрибут defer для скриптов, которые должны выполняться после полной загрузки DOM, и async для независимых скриптов.
Правильная управление скриптами не только улучшает производительность, но и предотвращает потенциальные ошибки, связанные с доступом к DOM до его полной загрузки.
В MDN описывается, что событие DOMContentLoaded срабатывает один раз, когда HTML полностью разобран и все отложенные скрипты выполнены. Чтобы гарантировать, что второй скрипт выполнится после полной загрузки DOM, можно просто добавить его обработчик к тому же событию, либо использовать Promise, который резолвится в момент DOMContentLoaded. Например:
document.addEventListener("DOMContentLoaded", () => {
// первый скрипт
});
const domReady = new Promise(resolve => {
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", resolve);
} else {
resolve();
}
});
domReady.then(() => {
// второй скрипт
});
Если скрипт может загружаться позже, можно проверить document.readyState и сразу вызвать нужную функцию, если событие уже произошло. Для полной загрузки всех ресурсов, включая изображения, используйте событие load окна: window.addEventListener("load", () => { /* код */ });. Альтернативой повторной отправки DOMContentLoaded является создание собственного события, которое вы можете триггерить после выполнения первого скрипта, а второй скрипт будет слушать это событие. Также можно использовать атрибуты defer и type="module" в <script> тегах, чтобы браузер гарантировал выполнение скриптов после парсинга DOM.
Для управления выполнением нескольких скриптов при загрузке страницы в чистом JavaScript обычно используют событие DOMContentLoaded. Вы можете добавить к нему несколько обработчиков – каждый из них выполнится, когда DOM будет готов. Если второй скрипт должен запускаться только после того, как первый полностью завершил работу, можно внутри обработчика первого вызвать второй скрипт или же использовать Promise/async‑await. Например:
document.addEventListener('DOMContentLoaded', () => {
initFirst(); // первый скрипт
initSecond(); // второй скрипт после первого
});
Если скрипт загружается после того, как событие уже произошло, можно проверить document.readyState и вызвать функцию сразу:
function runAfterDOM(fn) {
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', fn);
} else {
fn();
}
}
Для полной загрузки всех ресурсов можно использовать событие load на window:
window.addEventListener('load', () => {
// код после полной загрузки
});
Также удобно использовать атрибут defer в теге <script>, чтобы скрипт исполнялся после парсинга DOM, но до события load. При необходимости синхронизации нескольких скриптов можно использовать пользовательские события или Promise, чтобы избежать рекурсии и лишних вызовов DOMContentLoaded.
Для управления порядком выполнения нескольких скриптов в чистом JavaScript удобно использовать атрибуты defer и async. Скрипты с атрибутом defer загружаются параллельно, но выполняются только после того, как весь документ распарсится, и в том порядке, в котором они находятся в разметке. Это гарантирует, что второй скрипт выполнится после полной загрузки DOM, и событие DOMContentLoaded не будет срабатывать дважды. Если нужно выполнить код сразу после полной загрузки DOM, можно добавить обработчик document.addEventListener('DOMContentLoaded', ...) один раз и внутри него вызвать нужный код, либо использовать window.onload для полной загрузки всех ресурсов. Альтернативой повторной отправки события DOMContentLoaded является использование пользовательского события (например, new CustomEvent('myScriptsLoaded')) и его слушателей, либо просто вызывать функции напрямую после загрузки скриптов. Ниже приведён пример использования defer:
<script defer src="first.js"></script>
<script defer src="second.js"></script>

