Как отложить встроенный Javascript?

У меня есть следующий код html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/blazy/1.8.2/blazy.min.js" defer></script>
    <script src="https://code.jquery.com/jquery-2.1.4.min.js" integrity="sha256-8WqyJLuWKRBVhxXIL1jBDD7SDxU936oZkCnxQbWwJVw=" crossorigin="anonymous" defer></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.9.0/js/lightbox.min.js" defer></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous" defer></script>
    <!-- 26 dec flexslider js -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/flexslider/2.6.3/jquery.flexslider.min.js" defer></script>
    <script defer>
    (function($) {
        $(document).ready(function() {
            //do something with b-lazy plugin, lightbox plugin and then with flexslider
        });
    })(jQuery);
    </script>
</head>
<body>
</body>
</html>

Я получаю сообщение об ошибке, говоря, что jQuery не определен. Теперь, даже если я удалю defer из моего встроенного JS-кода, он говорит, что jQuery - это undefined. По какой-то причине я должен держать плагины jQuery в голове и держать свой JS-код в строке. Мой вопрос:

  • Почему встроенный Javascript-код не откладывается, если на нем присутствует атрибут defer?

  • Есть ли способ подражать отложенному поведению моего встроенного кода JavaScript? Я могу поместить это в конец тега тела, если потребуется.

Ответ 1

Загрузка скриптов в том порядке, в котором они указаны, но поскольку все они имеют атрибут defer, все они выполняются после того, как сам документ был загружен. Поскольку defer не влияет на теги script, если они также не имеют тег src, первый script, который выполняется, является вашим встроенным script. Так что в то время jQuery еще не загружен.

Вы можете решить это, по крайней мере, двумя способами:

  • Поместите свой встроенный script в файл .js и укажите его с атрибутом src или

  • Дождитесь загрузки документа и отложенных сценариев следующим образом:

    <script>
        window.addEventListener('DOMContentLoaded', function() {
            (function($) {
                $(document).ready(function() {
                    //do something with b-lazy plugin, lightbox plugin and then with flexslider
                });
            })(jQuery);
        });
    </script>
    

NB: в последнем случае $(document).ready(function() действительно не нужно, так как в то время документ уже готов, но jQuery хорош и немедленно выполнит обратный вызов.

Ответ 2

Из документов MDN:

Перенести
Этот атрибут Boolean установлен для указания браузеру, что script предназначен для выполнения после того, как документ был проанализирован, но перед запуском DOMContentLoaded. Атрибут defer должен использоваться только для внешних скриптов.

Это называется IIFE (Expression Exited Function Expression), которое выполняется до того, как DOM доступен. Итак, в этом случае jQuery есть undefined, потому что это не в DOM.

Ответ 3

Вы можете создать URL-адрес Base64 из script и поместить его в src!

<script src="data:text/javascript;base64,YWxlcnQoJ0hlbGxvIHdvcmxkIScpOw=="
        defer>
</script>

Я построил быстрый тест, чтобы увидеть его в действии. Вы должны увидеть предупреждение с Hello world! последним, если defer работает:

<script defer>
  alert('Why no defer?!?');
</script>

<!-- alert('Hello world!'); -->
<script src="data:text/javascript;base64,YWxlcnQoJ0hlbGxvIHdvcmxkIScpOw=="
        defer></script>

<script>
  alert('Buh-bye world!');
</script>