如何推迟内联Javascript?

时间:2016-12-30 10:50:29

标签: javascript jquery html deferred-execution

我有以下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。现在,即使我从我的内联JS代码中删除defer,它也说jQuery是未定义的。出于某种原因,我必须将jQuery插件保留在头部并保持我的JS代码内联。我的问题是:

  1. 为什么当defer属性存在时,内联Javascript代码是否会被延迟?

  2. 有没有办法模仿我的内联Javascript代码的延迟行为?如果需要,我可以将它放在body标签的末尾。

7 个答案:

答案 0 :(得分:71)

defer属性的脚本按照指定的顺序加载,但不在文档本身之前加载。由于defer标记对script标记没有影响,除非它们也具有src属性,因此执行的第一个脚本是您的内联脚本。所以当时jQuery还没有加载。

您可以通过以下两种方式解决此问题:

  • 将您的内联脚本放在.js文件中,并使用src属性引用它(除了您已经拥有的defer属性),或

  • 让您的内联脚本等待文档和加载的延迟脚本。发生这种情况时,DOMContentLoaded事件将会触发:

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

注意:请注意,在后一种情况下$(document).ready(function()不再包括在内,因为它会等待同一事件(DOMContentLoaded)。您可以仍然像原始代码一样包含它,但是jQuery只会立即执行回调 ,这没有实际意义。

答案 1 :(得分:30)

您可以从脚本中创建Base64 URL并将其放入src!

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

我建立了一个快速测试,看看它在行动。 如果Hello world!正在运行,您应该会看到defer的提醒:

&#13;
&#13;
<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>
&#13;
&#13;
&#13;

手动操作有点费力,所以如果您能以某种方式(Handlebars,Angular等)编译HTML,那么这将有很大的帮助。

我目前正在使用:

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

答案 2 :(得分:5)

来自MDN docs:

  

<强>延迟
  此布尔属性设置为向浏览器指示在解析文档之后但在触发DOMContentLoaded之前要执行脚本。 defer属性只能用于外部脚本。

这称为IIFE (立即调用的函数表达式),它在DOM可用之前执行。因此,在这种情况下jQuery未定义,因为它不在DOM中。

答案 3 :(得分:1)

延迟/异步脚本标记不够好

有一个常识,您应该使用<script src=".." async defer>(或从JS进行操作时在分配script.async = true之前设置src)和/或将脚本放在最底端页面上的内容,以便尽可能快地将尽可能多的页面加载并呈现给用户。

defer.js(请注意:我是该脚本的作者)是用纯JavaScript编写的,从而使延迟加载其他内容更加快捷和高效。您可以有效地延迟任何javascript文件以及内联脚本块。

Defer loading of JavaScript

如果您的页面只是经过JavaScript增强的HTML页面,那么仅使用<script async>就可以了。浏览器需要花费一些时间来解析和执行这些脚本,并且每次UI更改都可能重新排列您的布局,使您的加载速度更加缓慢,没有人喜欢盯着空白的白纸;用户没有耐心,会很快离开。

在各种情况下,使用asyncdefer不能提供比defer.js更快的页面速度。

答案 4 :(得分:1)

您也可以使用type="module"

<meta charset="utf-8">

<script type="module">
let t = document.getElementById('top');
console.log(t);
</script>

<h1 id="top">Top Questions</h1>

https://developer.mozilla.org/docs/Web/HTML/Element/script#attr-type

答案 5 :(得分:0)

使用纯文本数据URI推迟加载-Chrome和FF

#noLib #vanillaJS

建议不要在跨浏览器产品中使用

直到MS IE死亡并且MS Edge将采用Chromium开源;)

推迟脚本的唯一方法是外部文件 Data_URI (不使用事件DOMContentLoaded)

推迟

spec script#attr-defer (MDN web docs):“如果不存在src属性(例如,对于内联脚本),则不得使用此属性,在这种情况下,它将无效。)”

Data_URI

规范Data_URI

使用正确的类型“文本/ javascript” ,根本不需要 base64 ...;)

使用纯文本,因此您可以使用简单的内容:

<script defer src="data:text/javascript,

//do something with b-lazy plugin, lightbox plugin and then with flexslider

lightbox.option({
  resizeDuration: 200,
  wrapAround: true
})

">

是的,这有点奇怪,但是默认情况下会延迟<script type="module">,没有其他选择以完全顺序混合以下内容

  • 模块外部文件-默认延迟
  • 模块内联脚本-默认情况下推迟
  • 外部文件-(可选)已推迟
  • 内联脚本-仅包含此 hack -据我所知(无库/框架)

答案 6 :(得分:0)

我检查了所有建议的解决方案,但都有其缺点。所以我自己发明了。

将此内联脚本放入您的 head 标记或 body 标记的开头之后:

<script>var Defer = []; document.addEventListener('DOMContentLoaded', function() { while (Defer.length) Defer.shift().call(); }); </script>

这个 liner 将收集所有您想要延迟的内联脚本,并在文档完全加载后立即分别运行它们。现在,只要您需要延迟运行内联脚本,只需像这样注册它:

<script>
  alert('This alert will show immediately.');

  Defer.push(function() {
   alert('This alert will show only after document is loaded.');
   // You can use anything which is not loaded yet, like jQuery
   $(".selector").doSomeJqueryStuff();
  });

  // You can use it as many times as you like and in any place of your DOM.
  Defer.push(function() {
    // Any inline code you want to defer
  });
</script>

此内联脚本仅在文档加载后运行。这意味着您可以运行内联 jQuery 脚本,让您的 jQuery 停留在 DOM 的末尾。