2009-04-21 43 views
10

Tôi có một tập lệnh biết tải các tập lệnh động có chứa các lớp javascript. tôi đang tải script lớp sử dụng đoạn mã sau:đồng bộ hóa tải tập lệnh động

var head = document.getElementsByTagName("head")[0]; 
var script = document.createElement("script"); 
script.type = "text/javascript"; 
script.src = "myscript.js"; 
head.appendChild(script); 

tôi sau đó cố gắng để tạo ra các lớp mới sử dụng eval:

var classObj = eval(" new MyClass()"); 

vấn đề được rằng mã của eval là được thực hiện bofre kịch bản đã được tải vào bộ nhớ và tôi nhận được một lỗi rằng MyClass is undefined.

Có cách nào để đồng bộ hóa các sự kiện này không? tôi cần phải chắc chắn rằng kịch bản được hoàn toàn được nạp vào bộ nhớ trước khi tôi có thể bắt đầu phân bổ các lớp học từ nó.

+0

Kiểm tra câu trả lời liên quan này ra: ** [Thứ tự tải của các thẻ tập lệnh được thêm động] (http://stackoverflow.com/a/38840724/2247494) ** – jherax

Trả lời

0

Bạn có chắc chắn rằng việc thêm phần tử <script> vào DOM sẽ khiến trình duyệt thực sự đánh giá tập lệnh không? Tôi có một ký ức mơ hồ về việc đọc một nơi nào đó mà nó không có, nhưng có lẽ tôi đã hít vào quá nhiều lò nướng ngày hôm qua.

+1

có. được gọi là JavaScript theo yêu cầu. bạn có thể đọc thêm tại đây: http://www.webreference.com/programming/javascript/rg29/ – Amir

0

Amir, có vẻ như tôi là tập lệnh vẫn đang ngồi trên máy chủ, điều đó có nghĩa là, chưa được trình duyệt tải. Vì vậy, head.appendChild(script); của bạn đang thêm null. Bạn không thể lấy một tập lệnh từ máy chủ chỉ bằng cách nói tên của nó, bạn cần phải yêu cầu nó và đưa nó vào trang, sử dụng ajax hoặc bằng cách tải nó bằng cách sử dụng thẻ <script>.

+0

tôi đã nghĩ lúc đầu. tôi debbuged nó và thấy rằng kịch bản đang trở nên có sẵn sau một vài phần nghìn giây. tôi đã thêm một cảnh báo ("tôi đang tải"); vào kịch bản và thấy nó được thực hiện. – Amir

+0

-1 Nó chắp thêm kịch bản lệnh _element_ ngay lập tức, nhưng nó tải và thực hiện tập lệnh _code_ một cách không đồng bộ, đó là lý do tại sao lớp đó không có sẵn ngay lập tức. –

1

Sử dụng jQuery (thư viện JavaScript) getScript để tải tập lệnh của bạn không đồng bộ. Sử dụng chức năng gọi lại để tạo các đối tượng của bạn. Ví dụ:

$.getScript("script.js", function() { 
    var classObj = new MyClass(); 
}); 
+0

Điều này là không đáng tin cậy, theo tài liệu 'getScript()': "Cuộc gọi lại được kích hoạt khi kịch bản đã được tải nhưng không nhất thiết phải được thực thi." –

5

Bạn cần phải đính kèm một xử lý sự kiện cho một trong hai phương pháp onload, trong các trình duyệt phù hợp với các tiêu chuẩn web, hoặc onreadystatechange, kiểm tra tài sản script.readyState nhận bằng "tải" hay "hoàn thành" , trong Internet Explorer.

Trước khi bạn nhận được thông báo rằng tập lệnh đã được tải, có thể bạn đang cố gắng truy cập các đối tượng, chức năng và thuộc tính chưa được khai báo hoặc tạo.

Dưới đây là một chức năng ví dụ, được chiết xuất từ ​​các mô-đun bezen.dom.js trong Javascript library, bezen.org tôi:

var appendScript = function(parent, scriptElt, listener) { 
    // append a script element as last child in parent and configure 
    // provided listener function for the script load event 
    // 
    // params: 
    // parent - (DOM element) (!nil) the parent node to append the script to 
    // scriptElt - (DOM element) (!nil) a new script element 
    // listener - (function) (!nil) listener function for script load event 
    // 
    // Notes: 
    // - in IE, the load event is simulated by setting an intermediate 
    //  listener to onreadystate which filters events and fires the 
    //  callback just once when the state is "loaded" or "complete" 
    // 
    // - Opera supports both readyState and onload, but does not behave in 
    //  the exact same way as IE for readyState, e.g. "loaded" may be 
    //  reached before the script runs. 

    var safelistener = catchError(listener,'script.onload'); 

    // Opera has readyState too, but does not behave in a consistent way 
    if (scriptElt.readyState && scriptElt.onload!==null) { 
     // IE only (onload===undefined) not Opera (onload===null) 
     scriptElt.onreadystatechange = function() { 
     if (scriptElt.readyState === "loaded" || 
      scriptElt.readyState === "complete") { 
      // Avoid memory leaks (and duplicate call to callback) in IE 
      scriptElt.onreadystatechange = null; 
      safelistener(); 
     } 
     }; 
    } else { 
     // other browsers (DOM Level 0) 
     scriptElt.onload = safelistener; 
    } 
    parent.appendChild(scriptElt); 
}; 

Để thích ứng với nhu cầu của bạn, bạn có thể thay thế các cuộc gọi đến catchError, bao bọc người nghe để đón và đăng nhập lỗi và sử dụng chức năng đã sửa đổi:

var appendScript = function(parent, scriptElt, listener) { 
    // append a script element as last child in parent and configure 
    // provided listener function for the script load event 
    // 
    // params: 
    // parent - (DOM element) (!nil) the parent node to append the script to 
    // scriptElt - (DOM element) (!nil) a new script element 
    // listener - (function) (!nil) listener function for script load event 
    // 
    // Notes: 
    // - in IE, the load event is simulated by setting an intermediate 
    //  listener to onreadystate which filters events and fires the 
    //  callback just once when the state is "loaded" or "complete" 
    // 
    // - Opera supports both readyState and onload, but does not behave in 
    //  the exact same way as IE for readyState, e.g. "loaded" may be 
    //  reached before the script runs. 

    var safelistener = function(){ 
     try { 
     listener(); 
     } catch(e) { 
     // do something with the error 
     } 
    }; 

    // Opera has readyState too, but does not behave in a consistent way 
    if (scriptElt.readyState && scriptElt.onload!==null) { 
     // IE only (onload===undefined) not Opera (onload===null) 
     scriptElt.onreadystatechange = function() { 
     if (scriptElt.readyState === "loaded" || 
      scriptElt.readyState === "complete") { 
      // Avoid memory leaks (and duplicate call to callback) in IE 
      scriptElt.onreadystatechange = null; 
      safelistener(); 
     } 
     }; 
    } else { 
     // other browsers (DOM Level 0) 
     scriptElt.onload = safelistener; 
    } 
    parent.appendChild(scriptElt); 
}; 
2

Vì bạn dường như có thể chỉnh sửa tập lệnh bên ngoài (vì bạn đã thử nghiệm bằng cảnh báo), tại sao không chỉ đặt mã này trong tập lệnh đó?

Nếu bạn không thể làm điều đó (có thể thêm mã được tạo ra hoặc các tập tin đầu tiên được chia sẻ có lẽ), chỉ cần thêm một cuộc gọi chức năng ở phần cuối của kịch bản bạn đang tải như thế này:

load_complete(); 

và sau đó đặt mã phụ của bạn trong chức năng đó:

function load_complete() { 
    var classObj = eval(" new MyClass()"); 
} 

đó là đơn giản hơn rất nhiều và hết sức rõ ràng hơn bất kỳ loại kích hoạt onload. Ngoài ra, nếu tệp js được chia sẻ, thì bạn có thể có các hàm load_complete khác nhau trên mọi trang sử dụng nó (chỉ cần đảm bảo luôn định nghĩa load_complete, ngay cả khi nó trống).

1

Tôi tin rằng điều này thực sự có thể được khắc phục bằng cách đảm bảo bạn đặt mã tải kịch bản bên ngoài và các mã sử dụng kịch bản bên ngoài khối kịch bản riêng biệt như thế này:

<script> 
var head = document.getElementsByTagName("head")[0]; 
var script = document.createElement("script"); 
script.type = "text/javascript"; 
script.src = "myscript.js"; 
head.appendChild(script); 

//this is in the same script block so it wouldn't work 
//var classObj = eval(" new MyClass()"); 
</script> 

<script> 
//this is in a separate script block so it will work 
var classObj = eval(" new MyClass()"); 
</script> 
Các vấn đề liên quan