Могу ли я запустить javascript до загрузки всей страницы?

97

Я хочу запустить javascript до того, как загрузится вся страница. Это возможно? Или код начинает выполняться </html>?

пикник
источник
2
Я не думаю, что это дубликат - это скорее вопрос связи сервер-клиент.
scunliffe

Ответы:

186

Не только можете , но и должны приложить особые усилия, чтобы не делать этого, если не хотите. :-)

Когда браузер встречает классический scriptтег при синтаксическом анализе HTML, он прекращает синтаксический анализ и передает его интерпретатору JavaScript, который запускает сценарий. Анализатор не продолжает работу до завершения выполнения сценария (поскольку сценарий может выполнять document.writeвызовы для вывода разметки, которую синтаксический анализатор должен обработать).

Это поведение по умолчанию, но у вас есть несколько вариантов отсрочки выполнения скрипта:

  1. Используйте модули JavaScript. type="module"Сценарий откладывается до тех пор , HTML не был полностью разобран и первоначальный DOM создан. Это не основная причина использования модулей, но одна из причин:

    <script type="module" src="./my-code.js"></script>
    <!-- Or -->
    <script type="module">
    // Your code here
    </script>

    Код будет извлечен (если он отдельный) и проанализирован параллельно с синтаксическим анализом HTML, но не будет запущен, пока не будет выполнен синтаксический анализ HTML. (Если код вашего модуля является встроенным, а не в собственном файле, он также откладывается до завершения синтаксического анализа HTML.)

    Этого не было, когда я впервые написал этот ответ в 2010 году, но здесь, в 2020 году, все основные современные браузеры поддерживают модули изначально, и если вам нужно поддерживать старые браузеры, вы можете использовать такие комплекты, как Webpack и Rollup.js.

  2. Используйте deferатрибут в классическом теге скрипта:

    <script defer src="./my-code.js"></script>

    Как и в случае с модулем, код my-code.jsбудет извлечен и проанализирован параллельно с синтаксическим анализом HTML, но не будет запущен, пока не будет выполнен анализ HTML. Но , deferне работает с встроенным содержанием сценария, только с внешними файлами с помощью ссылки src.

  3. Я не думаю, что это то, что вы хотите, но вы можете использовать asyncатрибут, чтобы указать браузеру, что нужно извлекать код JavaScript параллельно с синтаксическим анализом HTML, но затем запустить его как можно скорее, даже если синтаксический анализ HTML не завершен. . Вы можете прикрепить его к type="module"тегу или использовать вместо deferклассического scriptтега.

  4. Поместите scriptтег в конец документа, непосредственно перед закрывающим </body>тегом:

    <!doctype html>
    <html>
    <!-- ... -->
    <body>
    <!-- The document's HTML goes here -->
    <script type="module" src="./my-code.js"></script><!-- Or inline script -->
    </body>
    </html>

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

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

Спецификации имеют полезную схему , показывающую сырой scriptтег, defer, async, type="module", и type="module" asyncи сроки , когда код JavaScript извлекается и запустить:

введите описание изображения здесь

Вот пример поведения по умолчанию, необработанный scriptтег:

.found {
    color: green;
}
<p>Paragraph 1</p>
<script>
    if (typeof NodeList !== "undefined" && !NodeList.prototype.forEach) {
        NodeList.prototype.forEach = Array.prototype.forEach;
    }
    document.querySelectorAll("p").forEach(p => {
        p.classList.add("found");
    });
</script>
<p>Paragraph 2</p>

(Подробнее об этом коде см. В моем ответеNodeList .)

Когда вы запустите это, вы увидите «Параграф 1» зеленым, а «Параграф 2» - черным, потому что сценарий работал синхронно с синтаксическим анализом HTML, и поэтому он нашел только первый абзац, а не второй.

Напротив, вот type="module"сценарий:

.found {
    color: green;
}
<p>Paragraph 1</p>
<script type="module">
    document.querySelectorAll("p").forEach(p => {
        p.classList.add("found");
    });
</script>
<p>Paragraph 2</p>

Обратите внимание, как они оба теперь зеленые; код не запускался, пока не был завершен анализ HTML. То же самое будет и defer scriptс внешним содержимым (но не со встроенным содержимым).

(Там не было никакой необходимости в NodeListпроверке там , потому что любой современный браузер поддерживает модули уже есть forEachна NodeList.)

В этом современном мире нет реальной ценности в DOMContentLoadedслучае «готовой» функции, которую PrototypeJS, jQuery, ExtJS, Dojo и большинство других предоставили в свое время (и все еще предоставляют); просто используйте модули или defer. Даже когда-то не было особых причин для их использования (и они часто использовались неправильно, задерживая презентацию страницы, пока вся библиотека jQuery была загружена, потому что она scriptбыла в, headа не после документа), что некоторые разработчики отметили Google рано обратил внимание. Это также было одной из причин, по которой YUI рекомендовал ставить скрипты в конце body, снова в тот же день.

TJ Crowder
источник
1
@FeRD - Спасибо за редактирование. Рецензенты отвергли это, но вы были совершенно правы. :-)
TJ Crowder
Или поместите все это в функцию и вставьте <body onload="doIt()>".
Джастин Лю
@JustinLiu - loadсобытие происходит очень поздно в цикле загрузки страницы, почти никогда не рекомендуется ждать, чтобы запустить свой JavaScript до этого момента. Но да, это вариант. Кстати, я пересмотрел ответ на 2020 год.
TJ Crowder,
Или вы можете использоватьdocument.addEventListener('DOMContentLoaded', function(){ //doyour stuff })
Джастин Лю
@JustinLiu - Ага, смотрите последний абзац. :-) Я никогда не видел необходимости в этом, но вы можете.
TJ Crowder,
6

Вы можете запустить код javascript в любое время. AFAIK он выполняется в тот момент, когда браузер достигает тега <script>, в котором он находится. Но вы не можете получить доступ к элементам, которые еще не загружены.

Поэтому, если вам нужен доступ к элементам, вы должны дождаться загрузки DOM (это не означает, что загружается вся страница, включая изображения и прочее. Это всего лишь структура документа, которая загружается намного раньше, поэтому вы обычно выигрываете. не замечаю задержки), используя DOMContentLoadedсобытие или функции как $.readyв jQuery.

Мариан
источник
2
Событие DOMContentLoaded срабатывает, как только иерархия DOM будет полностью построена, событие загрузки сделает это, когда все изображения и субкадры закончат загрузку.
BeauCielBleu