Чи можу я запустити javascript до завантаження цілої сторінки?


96

Я хочу запустити трохи 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, знову тому ж дні.


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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.