2011-08-24 9 views
30

Tôi chỉ mới bắt đầu với d3.js và có một chi tiết hoàn toàn bỏ qua tôi: làm cách nào để mã của tôi chỉ thực thi sau khi DOM sẵn sàng nhận dữ liệu nhập?d3.js và document.onReady

Tôi có thể, tất nhiên, sử dụng một cái gì đó như jQuery nhưng điều đó có vẻ quá mức.

Trong every d3.js example Tôi đã gặp phải dường như không có loại đặc biệt nào là document.onReady(), nhưng tất cả các ví dụ đều hoạt động hoàn hảo. Tuy nhiên, khi thử nghiệm mã trên đầu của tôi, mã hoàn toàn thất bại nếu được thực hiện trước khi DOM sẵn sàng (ném mã của tôi vào một số window.onload xác nhận điều này).

Điều gì mang lại?

Trả lời

46

Bạn sẽ nhận thấy trong ví dụ của họ rằng javascript của họ ở bên dưới bất kỳ phần tử html nào được sử dụng sao cho một phần của dom được tải trước khi bắt đầu thực thi javascript.

Chỉ cần đặt javascript của bạn ở dưới cùng của cơ thể thường là đủ tốt.

+7

Rực rỡ, cảm ơn bạn. Rõ ràng, tôi đã ở vùng đất jQuery quá lâu rồi. – Roshambo

+2

Đây là thực hành không tốt, và tại sao có rất nhiều biến thể onready trong jQuery và các đối thủ cạnh tranh của nó. – ijw

+0

ilw là đúng. Trên một số WebView trên thiết bị di động nhất định, nó sẽ báo cáo kích thước chiều rộng màn hình không chính xác đôi khi khi mở một ứng dụng. Đang chờ tài liệu sẵn sàng tránh điều đó. – phreakhead

5

Đôi khi bạn không thể dựa vào việc đặt phần tử DIV/HTML, ví dụ khi bạn cần chèn phần tử được thao tác với D3 động vào tài liệu. Trong những tình huống như vậy, một giải pháp là giám sát tài liệu cho các sự kiện DOMNodeInserted và chèn mã D3 vào trong một cuộc gọi lại (tôi tin rằng các quy tắc này trong các phiên bản IE trước 9 mặc dù). Dưới đây là ví dụ về 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

Câu trả lời hay. Khi tự động tải SVG vào DOM, có cách nào để kích hoạt sự kiện chèn nút SVG không? Nếu vậy, nó sẽ làm giảm việc tái render các đối tượng SVG khi các phần tử DOM không liên quan như divs, p's, vv được tạo ra. –

+0

Kỹ thuật tuyệt vời, ít được biết đến! – VividD

+1

Rất tiếc, các sự kiện đột biến như 'DOMNodeInserted' hiện không còn được dùng nữa - https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Mutation_events. Tôi muốn tôi có thể cho bạn biết lựa chọn tốt nhất là gì nhưng tôi không thể (đó là những gì tôi đang cố khám phá khi tôi tìm thấy câu hỏi này!) – Willl

2

Bạn có thể đặt sự kiện onload vào cơ thể và đặt tất cả mã d3js vào một hàm. Ví dụ:

<body onload="yourFunctionName()"> 

Và trong javascript của bạn, chèn này:

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

Chỉ cần dán đầy đủ d3 mã ví dụ bên trong chức năng này. Sự kiện onload sẽ xảy ra sau khi DOM sẵn sàng.

+0

Vì mục đích thử nghiệm là tốt nhưng lưu ý rằng cài đặt 'body.onload' thường được xem là hình thức xấu như các kịch bản khác có thể ghi đè lên nó. 'addEventListener' là tốt hơn, nhưng vẫn hơi chậm so với' document.onReady'. – Roshambo

3

Câu trả lời được đánh dấu là chính xác không phù hợp với tôi và thực sự là sai. Đó là một loại hack và không nên được coi là câu trả lời đúng. Cùng một cách bạn có thể thực thi mã của bạn bên trong setTimeout (function() {..}, 1000). Nó thậm chí còn đáng tin cậy hơn khi bạn có thể đặt độ trễ: -/

Trong trường hợp của tôi, tôi cần phải đợi cho tất cả các thành phần được xử lý để biết thứ nguyên thực tế của chúng. Khi chúng không được xây dựng, xử lý và thực hiện, các con số không chính xác.

CẬP NHẬT. Đây là câu trả lời đúng:

Hầu hết bạn có thể lấy dữ liệu để xây dựng DOM bằng cách sử dụng một số cuộc gọi không đồng bộ như d3.json() và đó là lý do tại sao phải mất một thời gian. Khi cuộc gọi không đồng bộ là không chặn, sau đó mã tiếp theo của bạn được gọi ngay cả trước khi cuộc gọi async kết thúc, gây ra sự cố và đây là lý do tại sao bạn đăng câu hỏi này.

Vì vậy, bạn đang cố gắng giải quyết điều này bằng cách tìm kiếm thứ gì đó trong D3 hoặc bất cứ nơi nào khác, điều đó sẽ cho bạn biết rằng cuộc gọi không đồng bộ D3 đã kết thúc và bây giờ bạn có thể làm điều tiếp theo. Một số người đề nghị thực hiện cuộc gọi đồng bộ ajax. Điều này là rất lớn. Cuộc gọi không đồng bộ được tạo thành không đồng bộ.

Điều bạn thực sự cần làm là thay đổi mô hình của mình. Chỉ cần chuyển một cuộc gọi lại đến cuộc gọi async đó và đó là nó!Một cái gì đó như thế:

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

Ngoài ra, hãy nhìn vào async.js

sự kiện Binding như gợi ý ở trên cũng là một ý tưởng khủng khiếp. Bạn nên sử dụng .enter() .exit() để thay thế. D3 là dữ liệu điều khiển, nếu bạn cần sự kiện điều khiển dòng chảy, sau đó chỉ cần sử dụng jQuery hoặc vanilla JS!