JavaScript на странице: способы встраивания через тег script
Основные методы подключения тега script
Веб-разработка предполагает тесную связь HTML, CSS и JavaScript. Тег <script> является основным инструментом для внедрения JS-кода в HTML-документ. Существует несколько подходов, и наиболее предпочтительным в современных проектах считается использование атрибута defer для внешних скриптов.
Вопрос: Как подключить внешний JavaScript, чтобы не блокировать отображение страницы и сохранить порядок выполнения?
Ответ: атрибут defer. Пример:
<script src='app.js' defer></script>Link href js html (подключение javascript файлов в html)
Пояснение: атрибут defer даёт браузеру команду загружать скрипт параллельно с парсингом HTML, но откладывает его выполнение до момента, когда весь HTML будет полностью разобран. Скрипты с defer выполняются в порядке их появления в документе, непосредственно перед событием DOMContentLoaded. Это обеспечивает доступ к полной DOM-структуре без задержек рендеринга.
Типичные проблемы и их решения:
- Проблема:
deferне работает для встроенных (inline) скриптов. Решение: для inline-кода используется размещение в конце<body>или атрибутtype='module'. - Проблема: Старые браузеры (IE9 и ниже) не поддерживают
defer. Решение: для совместимости добавлять скрипт в конце<body>или использовать полифиллы. - Проблема: Скрипт использует
document.write. Решение: заменитьdocument.writeна современные методы манипуляции DOM, так какdeferне предотвращает проблем, связанных с этим устаревшим методом.
Вариант: внутренний скрипт в конце <body>
Вопрос: Как выполнить скрипт после загрузки всех HTML-элементов без использования дополнительных атрибутов?
Ответ: разместить тег <script> непосредственно перед закрывающим тегом </body>. Пример:
<!DOCTYPE html>
<html>
<head>...</head>
<body>
<!-- содержимое страницы -->
<script>
console.log('Страница загружена');
</script>
</body>
</html>Js теги (теги script в html)
Пояснение: браузер парсит HTML сверху вниз. Когда он встречает <script> (без атрибутов async или defer), он приостанавливает парсинг, загружает (если внешний) и выполняет скрипт. Размещая скрипт в самом конце, гарантируется, что все предыдущие элементы DOM уже доступны. Это простой и надёжный способ для небольших скриптов.
Типичные проблемы и их решения:
- Проблема: Если скрипт внешний (
src), загрузка файла может задержать отображение страницы, так как браузер ждёт завершения загрузки перед продолжением парсинга. Решение: использоватьdeferилиasync. - Проблема: Для больших inline-скриптов парсинг HTML задерживается, увеличивая время до первого рендеринга. Решение: вынести код во внешний файл с
defer.
Вариант: внешний скрипт в <head> без атрибутов
Вопрос: Как подключить внешний файл, когда необходимо, чтобы скрипт выполнился до начала отображения содержимого?
Ответ: разместить <script src='script.js'></script> в <head>. Пример:
<head>
<script src='analytics.js'></script>
</head>
Пояснение: такой способ блокирует парсинг и рендеринг HTML до полной загрузки и выполнения скрипта. Используется редко, в основном для скриптов, которые должны быть доступны с самого начала (например, для переопределения глобальных функций или для библиотек, необходимых для инициализации).
Типичные проблемы и их решения:
- Проблема: Сильное замедление загрузки страницы, особенно при медленных сетях. Решение: переместить скрипт в конец
<body>или добавитьasync/defer. - Проблема: Если скрипт обращается к элементам DOM, которые ещё не созданы, возникает ошибка. Решение: обернуть код в обработчик события
DOMContentLoadedили использоватьdefer.
Вариант: атрибут async
Вопрос: Как загрузить внешний скрипт параллельно и выполнить его сразу после загрузки, не дожидаясь полного парсинга HTML?
Ответ: добавить атрибут async. Пример:
<script src='analytics.js' async></script>
Пояснение: браузер загружает скрипт асинхронно (не блокируя парсинг). Как только файл загружен, скрипт выполняется, даже если HTML ещё не полностью разобран. Порядок выполнения нескольких async-скриптов не гарантируется. Идеально для скриптов, которые не зависят от DOM и друг от друга (счётчики, реклама).
Типичные проблемы и их решения:
- Проблема: Скрипт обращается к DOM-элементам, которые ещё не созданы. Решение: проверять существование элементов или использовать обработчик
DOMContentLoaded. - Проблема: Если несколько
async-скриптов зависят друг от друга, порядок выполнения может нарушиться. Решение: использоватьdeferили объединить скрипты в один.
Вариант: атрибут type='module'
Вопрос: Как подключить модульный JavaScript с автоматическим отложенным выполнением и поддержкой импорта/экспорта?
Ответ: указать type='module'. Пример:
<script type='module' src='main.mjs'></script>
Пояснение: скрипты с type='module' автоматически ведут себя как defer (выполняются после парсинга). Они работают в строгом режиме, поддерживают import и export. Модули могут быть встроенными или внешними. Для внешних модулей требуется соблюдение CORS.
Типичные проблемы и их решения:
- Проблема: Модули не работают в старых браузерах. Решение: использовать атрибут
nomoduleдля запасного варианта. - Проблема: Ошибка CORS при загрузке модуля с другого домена. Решение: настроить сервер для отправки корректных заголовков CORS.
- Проблема: Встроенные модули (inline) не кэшируются браузером. Решение: для повторного использования выносить модуль во внешний файл.
Примеры расширенного использования тега script
// Создаём элемент script
var script = document.createElement('script');
script.src = 'lazy.js';
script.async = true;
document.head.appendChild(script);
Результат: скрипт lazy.js загружается и выполняется асинхронно. Этот метод позволяет загружать скрипты после каких-либо действий пользователя (например, после клика).
(в консоли браузера появится вывод из lazy.js, если он есть)
<script src='https://cdn.example.com/lib.js'
integrity='sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC'
crossorigin='anonymous'></script>
Пояснение: атрибут integrity содержит криптографический хеш (SHA-384) содержимого файла. Браузер вычисляет хеш загруженного файла и сравнивает с указанным. Если хеши не совпадают, скрипт не выполняется. Это защита от подмены файла при использовании CDN.
Скрипт выполнится только если содержимое совпадает с хешем. При несовпадении – ошибка в консоли.
<script type='module' src='modern.js'></script>
<script nomodule src='legacy.js'></script>
Пояснение: браузеры, поддерживающие модули, загрузят modern.js и проигнорируют скрипт с nomodule. Старые браузеры, не понимающие type='module', загрузят legacy.js. Это позволяет поддерживать старые версии без дублирования кода.
В современных браузерах выполнится modern.js, в старых – legacy.js.
<script defer src='first.js'></script>
<script defer src='second.js'></script>
<script defer src='third.js'></script>
Пояснение: все три скрипта загружаются параллельно, но выполняются строго в порядке появления: first.js, затем second.js, затем third.js – после полного парсинга HTML. Это важно, если второй скрипт зависит от первого.
В консоль выводятся сообщения из каждого файла в порядке first, second, third.
<script>
document.write('<p>Этот текст будет вставлен</p>');
</script>
Пояснение: document.write может использоваться только во время парсинга. Если вызвать его после загрузки страницы (например, в async или defer скрипте), он перезапишет весь документ. Поэтому не рекомендуется использовать document.write в современных скриптах.
При выполнении после парсинга – страница очищается и отображается только вставленный текст.
<script src='https://api.example.com/widget.js' crossorigin='anonymous'></script>
Пояснение: при загрузке скрипта с другого домена браузер отправляет запрос с заголовком Origin. Атрибут crossorigin указывает, следует ли передавать учётные данные (cookies, сертификаты). Значение anonymous означает запрос без учётных данных. Необходим для модулей (type='module') и для получения ошибок выполнения (если сервер настроен на CORS).
Скрипт загрузится, если сервер разрешает CORS; иначе – ошибка.
<script type='module'>
import { sayHello } from './hello.js';
sayHello('Мир');
</script>
hello.js:
export function sayHello(name) {
console.log(`Привет, ${name}!`);
}
В консоль выводится 'Привет, Мир!'
<script type='module' src='./utils/helpers.mjs'></script>
Пояснение: расширение .mjs или .js – не важно, главное, чтобы сервер возвращал правильный MIME-тип (text/javascript или application/javascript). Модули должны быть доступны по тому же протоколу (HTTP/HTTPS).