2011-08-24 25 views
30

Estoy empezando con d3.js y hay un detalle que me está eludiendo completamente: ¿cómo puedo ejecutar mi código solo después de que el DOM esté listo para recibir la entrada?d3.js y document.onReady

Podría, por supuesto, usar algo como jQuery pero eso parece excesivo.

En every d3.js example Me he encontrado que no parece haber ningún tipo de rutina document.onReady() especial, sin embargo, todos los ejemplos funcionan sin problemas. Sin embargo, al probar el código en mi extremo, el código falla totalmente si se ejecuta antes de que el DOM esté listo (arrojar mi código en un window.onload lo confirma).

¿Qué ofrece?

Respuesta

46

Notarás en sus ejemplos que su javascript está debajo de cualquiera de los elementos html que se utilizan para que parte del dom se cargue antes de que comience a ejecutar javascript.

Simplemente poniendo su javascript en la parte inferior del cuerpo suele ser lo suficientemente bueno.

+7

Brillante, gracias. Claramente, he estado en una tierra jQuery demasiado tiempo. – Roshambo

+2

Esta es una mala práctica, y por qué hay tantas variantes en curso en jQuery y sus competidores. – ijw

+0

ilw tiene razón. En ciertos WebViews móviles, a veces informará los tamaños de ancho de pantalla incorrectos al abrir una aplicación. Esperando el documento en listo evita eso. – phreakhead

5

A veces no puede confiar en la colocación del elemento DIV/HTML, por ejemplo cuando necesita insertar dinámicamente el elemento manipulado con D3 en el documento. En tales situaciones, una solución es monitorear el documento para eventos DOMNodeInserted e insertar el código D3 en una devolución de llamada (creo que esto excluye las versiones de IE anteriores al 9). He aquí un ejemplo con 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");} 
       ); 
    } 
}); 
+1

Buena respuesta. Al cargar dinámicamente SVG en el DOM, ¿hay alguna forma de desencadenar un evento de inserción del nodo SVG? Si es así, reduciría la re-representación de objetos SVG cuando se crean elementos DOM no relacionados tales como divs, p's, etc. –

+0

¡Gran técnica, poco conocida! – VividD

+1

Lamentablemente, los eventos de mutación como 'DOMNodeInserted' ahora están en desuso - https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Mutation_events. Desearía poder decirte cuál es la mejor alternativa, pero no puedo (¡eso es lo que estaba tratando de descubrir cuando encontré esta pregunta!) – Willl

2

Se puede poner un proceso de carga en el cuerpo y poner todo d3js código en una función. Por ejemplo:

<body onload="yourFunctionName()"> 

Y en su Javascript, inserte esto:

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

basta con pegar el código completo ejemplo d3 dentro de esta función. El evento de carga ocurrirá después de que el DOM esté listo.

+0

Para fines de prueba, está bien, pero ten en cuenta que la configuración 'body.onload' generalmente se considera mala forma ya que otros scripts pueden anularla. 'addEventListener' es mejor, pero aún un poco lento en comparación con' document.onReady'. – Roshambo

3

La respuesta marcada como correcta no funcionó, y en realidad es incorrecta. Es una especie de truco y no debe considerarse una respuesta correcta. De la misma forma en que podría ejecutar su código dentro de setTimeout (function() {..}, 1000). Es aún más confiable ya que puede configurar la demora: -/

En mi caso, tuve que esperar a que se procesaran todos los elementos para conocer sus dimensiones reales. Cuando no están construidos, procesados ​​y hechos, los números no eran correctos.

ACTUALIZADO. Aquí está la respuesta correcta:

Lo más probable es que obtenga sus datos para construir DOM utilizando alguna llamada asincrónica como d3.json() y es por eso que lleva algún tiempo. Como la llamada asincrónica no es bloqueante, su código subsiguiente se invoca incluso antes de que finalice la llamada asincrónica, lo que causa problemas, y esta es la razón por la que ha enviado esta pregunta.

Así que está tratando de resolver esto buscando algo en D3 o en cualquier otro lugar, eso le indicará que la llamada asincrónica D3 ha finalizado y que ahora puede hacer lo siguiente. Algunas personas sugieren hacer una llamada ajax de sincronización. Esto es terriblemente INCORRECTO Las llamadas asincrónicas se crean para ser asincrónicas.

Lo que realmente necesita hacer es cambiar su paradigma. ¡Simplemente pase una devolución de llamada a esa llamada asíncrona y listo!Algo así:

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) 
} 

También, echar un vistazo a async.js

sucesos de unión como se sugirió anteriormente es también una idea horrible. Debería usar .enter() .exit() en su lugar. D3 es impulsado por datos, si necesita flujo controlado por eventos, entonces simplemente use jQuery o JS vainilla.