2010-10-17 29 views
5

Điều tôi đang cố gắng thực hiện bao gồm một hoặc nhiều tệp js từ bên trong Javascript. Tôi biết có một loạt các kỹ thuật cho việc này, nhưng tôi đang tìm một giải pháp tuân theo các quy tắc sau:Tự động bao gồm JavaScript và đợi

1) Không sử dụng bất kỳ khung công tác hoặc thư viện JS nào (jQuery, Prototype, v.v.).

2) Tập lệnh phải tạm dừng thực thi cho đến khi tệp js bên ngoài được tải hoàn toàn và phân tích cú pháp bởi trình duyệt.

Lý do đằng sau # 2 là tệp js bên ngoài sẽ bao gồm các định nghĩa hàm cần được sử dụng ngay sau khi tập lệnh được bao gồm. Hầu hết thời gian trình duyệt không có thời gian để phân tích cú pháp tệp js trước khi tôi bắt đầu gọi các hàm đó.

Tôi không quan tâm sử dụng một callback để biết khi kịch bản được tải xong, nhưng:

1) Tôi không biết trước thời hạn bao nhiêu kịch bản sẽ được bao gồm tự động, vì vậy tôi don không muốn và không thể viết một loạt các callbacks lồng nhau. Tôi chỉ cần biết khi nào họ tải xong.

2) Tôi đang nói rằng cố gắng sử dụng một số loại "tải" sự kiện để kích hoạt tính năng gọi lại có thể không hoạt động nếu trình duyệt đã lưu trong bộ nhớ cache JavaScript.

Chỉnh sửa Tôi đã đề cập từ đầu rằng đây là dành cho hệ thống plugin nhưng tôi muốn giữ cho câu hỏi/câu trả lời của tôi đủ chung để giúp ích cho người khác.

Người dùng sẽ xác định trình cắm nào họ muốn tải trong một mảng.

plugins = [ 'FooPlugin', 'BarPlugin' ]; 

tôi sẽ sau đó lặp qua mảng, và tải các script js cho mỗi plugin:

for(var i = 0; i < plugins.length; i++) { 
    loadScript('plugins/' + plugins[i] + '.js'); 
} 

Mỗi Plugin đẩy bản thân vào mảng loaded_plugins (Đây là một ví dụ về FooPlugin.js)

load_plugins.push({ 
    name: 'FooPlugin', 
    // Other plugin methods and properties here 
}); 
+0

Không chắc chắn về câu trả lời này, do đó, chỉ cần đăng nó như một bình luận. Bạn đã thử sử dụng AJAX để tải nội dung của javascript vào biến phía máy khách, sau đó sử dụng 'eval()'? Vì bạn phải đợi AJAX trả về một thông báo 200/OK, bạn biết toàn bộ kịch bản đã được nạp, và 'eval()' không nên đưa bạn đến dòng kế tiếp cho đến khi nó chạy xong toàn bộ tập lệnh. – stevendesu

+0

Bạn có thể xác định tập lệnh nào cần thiết trong khi tải trang không? Nếu vậy, thì giải pháp của @ Paul là lý tưởng. Nếu không, giải pháp của bạn sẽ tùy thuộc vào trình duyệt nào bạn muốn hỗ trợ và liệu các tập lệnh có nằm trong cùng một tên miền với trang của bạn hay không. –

+0

@steven_desu - Các hàm/biến trong kịch bản được nạp có kết thúc trong ngữ cảnh mà eval() được gọi không? Có nghĩa là họ sẽ không có sẵn bên ngoài chức năng sử dụng eval. – mellowsoon

Trả lời

3

Trình duyệt chéo hoạt động, bắt đầu với IE6.

document.loadScript = function (src, callback) { 
    function script(src, onload) { 
     var scriptTag = document.createElement('script'); 
     if (onload) scriptTag.onload = onload; 
     scriptTag.src = src; 
     scriptTag.type = 'text/javascript'; 
     return scriptTag; 
    } 
    function outer(tag) { 
     var d = document.createElement('div'); 
     d.appendChild(tag); 
     return d.innerHTML; 
    } 
    if (!(src instanceof Array)) src = [src]; 
    var i, scr, 
     callbackId = 'dynCall_'+Math.floor(Math.random()*89999+10000); 
     counter = src.length, 
     call = function() { if (--counter == 0) callback(); }; 
    if (!document.body) { 
     window[callbackId] = function() { 
      delete window[callbackId]; 
      if (callback instanceof Function) callback(); 
     }; 
     for (i=0; i<src.length; i++) document.write(outer(script(src[i]))); 
     document.write('<scr'+'ipt type="text/javascript">'+'window.'+callbackId+'();'+'</scr'+'ipt>'); 
     return; 
    } 
    for (i=0; i<src.length; i++) document.body.appendChild(script(src[i], call)); 
}; 

được rút gọn/obfuscated:

(function(){document.loadScript=function(src,callback){function script(src,onload){var scriptTag=document.createElement('script');if(onload)scriptTag.onload=onload;scriptTag.src=src;scriptTag.type='text/javascript';return scriptTag}function outer(tag){var d=document.createElement('div');d.appendChild(tag);return d.innerHTML}if(!(src instanceof Array))src=[src];var i,scr,callbackId='dynCall_'+Math.floor(Math.random()*89999+10000);counter=src.length,call=function(){if(--counter==0)callback()};if(!document.body){window[callbackId]=function(){delete window[callbackId];if(callback instanceof Function)callback()};for(i=0;i<src.length;i++)document.write(outer(script(src[i])));document.write('<scr'+'ipt type="text/javascript">'+'window.'+callbackId+'();'+'</scr'+'ipt>');return}for(i=0;i<src.length;i++)document.body.appendChild(script(src[i],call))};document.loadScript.toString=function(){return'function loadScript() { [obfuscated] }'}})(); 

nhanh giải thích về các ngành:

Nếu thẻ script gọi loadScript nằm trong phần đầu tài liệu hoặc cơ thể, document.body sẽ không xác định, như DOM chưa có. Do đó, chúng tôi không thể sử dụng phương pháp tiêu chuẩn để gắn thẻ và chúng tôi phải sử dụng tính năng ghi tài liệu. Điều này có lợi thế là các thẻ chúng ta viết sẽ xảy ra theo trật tự tuần tự nhất thiết, nhưng nhược điểm là chúng ta phải có một hàm gọi lại phạm vi toàn cục.

Trong khi đó, nếu chúng ta có document.body, chúng ta sẽ làm vàng cho việc làm đúng (-ish - chúng ta hi sinh khi không có thư viện xung quanh để làm điều đó đúng, do đó .onload(). nó không giống như chúng ta sắp ném rất nhiều sự kiện trên một thẻ script, vì vậy nó có thể sẽ ổn.) Những bất lợi (có thể?) là các script đều tải không đồng bộ, vì vậy chúng ta cần phải chạy đếm ngược khi chúng tải.

+0

Chỉ cần được rõ ràng ... là scr.onload vẫn sẽ được gọi là nếu trình duyệt có kịch bản được lưu trữ, và không thực sự tải nó? – mellowsoon

+0

Scr.onload sẽ được gọi, được lưu trong bộ nhớ cache hoặc không, miễn là sự kiện onload được thiết lập * trước * thuộc tính src là. Nếu không, bộ nhớ cache bắt trước khi lệnh JS tiếp theo đi qua. Bổ sung: bạn có thể muốn kéo tập lệnh hiện tại; Tôi đã thêm nhiều tập lệnh hỗ trợ. – Fordi

+0

Dường như thuộc tính bên ngoàiHTML không được Firefox hỗ trợ. – mellowsoon

2

Giải pháp dễ nhất (và đáng tin cậy nhất) mà tôi biết là chỉ cần sử dụng document.write để chèn thẻ tập lệnh mới vào tài liệu (hoặc nhiều như bạn cần, tất nhiên):

<script type="text/javascript">document.write('<' + 'script src="/assets/js/file.js" type="text/javascript"><\/script>');</script> 

trình duyệt sẽ (afaik) phân tích & thực thi kịch bản theo trình tự quy định trong tài liệu của bạn bằng cách các thẻ kịch bản, cũng có khi thêm vào theo cách này. Vì vậy, bạn nên được tốt nếu bạn đặt phần còn lại của mã bạn muốn thực hiện trong một mới, riêng biệt, thẻ script.

EDIT: Dưới đây là một (nhiều) more elaborate approach, mà trong cơ sở đang sử dụng "elem = document.createElement ('script')", tiếp theo là nghe elem.onreadystatechange (đối với IE) và elem.onload. Sẽ tải toàn bộ một loạt các tập lệnh song song, sau đó kích hoạt một cuộc gọi lại khi chúng ở đó. Đã sử dụng điều này trong quá khứ, khi các trình duyệt vẫn chủ yếu tải các kịch bản một lần để tăng tốc độ tải (nó dựa trên một kịch bản với mục đích tương tự một nơi nào đó trên mạng). Ví dụ sử dụng:

Loader.script([ 
    './js/jquery/jquery.jmap.js', 
    './js/jquery/jquery.getUrlParam.js', 
    './js/jquery/jquery.form.js', 
    './js/jquery/jquery.gettext.js', 
    './js/jquery/jquery.scrollTo.js', 
    './js/jquery/jquery.prettydate.js' 
    ], { complete: function() { 
    // do your thing 
    } 
}) 
+1

document.write chỉ là giải pháp khả thi trong quá trình tải trang ban đầu. Sử dụng nó bất cứ lúc nào sau đó sẽ ghi đè lên toàn bộ trang. Vấn đề phức tạp hơn một chút. http://www.javascriptkit.com/javatutors/loadjavascriptcss.shtml giải thích tự động tải javascript, nhưng điều này là không đủ thông minh để nhận ra khi nó được hoàn thành thực thi kịch bản, thậm chí. – stevendesu

+0

Vâng, tốt nhất; Tôi đã chỉ sử dụng trực tiếp trong đầu, khá một thời gian trở lại. – Paul

+0

Cách tiếp cận thứ hai tương thích với trình duyệt chéo? – mellowsoon

0

Một vài cách tiếp cận có thể có ở đầu đầu của tôi. Tôi sẽ viết các bước, chứ không phải mã.

Lựa chọn 1

  1. tải các tập tin JS bằng cách đặt nó trong phần tử với một thẻ.
  2. Ngay sau khi kích hoạt một setTimout() để kiểm tra một biến trong tệp JS bạn tải.
  3. Lặp lại # 2 cho đến khi có biến ở đó hoặc cho đến khi đạt đến maxCounter (maxCounter = 10) để bạn không ngồi đó mãi mãi.
  4. Khi biến đó ở đó, hãy chuyển đến tệp JS tiếp theo và bắt đầu lại ở bước 1.

Lựa chọn 2

  1. Sử dụng một cách tiếp cận Ajax.
  2. Gọi tệp JS bằng Ajax.
  3. Nối kết quả vào hướng dẫn sử dụng trong bài viết dưới đây.
  4. setTimeout() hoạt động trong vài giây và xử lý tập lệnh HOẶC kiểm tra biến tương tự như tùy chọn # 1 và thời gian chờ lặp lại cho đến khi có biến.

tham khảo bài viết này cho cả hai phương pháp: http://www.hunlock.com/blogs/Howto_Dynamically_Insert_Javascript_And_CSS

Phần khó khăn là bạn đang yêu cầu mã của bạn để biết khi nào mỗi tập tin JS được nạp và xử lý bởi trình duyệt. Vì máy tính và trình duyệt thay đổi thời gian cần thiết để xử lý tệp JS có thể khác nhau khiến việc theo dõi mà không cần một số logic trong tệp JS sẽ làm cho việc này khó thực hiện.

tôi sẽ thích một cách tiếp cận như thế này:

. Tạo một thẻ yêu cầu Javascript, ajax, bất cứ điều gì đến một máy chủ có thể xử lý logic động (php, java, .NET, v.v.).

. Chuyển một hàm gọi lại đã tồn tại trên trang và biết có bao nhiêu tệp JS bổ sung mà bạn cần biết. Thậm chí bao gồm một tham số chỉ mục để gọi lại (một cái gì đó như myCallback (5)) để bạn biết bạn đang ở đâu.

1-2a. Bạn chỉ có thể nối thêm hàm gọi lại cho tất cả các tệp JS của bạn theo cách thủ công và để cho hàm quản lý hàng đợi JS mà không có các tệp JS có bất kỳ kiến ​​thức nào khác ngoài hàm gọi lại.

. Hãy để máy chủ nối thêm gọi lại vào tệp JS đang được trả về.

. Hàm gọi lại sẽ được kích hoạt khi JS được xử lý và nó sẽ quản lý việc tải các tệp JS bổ sung cho bạn.

Làm thế nào về điều đó?

Các vấn đề liên quan