【问题标题】:Code inside DOMContentLoaded event not workingDOMContentLoaded 事件中的代码不起作用
【发布时间】:2017-02-20 23:09:15
【问题描述】:

我用过

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
</head>
<body>
  <button type="button" id="button">Click</button>
  <pre id="output">Not Loading...</pre>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.17.0/babel.min.js"></script>
  <script type="text/babel">
    document.addEventListener('DOMContentLoaded', function () {
      const button = document.getElementById('button');
      const output = document.getElementById('output');

      output.textContent = 'Loading...';

      addEventListener('click', function () {
        output.textContent = 'Done';
      });
     });
  </script>
</body>
</html>

document.addEventListener('DOMContentLoaded', function () {}); 中的代码似乎没有加载。

如果我从我的代码中删除它,它会突然起作用。

我已经发了JS Bin here

【问题讨论】:

  • FWIW,根本不需要使用DOMContentLoaded。您的脚本已经在文档的最后,这已经足够好了。
  • 为什么要在脚本中添加text/babel 类型?
  • 这只是一个例子。在我的其他脚本中,我使用的是箭头函数,所以我需要使用 babel 将其从 ES6 转换为 ES5
  • 但是当我删除它时,代码确实有效。
  • @Mohammad:OP 正在使用 Babel Standalone 来使用 Babel 的转译 in 浏览器。这就是你告诉 Babel Standalone 应该转换哪些脚本的方式。

标签: javascript babeljs addeventlistener domcontentloaded


【解决方案1】:

这很可能是因为此时DOMContentLoaded 事件已被触发。一般来说,最佳做法是检查 document.readyState 以确定您是否需要监听该事件。

if( document.readyState !== 'loading' ) {
    console.log( 'document is already ready, just execute code here' );
    myInitCode();
} else {
    document.addEventListener('DOMContentLoaded', function () {
        console.log( 'document was not ready, place code here' );
        myInitCode();
    });
}

function myInitCode() {}

【讨论】:

  • 不错的一个 - OP 询问我如何处理它是否已经运行以激励我添加它,然后我看到你已经...
  • 哈,是的,我想这几乎是当今香草 JS 的标准方式。
  • 好吧,我认为标准的做法是确保script 在正文的末尾,而不是对DOMContentLoaded 大惊小怪。只有在作者无法控制脚本标记的位置时才会使用需要此功能的代码(例如,有趣的是,Babel Standalone)。
  • 不要检查document.readyState === 'complete'!检查document.readyState !== 'loading',因为如果document.readyState 是“交互式的”,那么监听DOMContentLoaded 是没有用的。测试您的错误并修复代码。
  • @Ruslan 你能说出它为什么没用吗?您的意思是如果状态等于“交互式”,它也不会触发?
【解决方案2】:

在代码挂钩时,事件已经触发了。 Babel standalone 的工作方式是通过查找并执行页面上的所有 type="text/babel" 脚本来响应 DOMContentLoaded。你可以在index.js file看到这个:

// Listen for load event if we're in a browser and then kick off finding and
// running of scripts with "text/babel" type.
const transformScriptTags = () => runScripts(transform);
if (typeof window !== 'undefined' && window && window.addEventListener) {
  window.addEventListener('DOMContentLoaded', transformScriptTags, false);
}

直接运行代码,无需等待事件,因为你知道 Babel Standalone 会为你等待。

还要注意,如果你把你的脚本放在正文的末尾,就在结束 &lt;/body&gt; 标记之前,即使你不使用 Babel,也不需要等待 DOMContentLoaded。脚本上面定义的所有元素都将存在并可供您的脚本使用。


在您提出的评论中:

但是我在开发中使用的是独立的 Babel,但是当我投入生产时我会预编译它。我应该在投入生产时重新添加它吗?

如上所述,只需确保您的script 标记位于body 的末尾即可,无需使用该事件。

如果无论如何使用它对你来说很重要,你可以通过检查document.readyState来查看事件是否已经运行(点击链接后,向上滚动一点):

function onReady() {
    // ...your code here...
}
if (document.readyState !== "loading") {
    onReady(); // Or setTimeout(onReady, 0); if you want it consistently async
} else {
    document.addEventListener("DOMContentLoaded", onReady);
}

document.readyState 经历了这些阶段(从上面的链接稍微向上滚动):

在文档加载时返回"loading",在完成解析但仍在加载子资源后返回"interactive",在加载后返回"complete"

【讨论】:

  • 但是我在开发中使用的是独立的Babel,但是我会在生产时预编译它。我应该在投入生产时重新添加它吗?
  • @Jamgreen:我已经在上面的答案中添加了对该评论的回复。
  • @Ruslan:谢谢。 我被规范误导了 “每个文档都有当前文档就绪状态。创建 Document 对象时,它必须具有如果文档与 HTML 解析器、XML 解析器或 XSLT 处理器相关联,则其当前文档就绪设置为字符串“loading”,否则设置为字符串“complete”。页面加载期间的各种算法会影响此值。当值已设置,用户代理必须在 Document 对象上触发一个名为 readystatechange 的简单事件。” 但就在上面,很明显有一个中间"interactive" 状态! :-)
【解决方案3】:

感谢 Ruslan,这里是完整的代码 sn-p,使用后可以方便地分离 DOMContentLoaded 处理程序。

'use strict';
var dclhandler = false;
if (document.readyState !== 'loading') {
    start();
} else {
    dclhandler = true;
    document.addEventListener('DOMContentLoaded', start);
}
function start() {
    if (dclhandler) { document.removeEventListener('DOMContentLoaded', start); }
    console.log('Start the site`s JS activities');
}

【讨论】:

    【解决方案4】:

    另一种选择是使用readystatechange 事件。 readystatechange 事件在documentreadyState 属性发生更改时触发。 readyState 属性可以是以下三个值之一:'loading''interactive''complete'。使用DOMContentLoaded 事件的另一种方法是在documentreadystatechange 事件中查找readyState 等于'interactive',如下面的sn-p。

    document.onreadystatechange = function () {
      if (document.readyState === 'interactive') {
        // Execute code here
      }
    }
    

    尽管在您的情况下,documentreadyState 似乎已经达到'complete'。在这种情况下,您可以在上面的 sn-p 中简单地将 'interactive' 替换为 'complete'。这在技术上等同于load 事件,而不是DOMContentLoaded 事件。

    阅读更多关于 MDN, Document.readyState Document: readystatechange event

    【讨论】:

    • 我只是遇到了 DOMContentLoaded 无法正常工作的问题,因此根据您的回答和 readystate = complete 检查它工作正常 - 非常感谢!
    【解决方案5】:

    当我在 Cloudflare 中同时启用 HTML Auto Minify 和 Rocket Loader 时,我也遇到了同样的问题。结论是,当这些功能同时处理时,DOMContentLoaded 事件丢失了。

    您可以在 HTML 文件脚本块中的所有 DOMContentLoaded 事件侦听器之前添加以下代码来解决此问题。

    var inCloudFlare = true;
    window.addEventListener("DOMContentLoaded", function () {
        inCloudFlare = false;
    });
    if (document.readyState === "loading") {
        window.addEventListener("load", function () {
            if (inCloudFlare) window.dispatchEvent(new Event("DOMContentLoaded"));
        });
    }
    

    解释请到我的博客。

    https://hollowmansblog.wordpress.com/2021/10/18/solution-to-missing-domcontentloaded-event-when-enabling-both-html-auto-minify-and-rocket-loader-in-cloudflare/

    【讨论】:

      【解决方案6】:

      我会使用document.addEventListener("DOMContentLoaded", () =&gt; {/*yourcode*/});

      【讨论】:

        【解决方案7】:

        https://learnwithparam.com/blog/vanilla-js-equivalent-of-jquery-ready/

        function ready(callbackFunc) {
          if (document.readyState !== 'loading') {
            // Document is already ready, call the callback directly
            callbackFunc();
          } else if (document.addEventListener) {
            // All modern browsers to register DOMContentLoaded
            document.addEventListener('DOMContentLoaded', callbackFunc);
          } else {
            // Old IE browsers
            document.attachEvent('onreadystatechange', function() {
              if (document.readyState === 'complete') {
                callbackFunc();
              }
            });
          }
        }
        
        ready(function() {
          // your code here
        });

        【讨论】:

        • 截至本文发布之时 - Run code snippet 为空
        【解决方案8】:

        我的干净方法...

        if (document.readyState !== 'loading') init()
        else document.addEventListener('DOMContentLoaded', init);
        
        function init() {
            console.log("Do it !");
            ...
        }
        

        【讨论】:

          【解决方案9】:

          我的实时服务器遇到了这个问题。 我的实时服务器在 cloudflare 上运行,带有一些缓存和火箭加载 js。我认为这是导致意外行为的原因。 使用 DOMContentLoaded 时,该代码在本地服务器上运行良好,但在实时运行时却不行。

          如果你可以使用jQuery,切换到

          jQuery(document).ready(function() {
          //code...
          })
          

          按预期工作。

          【讨论】:

            猜你喜欢
            • 2022-01-09
            • 1970-01-01
            • 2013-04-28
            • 1970-01-01
            • 1970-01-01
            • 2023-04-10
            • 1970-01-01
            • 1970-01-01
            • 2012-11-11
            相关资源
            最近更新 更多