Cách thực hiện tập lệnh chờ cho đến khi tải xong jquery


106

Tôi đang gặp sự cố khi trang tải quá nhanh, jquery chưa tải xong trước khi nó được gọi bằng tập lệnh tiếp theo. Có cách nào để kiểm tra sự tồn tại của jquery và nếu nó không tồn tại, hãy đợi một lát rồi thử lại?


Để trả lời các câu trả lời / nhận xét bên dưới, tôi đăng một số đánh dấu.

Tình hình ... trang chủ và trang con của asp.net.

Trong trang chủ, tôi có tham chiếu đến jquery. Sau đó, trong trang nội dung, tôi có tham chiếu đến tập lệnh cho từng trang. Khi tập lệnh của trang cụ thể đang được tải, nó sẽ phàn nàn rằng "$ không được xác định".

Tôi đặt cảnh báo tại một số điểm trong phần đánh dấu để xem thứ tự mà mọi thứ đang kích hoạt và xác nhận rằng nó sẽ kích hoạt theo thứ tự sau:

  1. Tiêu đề trang chính.
  2. Khối nội dung trang con 1 (nằm bên trong phần đầu của trang chủ, nhưng sau khi các tập lệnh trang chính được gọi).
  3. Khối nội dung trang con 2.

Đây là phần đánh dấu ở đầu trang chủ:

<%@ Master Language="C#" AutoEventWireup="true" CodeBehind="Site.master.cs" Inherits="SiteMaster" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title>Reporting Portal</title>
    <link href="~/Styles/site.css" rel="stylesheet" type="text/css" />
    <link href="~/Styles/red/red.css" rel="stylesheet" type="text/css" />
    <script type="text/Scripts" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script> 
    <script type="text/Scripts" language="javascript" src="../Scripts/jquery.dropdownPlain.js"></script>
    <script type="text/Scripts" language="javascript" src="../Scripts/facebox.js"></script>
    <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />
    <asp:ContentPlaceHolder ID="head" runat="server">
    </asp:ContentPlaceHolder>
</head>

Sau đó, trong phần nội dung của trang chủ, có thêm ContentPlaceHolder:

 <asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server">
                </asp:ContentPlaceHolder>

Trong trang con, nó trông giống như vậy:

<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Dashboard.aspx.cs" Inherits="Data.Dashboard" %>
<%@ Register src="../userControls/ucDropdownMenu.ascx" tagname="ucDropdownMenu" tagprefix="uc1" %>
<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
    <link rel="stylesheet" type="text/css" href="../Styles/paserMap.css" />
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
***CONTENT HERE***
    <script src="../Scripts/Dashboard.js" type="text/javascript"></script>
</asp:Content>

Đây là nội dung của tệp "../Script/Dashboard.js":

    $(document).ready(function () {

    $('.tgl:first').show(); // Show the first div

    //Description: East panel parent tab navigation
    $('.tabNav label').click(function () {
        $('.tabNav li').removeClass('active')
        $(this).parent().addClass('active');

        var index = $(this).parent('li').index();
        var divToggle = $('.ui-layout-content').children('div.tgl');

        //hide all subToggle divs
        divToggle.hide();
        divToggle.eq(index).show();
    });

});

4
bạn đang sử dụng $(document).ready(function(){...});?
rownage

Nếu bạn đang tải tập lệnh với <script src="...">các thẻ đơn giản , điều đó không thể xảy ra. Bạn có đang sử dụng một cái gì đó như RequiJS hoặc LABjs không?
Pointy

bạn đang chạy tập lệnh tiếp theo trong $(document).ready()hoặc $(window).load()? Chúng ta có thể xem một ví dụ?
John Hartsock

1
@AmandaMyer bây giờ, bỏ qua tất cả những gợi ý về việc sử dụng tài liệu đã sẵn sàng, kể từ khi bạn đã đang làm điều đó, nhưng tôi đặt cược nó là Mimetype đó gây ra họ không nên tải đồng bộ
Sander

2
Hãy thử thay đổi type="text/Scripts"để type="text/javascript", hoặc thoát khỏi nó tất cả cùng nhau nó không cần thiết nữa, cũng thoát khỏi language="javascript. Cũng có thể bắt đầu thử mọi thứ.
Jack

Câu trả lời:


11

biên tập

Bạn có thể thử đúng loại cho các thẻ script của mình không? Tôi thấy bạn sử dụng text/Scripts, đó không phải là mimetype phù hợp cho javascript.

Dùng cái này:

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script> 
<script type="text/javascript" src="../Scripts/jquery.dropdownPlain.js"></script>
<script type="text/javascript" src="../Scripts/facebox.js"></script>

kết thúc chỉnh sửa

hoặc bạn có thể xem qua request.js , đây là một trình tải cho mã javascript của bạn.

tùy thuộc vào dự án của bạn, tuy nhiên, điều này có thể hơi quá mức cần thiết


205

Đến muộn, và tương tự như câu hỏi của Briguy37, nhưng để tham khảo trong tương lai, tôi sử dụng phương pháp sau và chuyển các hàm tôi muốn hoãn cho đến khi tải xong jQuery:

function defer(method) {
    if (window.jQuery) {
        method();
    } else {
        setTimeout(function() { defer(method) }, 50);
    }
}

Nó sẽ gọi đệ quy phương thức trì hoãn sau mỗi 50ms cho đến khi window.jQuerytồn tại tại thời điểm nó thoát và gọimethod()

Một ví dụ với chức năng ẩn danh:

defer(function () {
    alert("jQuery is now loaded");
});

2
Điều này đã không làm việc cho tôi. Bên trong phương thức, $ không được xác định ... không rõ tại sao.
Aaron Lifshin

Đây là một giải pháp tốt để có được một số mã tùy chỉnh hoạt động trong một trang web được thiết kế bởi người khác trong Adobe Muse.
Buggabill

@gidim Nó sẽ không, vì nó là một bộ đếm thời gian chứ không phải một vòng lặp. Nhưng nó sẽ tiếp tục chạy miễn là người dùng vẫn ở trên trang.
Micros

Sẽ tốt hơn nếu điều tra thứ tự tải của các tập lệnh. Tôi nhận thấy rằng nếu scriptthẻ tải jQuery có deferthuộc tính, nó sẽ gây ra sự cố do không tải cho đến sau này, mặc dù thứ tự tập lệnh được xác định bằng mã.
ADTC

19

bạn có thể sử dụng thuộc tính defer để tải script ở cuối thực sự.

<script type='text/javascript' src='myscript.js' defer='defer'></script>

nhưng thông thường tải tập lệnh của bạn theo đúng thứ tự sẽ thực hiện thủ thuật, vì vậy hãy đảm bảo đặt bao gồm jquery trước tập lệnh của riêng bạn

Nếu mã của bạn nằm trong trang chứ không phải trong tệp js riêng biệt, vì vậy bạn phải thực thi tập lệnh của mình chỉ sau khi tài liệu đã sẵn sàng và việc đóng gói mã của bạn như thế này cũng sẽ hoạt động:

$(function(){
//here goes your code
});

Đây là ok chừng nào bạn có một phụ thuộc duy nhất
Guillaume Massé

17

Tuy nhiên, một cách khác để làm điều này, mặc dù phương pháp trì hoãn của Darbio linh hoạt hơn.

(function() {
  var nTimer = setInterval(function() {
    if (window.jQuery) {
      // Do something with jQuery
      clearInterval(nTimer);
    }
  }, 100);
})();

16

cách dễ nhất và an toàn nhất là sử dụng một cái gì đó như sau:

var waitForJQuery = setInterval(function () {
    if (typeof $ != 'undefined') {

        // place your code here.

        clearInterval(waitForJQuery);
    }
}, 10);

2
Giải pháp thiên tài. Cảm ơn bạn
Diego Fortes

13

Bạn có thể thử sự kiện onload. Nó tăng lên khi tất cả các tập lệnh đã được tải:

window.onload = function () {
   //jquery ready for use here
}

Nhưng hãy nhớ rằng bạn có thể ghi đè các tập lệnh khác khi sử dụng window.onload.


làm việc này nhưng có thể bạn sẽ thấy một đèn flash của nội dung unstyled nếu jquery của bạn thay đổi sự xuất hiện của trang
Matthew Khóa

1
Bạn có thể thêm trình xử lý sự kiện để tránh ghi đè các tập lệnh khác: stackoverflow.com/a/15564394/32453 FWIW '
rogerdpack Ngày

@rogerdpack đồng ý, đó là giải pháp tốt hơn.
Nigrimmist

10

Tôi đã nhận thấy rằng giải pháp được đề xuất chỉ hoạt động trong khi lưu ý đến mã không đồng bộ. Đây là phiên bản sẽ hoạt động trong cả hai trường hợp:

document.addEventListener('DOMContentLoaded', function load() {
    if (!window.jQuery) return setTimeout(load, 50);
    //your synchronous or asynchronous jQuery-related code
}, false);

Cảm ơn, kiểm tra một lần khi tải và chỉ khi bắt đầu dự phòng lặp lại sau mỗi 50ms sẽ tốt hơn nhiều so với việc chỉ để nó lặp lại với setInterval như câu trả lời được bình chọn hàng đầu. Trong trường hợp tốt nhất, bạn có độ trễ 0ms, thay vì trường hợp trung bình là 25ms của câu trả lời hàng đầu.
Luc

Đẹp và thanh lịch - Tôi thích cách bạn truyền loadhàm nhưng sau đó cũng sử dụng lại nó setTimeout.
Nemesarial

6

Đó là một vấn đề phổ biến, hãy tưởng tượng bạn sử dụng một công cụ tạo khuôn mẫu PHP tuyệt vời, vì vậy bạn có bố cục cơ sở của mình:

HEADER
BODY ==> dynamic CONTENT/PAGE
FOOTER

Và tất nhiên, bạn đọc ở đâu đó tốt hơn nên tải Javascript ở cuối trang, vì vậy nội dung động của bạn không biết ai là jQuery (hoặc $).

Ngoài ra, bạn đã đọc ở đâu đó rất tốt khi inline Javascript nhỏ, vì vậy hãy tưởng tượng bạn cần jQuery trong một trang, baboom, $ chưa được định nghĩa (.. chưa ^^).

Tôi thích giải pháp mà Facebook cung cấp

window.fbAsyncInit = function() { alert('FB is ready !'); }

Vì vậy, là một lập trình viên lười biếng (tôi nên nói là một lập trình viên giỏi ^^), bạn có thể sử dụng một cách tương đương (trong trang của bạn):

window.jqReady = function() {}

Và thêm vào cuối bố cục của bạn, sau khi jQuery bao gồm

if (window.hasOwnProperty('jqReady')) $(function() {window.jqReady();});

Cảm ơn! Đó chính xác là trường hợp tôi đang gặp phải.
KumZ

Hoặc di chuyển tải jquery lên đầu :) stackoverflow.com/a/11012073/32453
rogerdpack Ngày

5

Thay vì "đợi" (thường được thực hiện bằng cách sử dụng setTimeout), bạn cũng có thể sử dụng định nghĩa đối tượng jQuery trong chính cửa sổ như một hook để thực thi mã dựa vào nó. Điều này có thể đạt được thông qua một định nghĩa thuộc tính, được xác định bằng cách sử dụng Object.defineProperty.

(function(){
  var _jQuery;
  Object.defineProperty(window, 'jQuery', {
    get: function() { return _jQuery; },
    set: function($) {
      _jQuery = $;

      // put code or call to function that uses jQuery here

    }
  });
})();

điều này sẽ hữu ích một cách mơ hồ nếu jQuery thực sự định nghĩa một thuộc tính cửa sổ, nhưng nó dường như không?
Jim

Nó không thực sự đặt một thuộc tính, nhưng nó đặt window.jQuerythành một giá trị, bằng cách xác định jQuerybiến trong phạm vi toàn cục (tức là window). Điều này sẽ kích hoạt chức năng setter thuộc tính trên đối tượng cửa sổ được xác định trong mã.
Người bảo vệ một

Điều này không hoạt động nếu thuộc tính jQuery được xác định trước khi mã này được thực thi; tức là nếu jquery.js hoãn lại của bạn tải trước khi mã này được tải. Một giải pháp là đưa jquery.js hoãn của bạn trước khi </body>thay vì trước</head>
user36388

@user: Đơn giản chỉ cần thực thi mã trước khi jQuery (hoãn lại hoặc cách khác) được tải. Bạn tải nó ở đâu không quan trọng, chỉ là đoạn mã của tôi xuất hiện trước nó.
Người bảo vệ một

1
Điều này rất hữu ích nếu bạn có thể đảm bảo rằng các tập lệnh sử dụng điều này sẽ xuất hiện trước khi đưa jQuery vào tài liệu của bạn. Không có sự thiếu hiệu quả hoặc tình huống chủng tộc nào từ các phương pháp lặp lại ở trên.
RedYetiCo

4

Sử dụng:

$(document).ready(function() {
    // put all your jQuery goodness in here.
});

Kiểm tra phần này để biết thêm thông tin: http://www.learningjquery.com/2006/09/introductioning-document-ready

Lưu ý: Điều này sẽ hoạt động miễn là nhập tập lệnh cho thư viện JQuery của bạn ở trên lệnh gọi này.

Cập nhật:

Nếu vì lý do nào đó mà mã của bạn không được tải đồng bộ (điều mà tôi chưa bao giờ gặp phải, nhưng dường như có thể xảy ra từ nhận xét bên dưới ), bạn có thể viết mã như sau.

function yourFunctionToRun(){
    //Your JQuery goodness here
}

function runYourFunctionWhenJQueryIsLoaded() {
    if (window.$){
        //possibly some other JQuery checks to make sure that everything is loaded here

        yourFunctionToRun();
    } else {
        setTimeout(runYourFunctionWhenJQueryIsLoaded, 50);
    }
}

runYourFunctionWhenJQueryIsLoaded();

6
sự chờ đợi này cho đến khi dom sẵn sàng, không phải cho đến khi jquery được tải. anh ta có thể có 2 tệp tập lệnh, 1 từ CDN (jquery từ google và một plugin cục bộ) trong đó tệp 1 được tải sớm hơn tệp kia .... mã của bạn không giải quyết được điều này, nếu plugin được tải nhanh hơn chính jquery
Sander

2
tôi đồng ý với bạn @Sander này nên hoạt động như tải đồng bộ
malko

bạn nói đúng, giả định của tôi về việc chúng được tải không đồng bộ nếu chúng đến từ một miền khác là hoàn toàn sai! xin lỗi cho điều đó
Sander

Điều này sẽ ném ra một ngoại lệ nếu jquery không có sẵn khiến nó không bao giờ đi vào câu lệnh else. Đoán nó nên là một thử và nắm bắt hoặc một cái gì đó.
Sam Stoelinga

@SamStoelinga: Cảm ơn, điều này hiện đã được khắc phục.
Briguy37

3

Tôi không quá thích những thứ ngắt quãng. Khi tôi muốn trì hoãn jquery, hoặc bất cứ điều gì thực sự, nó thường diễn ra như thế này.

Bắt đầu với:

<html>
 <head>
  <script>var $d=[];var $=(n)=>{$d.push(n)}</script>
 </head>

Sau đó:

 <body>
  <div id="thediv"></div>

  <script>
    $(function(){
       $('#thediv').html('thecode');
    });
  </script>

  <script src="http://code.jquery.com/jquery-3.2.1.min.js" type="text/javascript"></script>

Rồi cuối cùng:

  <script>for(var f in $d){$d[f]();}</script>
 </body>
<html>

Hoặc phiên bản ít hoang mang hơn:

<script>var def=[];function defer(n){def.push(n)}</script>
<script>
defer(function(){
   $('#thediv').html('thecode');
});
</script>
<script src="http://code.jquery.com/jquery-3.2.1.min.js" type="text/javascript"></script>
<script>for(var f in def){def[f]();}</script>

Và trong trường hợp không đồng bộ, bạn có thể thực thi các hàm được đẩy trên jquery onload.

<script async onload="for(var f in def){def[f]();}" 
src="jquery.min.js" type="text/javascript"></script>

Ngoài ra:

function loadscript(src, callback){
  var script = document.createElement('script');
  script.src = src
  script.async = true;
  script.onload = callback;
  document.body.appendChild(script);
};
loadscript("jquery.min", function(){for(var f in def){def[f]();}});

2

Tôi không nghĩ đó là vấn đề của bạn. Tải tập lệnh là đồng bộ theo mặc định, vì vậy trừ khi bạn đang sử dụng deferthuộc tính hoặc tải bản thân jQuery thông qua một yêu cầu AJAX khác, vấn đề của bạn có thể giống với 404. Bạn có thể hiển thị đánh dấu của mình và cho chúng tôi biết nếu bạn thấy bất kỳ điều gì đáng ngờ trong firebug hay trình kiểm tra web?


2

Hãy mở rộng defer()từ Dario để có thể tái sử dụng nhiều hơn.

function defer(toWaitFor, method) {
    if (window[toWaitFor]) {
        method();
    } else {
        setTimeout(function () { defer(toWaitFor, method) }, 50);
    }
}

Sau đó sẽ chạy:

function waitFor() {
    defer('jQuery', () => {console.log('jq done')});
    defer('utag', () => {console.log('utag done')});
}

1

Kiểm tra điều này:

https://jsfiddle.net/neohunter/ey2pqt5z/

Nó sẽ tạo một đối tượng jQuery giả mạo, cho phép bạn sử dụng các phương thức onload của jquery và chúng sẽ được thực thi ngay khi jquery được tải.

Nó không hoàn hảo.

// This have to be on <HEAD> preferibly inline
var delayed_jquery = [];
jQuery = function() {
  if (typeof arguments[0] == "function") {
    jQuery(document).ready(arguments[0]);
  } else {
    return {
      ready: function(fn) {
        console.log("registering function");
        delayed_jquery.push(fn);
      }
    }
  }
};
$ = jQuery;
var waitForLoad = function() {
  if (typeof jQuery.fn != "undefined") {
    console.log("jquery loaded!!!");
    for (k in delayed_jquery) {
      delayed_jquery[k]();
    }
  } else {
    console.log("jquery not loaded..");
    window.setTimeout(waitForLoad, 500);
  }
};
window.setTimeout(waitForLoad, 500);
// end



// now lets use jQuery (the fake version)
jQuery(document).ready(function() {
  alert('Jquery now exists!');
});

jQuery(function() {
  alert('Jquery now exists, this is using an alternative call');
})

// And lets load the real jquery after 3 seconds..
window.setTimeout(function() {
  var newscript = document.createElement('script');
  newscript.type = 'text/javascript';
  newscript.async = true;
  newscript.src = 'https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js';
  (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(newscript);
}, 3000);


đây là một giải pháp không rắc rối cho đăng ký tài liệu chức năng sẵn sàng nhờ
zanedev

0

Một lưu ý tiếp tuyến về các cách tiếp cận ở đây tải sử dụng setTimeouthoặc setInterval. Trong những trường hợp đó, có thể khi kiểm tra của bạn chạy lại, DOM đã được tải và DOMContentLoadedsự kiện của trình duyệt sẽ được kích hoạt, vì vậy bạn không thể phát hiện sự kiện đó một cách đáng tin cậy bằng cách sử dụng các phương pháp này. Điều tôi thấy là jQuery readyvẫn hoạt động, vì vậy bạn có thể nhúng

jQuery(document).ready(function ($) { ... }

bên trong của bạn setTimeouthoặc setIntervalvà mọi thứ sẽ hoạt động như bình thường.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.