2011-08-24 30 views
30

Ho appena iniziato con d3.js e c'è un dettaglio che mi sfugge completamente: come faccio a eseguire il mio codice solo dopo che il DOM è pronto per ricevere input?d3.js e document.onReady

Potrei, ovviamente, usare qualcosa come jQuery ma sembra eccessivo.

In every d3.js example Ho riscontrato che non sembra esserci un tipo speciale di routine document.onReady(), ma tutti gli esempi funzionano perfettamente. Tuttavia, quando eseguo il test del codice sul mio terminale, il codice fallisce completamente se viene eseguito prima che il DOM sia pronto (il lancio del mio codice in un window.onload lo conferma).

Cosa dà?

risposta

46

Noterete nei loro esempi che il loro javascript è al di sotto di qualsiasi elemento html utilizzato in modo tale che parte della dom venga caricata prima che inizi l'esecuzione del javascript.

Inserire semplicemente il javascript nella parte inferiore del corpo è in genere sufficiente.

+7

Brillante, grazie. Chiaramente, sono stato in terra jQuery per troppo tempo. – Roshambo

+2

Questa è una cattiva pratica, e perché ci sono così tante varianti già presenti in jQuery e nei suoi concorrenti. – ijw

+0

ilw ha ragione. Su alcune WebViews su dispositivi mobili, quando si apre un'app viene segnalato a volte dimensioni della larghezza dello schermo errate. Aspettare il documento pronto lo evita. – phreakhead

5

A volte non è possibile fare affidamento sulla collocazione di elementi DIV/HTML, ad esempio quando è necessario inserire l'elemento manipolato con D3 in modo dinamico nel documento. In tali situazioni una soluzione è monitorare il documento per eventi DOMNodeInserted e inserire il codice D3 in un callback (credo che questo escluda comunque versioni di IE precedenti alla 9). Ecco un esempio 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

Buona risposta. Quando si carica in modo dinamico SVG nel DOM, esiste un modo per attivare un evento di inserimento del nodo SVG? In tal caso, ridurrebbe il re-rendering degli oggetti SVG quando vengono creati elementi DOM non collegati come div, p, ecc. –

+0

Grande tecnica, poco conosciuta! – VividD

+1

Purtroppo gli eventi di mutazione come "DOMNodeInserted" sono ora deprecati - https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Mutation_events. Vorrei poterti dire quale sia l'alternativa migliore ma non posso (è quello che stavo cercando di scoprire quando ho trovato questa domanda!) – Willl

2

È possibile inserire un evento onload sul corpo e inserire tutto il codice d3js in una funzione. Per esempio:

<body onload="yourFunctionName()"> 

E nel tuo javascript, inserire questo:

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

Basta incollare il codice di esempio d3 completo all'interno di questa funzione. L'evento onload avverrà dopo che il DOM è pronto.

+0

A fini di test va bene, ma tieni presente che l'impostazione 'body.onload 'è generalmente considerata cattiva forma come altri script possono sovrascriverlo. 'addEventListener' è migliore, ma ancora un po 'lento rispetto a' document.onReady'. – Roshambo

3

La risposta contrassegnata come corretta non ha funzionato per me e in realtà è sbagliata. È una specie di trucco e non dovrebbe essere considerato una risposta corretta. Allo stesso modo puoi eseguire il tuo codice all'interno di setTimeout (function() {..}, 1000). È ancora più affidabile in quanto è possibile impostare il ritardo: -/

Nel mio caso ho dovuto aspettare che tutti gli elementi siano elaborati per conoscere le loro dimensioni reali. Quando non sono costruiti, elaborati e completati, i numeri non sono corretti.

AGGIORNATO. Ecco la risposta corretta:

Molto probabilmente si ottengono i dati per la creazione di DOM utilizzando una chiamata asincrona come d3.json() ed è per questo che ci vuole del tempo. Poiché la chiamata asincrona è non bloccante, il codice successivo viene richiamato anche prima della fine della chiamata asincrona, causando problemi e questo è il motivo per cui hai postato questa domanda.

Quindi stai provando a risolvere questo cercando qualcosa in D3 o in qualsiasi altro luogo, questo ti dirà che la chiamata asincrona D3 è finita e ora puoi fare la tua prossima cosa. Alcune persone suggeriscono di effettuare una chiamata ajax di sincronizzazione. Questo è terribilmente sbagliato. Le chiamate asincrone sono create per essere asincrone.

Quello che devi fare è cambiare il tuo paradigma. Basta passare una richiamata a quella chiamata asincrona e il gioco è fatto!Qualcosa del genere:

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

Inoltre, dare un'occhiata a async.js

eventi di legame come suggerito sopra è anche un'idea orribile. Dovresti invece usare .enter() .exit(). D3 è basato sui dati, se hai bisogno di flusso basato sugli eventi, quindi usa solo jQuery o vanilla JS!