【问题标题】:d3.js and document.onReadyd3.js 和 document.onReady
【发布时间】:2011-08-24 01:08:49
【问题描述】:

我刚刚开始使用d3.js,有一个细节让我完全无法理解:如何让我的代码仅在 DOM 准备好接收输入后执行?

当然,我可以使用 jQuery 之类的东西,但这似乎太过分了。

every d3.js example 我遇到过似乎没有特殊的document.onReady() 类型的例程,但所有示例都完美无缺。然而,在我测试代码时,如果在 DOM 准备好之前执行代码会完全失败(将我的代码放入 window.onload 即可确认这一点)。

什么给了?

【问题讨论】:

    标签: javascript d3.js


    【解决方案1】:

    您会在他们的示例中注意到,他们的 javascript 位于所使用的任何 html 元素下方,因此 dom 的一部分在开始执行 javascript 之前已加载。

    只需将您的 javascript 放在正文的底部通常就足够了。

    【讨论】:

    • 太棒了,谢谢。显然,我在 jQuery 领域待得太久了。
    • 这是不好的做法,为什么 jQuery 及其竞争对手中有这么多现成的变体。
    • ilw 是对的。在某些移动 WebViews 上,它有时会在打开应用程序时报告错误的屏幕宽度大小。等待准备好的文件可以避免这种情况。
    【解决方案2】:

    有时您不能依赖 DIV / HTML 元素放置,例如当您需要将使用 D3 操作的元素动态插入到文档中时。在这种情况下,一种解决方案是监视文档中的 DOMNodeInserted 事件并在回调中插入 D3 代码(我相信这排除了 IE 9 之前的版本)。这是一个使用 jQuery 的示例:

    $(document).bind('DOMNodeInserted', function(event)
    {
        if (event.target.id == "viz")
        {
            var sampleSVG = d3.select("#viz")
                     .append("svg:svg")
                     .attr("width", 100)
                     .attr("height", 100);    
    
            sampleSVG.append("svg:circle")
                     .style("stroke", "gray")
                     .style("fill", "white")
                     .attr("r", 40)
                     .attr("cx", 50)
                     .attr("cy", 50)
                     .on("mouseover", function() {
                          d3.select(this).style("fill", "aliceblue");
                     })
                     .on("mouseout", function() {
                          d3.select(this).style("fill", "white");}
                     );
        }
    });
    

    【讨论】:

    • 好答案。动态加载 SVG 到 DOM 时,有没有办法触发 SVG 节点插入事件?如果是这样,当创建不相关的 DOM 元素(如 div、p 等)时,它将减少 SVG 对象的重新渲染。
    • 伟大的技术,鲜为人知!
    • 不幸的是,诸如“DOMNodeInserted”之类的突变事件现在已被弃用 - developer.mozilla.org/en-US/docs/Web/Guide/Events/…。我希望我能告诉你最好的选择是什么,但我不能(这就是我在发现这个问题时试图发现的!)
    【解决方案3】:

    标记为正确的答案对我不起作用,实际上是错误的。这是某种黑客行为,不应被视为正确答案。同样的方式你可以在 setTimeout(function() { .. }, 1000) 中执行你的代码。它更加可靠,因为您可以设置延迟:-/

    在我的情况下,我需要等待所有元素都被处理才能知道它们的实际尺寸。当它们没有被构建、处理和完成时,数字是不正确的。

    已更新。这是正确答案:

    您很可能使用诸如 d3.json() 之类的异步调用来获取构建 DOM 的数据,这就是它需要一些时间的原因。由于异步调用是非阻塞的,因此即使在异步调用完成之前,您的后续代码也会被调用,从而导致问题,这就是您发布此问题的原因。

    因此,您正试图通过在 D3 或其他任何地方查找某些内容来解决此问题,这会告诉您 D3 异步调用已完成,现在您可以执行下一件事了。有人建议进行同步 ajax 调用。这是非常错误的。异步调用被创建为异步的。

    你真正需要做的是改变你的范式。只需将回调传递给该异步调用即可!类似的东西:

    function thingsToDoWhenDOMisBuilt() {
        ...
    }
    
    function buildDOM(rootNode, error, thingsToDoWhenDOMisBuilt) {
        ...
        // everything is built, so we can do our post-build thing against DOM
        if (thingsToDoWhenDOMisBuilt)
            thingsToDoWhenDOMisBuilt()
    }
    
    d3.json(urlToData, buildDOM) {
        if (error)
            console.log("oops")
        buildDOM(rootNode, thingsToDoWhenDOMisBuilt)
    }
    

    另外,看看async.js

    上面建议的绑定事件也是一个可怕的想法。您应该改用 .enter() .exit() 。 D3是数据驱动的,如果你需要事件驱动的流程,那就用jQuery或者vanilla JS吧!

    【讨论】:

      【解决方案4】:

      您可以在 body 中放置一个 onload 事件,并将所有 d3js 代码放在一个函数中。例如:

      <body onload="yourFunctionName()">
      

      在你的 javascript 中,插入这个:

      function yourFunctionName() {
          //Your d3js code goes here
      }
      

      只需将完整的 d3 示例代码粘贴到此函数中即可。 onload 事件将在 DOM 准备好后发生。

      【讨论】:

      • 出于测试目的,这很好,但请注意设置body.onload 通常被认为是错误的形式,因为其他脚本可以覆盖它。 addEventListener 更好,但与document.onReady 相比仍然有点慢。