วิธีทำให้การเรียกใช้สคริปต์รอจนกว่า jquery จะถูกโหลด


106

ฉันมีปัญหาที่หน้าโหลดเร็วมาก jquery นั้นยังโหลดไม่เสร็จก่อนที่จะถูกเรียกใช้โดยสคริปต์ที่ตามมา มีวิธีตรวจสอบการมีอยู่ของ jquery หรือไม่และหากไม่มีอยู่ให้รอสักครู่แล้วลองอีกครั้ง


เพื่อตอบสนองต่อคำตอบ / ความคิดเห็นด้านล่างฉันกำลังโพสต์มาร์กอัปบางส่วน

สถานการณ์ ... asp.net masterpage และ childpage

ในมาสเตอร์เพจฉันมีการอ้างอิงถึง jquery จากนั้นในหน้าเนื้อหาฉันมีการอ้างอิงถึงสคริปต์เฉพาะหน้า เมื่อมีการโหลดสคริปต์เฉพาะของหน้ามันจะบ่นว่า "$ is undefined"

ฉันใส่การแจ้งเตือนในหลาย ๆ จุดในมาร์กอัปเพื่อดูลำดับการทำงานของสิ่งต่างๆและยืนยันว่ามันเริ่มทำงานตามลำดับนี้:

  1. ส่วนหัวของหน้าหลัก
  2. บล็อกเนื้อหาของหน้าย่อย 1 (อยู่ด้านในส่วนหัวของมาสเตอร์เพจ แต่หลังจากเรียกสคริปต์มาสเตอร์เพจแล้ว)
  3. บล็อกเนื้อหาของหน้าย่อย 2.

นี่คือมาร์กอัปที่ด้านบนของหน้ามาสเตอร์:

<%@ 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>

จากนั้นในเนื้อหาของหน้ามาสเตอร์มี ContentPlaceHolder เพิ่มเติม:

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

ในหน้าย่อยดูเหมือนว่า:

<%@ 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>

นี่คือเนื้อหาของไฟล์ "../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
คุณกำลังใช้$(document).ready(function(){...});?
แถว

หากคุณกำลังโหลดสคริปต์ด้วย<script src="...">แท็กธรรมดาสิ่งนั้นจะไม่เกิดขึ้น คุณกำลังใช้บางอย่างเช่น RequireJS หรือ LABjs หรือไม่?
Pointy

คุณกำลังเรียกใช้สคริปต์ที่ตามมาใน$(document).ready()หรือ$(window).load()? ขอดูตัวอย่างได้ไหม
John Hartsock

1
ตอนนี้ @AmandaMyer ละเว้นคำแนะนำทั้งหมดเกี่ยวกับการใช้เอกสารพร้อมเนื่องจากคุณกำลังทำเช่นนั้นอยู่แล้ว แต่ฉันพนันได้เลยว่ามันเป็นประเภทที่ทำให้พวกเขาไม่โหลดพร้อมกัน
ซานเดอร์

2
ลองเปลี่ยนtype="text/Scripts"เป็นtype="text/javascript"หรือกำจัดมันทั้งหมดที่ไม่จำเป็นอีกต่อไปและกำจัดไฟล์language="javascript. อาจเริ่มลองทำสิ่งต่างๆเช่นกัน
แจ็ค

คำตอบ:


11

แก้ไข

คุณสามารถลองประเภทที่ถูกต้องสำหรับแท็กสคริปต์ของคุณได้ไหม ฉันเห็นคุณใช้text/Scriptsซึ่งไม่ใช่ mimetype ที่ถูกต้องสำหรับ javascript

ใช้สิ่งนี้:

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

สิ้นสุดการแก้ไข

หรือคุณสามารถดูที่require.jsซึ่งเป็นตัวโหลดสำหรับโค้ดจาวาสคริปต์ของคุณ

ทั้งนี้ขึ้นอยู่กับโครงการของคุณอย่างไรก็ตามสิ่งนี้อาจเกินความจำเป็นเล็กน้อย


205

ไปปาร์ตี้ช้าและคล้ายกับคำถามของ Briguy37 แต่สำหรับการอ้างอิงในอนาคตฉันใช้วิธีการต่อไปนี้และส่งผ่านในฟังก์ชั่นที่ฉันต้องการเลื่อนออกไปจนกว่า jQuery จะโหลด:

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

มันจะเรียกเมธอด defer ซ้ำ ๆ ทุกๆ 50ms จนกว่าจะwindow.jQueryมีอยู่ในเวลาที่มันออกและโทรmethod()

ตัวอย่างที่มีฟังก์ชันไม่ระบุตัวตน:

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

2
สิ่งนี้ไม่ได้ผลสำหรับฉัน ข้างในของ method ไม่ได้กำหนด $ ... ไม่แน่ใจว่าทำไม
Aaron Lifshin

นี่เป็นทางออกที่ดีในการทำให้โค้ดที่กำหนดเองทำงานในไซต์ที่ออกแบบโดยบุคคลอื่นใน Adobe Muse
Buggabill

@gidim มันจะไม่เป็นเพราะมันเป็นตัวจับเวลาและไม่ใช่การวนซ้ำ แต่จะยังคงทำงานต่อไปตราบเท่าที่ผู้ใช้ยังคงอยู่ในหน้านั้น
Micros

การตรวจสอบลำดับการโหลดสคริปต์จะดีกว่า ฉันพบว่าหากscriptแท็กที่โหลด jQuery มีdeferแอตทริบิวต์จะทำให้เกิดปัญหาโดยการไม่โหลดจนกว่าในภายหลังแม้จะมีลำดับสคริปต์ที่กำหนดรหัส
ADTC

19

คุณสามารถใช้แอตทริบิวต์ defer เพื่อโหลดสคริปต์ในตอนท้ายจริงๆ

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

แต่โดยปกติการโหลดสคริปต์ของคุณตามลำดับที่ถูกต้องควรทำเคล็ดลับดังนั้นอย่าลืมใส่ jquery ไว้ก่อนสคริปต์ของคุณเอง

หากโค้ดของคุณอยู่ในเพจและไม่ได้อยู่ในไฟล์ js แยกต่างหากดังนั้นคุณต้องเรียกใช้สคริปต์ของคุณหลังจากที่เอกสารพร้อมและการห่อโค้ดของคุณแบบนี้ก็น่าจะใช้ได้เช่นกัน:

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

สิ่งนี้ใช้ได้ตราบใดที่คุณมีการพึ่งพาเพียงครั้งเดียว
Guillaume Massé

17

อีกวิธีหนึ่งในการทำเช่นนี้แม้ว่าวิธีการเลื่อนเวลาของ Darbio จะยืดหยุ่นกว่า

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

16

วิธีที่ง่ายและปลอดภัยที่สุดคือใช้สิ่งนี้:

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

        // place your code here.

        clearInterval(waitForJQuery);
    }
}, 10);

2
โซลูชันอัจฉริยะ ขอบคุณ
Diego Fortes

13

คุณสามารถลองโหลดเหตุการณ์ มันเพิ่มขึ้นเมื่อโหลดสคริปต์ทั้งหมด:

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

แต่โปรดทราบว่าคุณสามารถแทนที่สคริปต์อื่น ๆ ที่ window.onload ใช้.


วิธีนี้ใช้งานได้ แต่คุณอาจจะเห็นเนื้อหาที่ไม่เป็นระเบียบหาก jquery ของคุณเปลี่ยนรูปลักษณ์ของหน้า
Matthew Lock

2
คุณสามารถเพิ่มตัวฟังเหตุการณ์เพื่อหลีกเลี่ยงการแทนที่สคริปต์อื่น: stackoverflow.com/a/15564394/32453 FWIW '
rogerdpack

@rogerdpack เห็นด้วยเป็นทางออกที่ดีกว่า
Nigrimmist

10

ฉันพบว่าโซลูชันที่แนะนำใช้งานได้ในขณะที่คำนึงถึงรหัสอะซิงโครนัสเท่านั้น นี่คือเวอร์ชันที่ใช้งานได้ในทั้งสองกรณี:

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

ขอขอบคุณการตรวจสอบหนึ่งครั้งเมื่อโหลดและเฉพาะเมื่อเริ่มต้นทางเลือกที่วนซ้ำทุกๆ 50ms จะดีกว่าการปล่อยให้วนซ้ำกับ setInterval เหมือนคำตอบที่ได้รับการโหวตสูงสุด ในกรณีที่ดีที่สุดคุณมีความล่าช้า 0ms แทนที่จะเป็นกรณีเฉลี่ย 25ms ของคำตอบยอดนิยม
Luc

สวยและสง่างาม - ฉันชอบวิธีที่คุณส่งผ่านloadฟังก์ชั่นนี้ แต่ก็นำกลับมาใช้ใหม่setTimeoutด้วย
Nemesarial

6

เป็นปัญหาที่พบบ่อยลองจินตนาการว่าคุณใช้โปรแกรมสร้างเทมเพลต PHP ที่ยอดเยี่ยมดังนั้นคุณจึงมีเค้าโครงพื้นฐานของคุณ:

HEADER
BODY ==> dynamic CONTENT/PAGE
FOOTER

และแน่นอนว่าคุณอ่านที่ไหนจะดีกว่าถ้าโหลด Javascript ที่ด้านล่างของหน้าดังนั้นเนื้อหาแบบไดนามิกของคุณจึงไม่รู้ว่าใครคือ jQuery (หรือ $)

นอกจากนี้คุณอ่านบางที่ก็ดีที่จะใช้ Javascript ขนาดเล็กในบรรทัดดังนั้นลองนึกดูว่าคุณต้องการ jQuery ในหน้าหนึ่ง baboom ไม่ได้กำหนด $ ไว้ (.. แต่ ^^)

ฉันชอบวิธีแก้ปัญหาที่ Facebook มอบให้

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

ดังนั้นในฐานะโปรแกรมเมอร์ที่ขี้เกียจ (ฉันควรจะพูดว่าโปรแกรมเมอร์ที่ดี ^^) คุณสามารถใช้สิ่งที่เทียบเท่าได้ (ภายในเพจของคุณ):

window.jqReady = function() {}

และเพิ่มที่ด้านล่างของเค้าโครงของคุณหลังจาก jQuery รวม

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

ขอบคุณ! นั่นคือกรณีที่ฉันกำลังเผชิญอยู่
KumZ

หรือย้ายโหลด jquery ไปด้านบน :) stackoverflow.com/a/11012073/32453
rogerdpack

5

แทนที่จะ "รอ" (ซึ่งโดยปกติจะใช้setTimeout) คุณยังสามารถใช้การกำหนดวัตถุ jQuery ในหน้าต่างเป็นตะขอในการเรียกใช้งานโค้ดของคุณที่อาศัยมัน 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

    }
  });
})();

สิ่งนี้จะมีประโยชน์อย่างคลุมเครือหาก jQuery กำหนดคุณสมบัติของหน้าต่าง แต่ดูเหมือนจะไม่?
จิม

ไม่ได้ตั้งค่าคุณสมบัติ แต่ตั้งwindow.jQueryเป็นค่าโดยกำหนดjQueryตัวแปรในขอบเขตส่วนกลาง (เช่นwindow) สิ่งนี้จะทริกเกอร์ฟังก์ชัน setter คุณสมบัติบนออบเจ็กต์หน้าต่างที่กำหนดไว้ในโค้ด
Protector one

สิ่งนี้จะใช้ไม่ได้ถ้าคุณสมบัติ jQuery ได้รับการกำหนดก่อนที่จะเรียกใช้โค้ดนี้ เช่นถ้า jquery.js รอการตัดบัญชีของคุณโหลดก่อนที่โค้ดนี้จะถูกโหลด ทางออกหนึ่งคือใส่ jquery.js ที่รอการตัดบัญชีของคุณไว้ก่อน</body>แทนที่จะเป็นก่อน</head>
user36388

@user: เพียงรันโค้ดก่อนที่ jQuery (รอการตัดบัญชีหรืออื่น ๆ ) จะถูกโหลด ไม่สำคัญว่าคุณจะโหลดที่ไหนเพียงแค่ส่วนของรหัสของฉันมาก่อน
Protector one

1
สิ่งนี้มีประโยชน์มากหากคุณสามารถรับประกันได้ว่าสคริปต์ที่ใช้สิ่งนี้มาก่อนการรวม jQuery ในเอกสารของคุณ ไม่มีความไร้ประสิทธิภาพหรือสถานการณ์กรณีการแข่งขันจากแนวทางการวนซ้ำข้างต้นอย่างใดอย่างหนึ่ง
RedYetiCo

4

ใช้:

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

ดูข้อมูลเพิ่มเติมได้ที่: http://www.learningjquery.com/2006/09/introducing-document-ready

หมายเหตุ: สิ่งนี้ควรใช้ได้ตราบเท่าที่การนำเข้าสคริปต์สำหรับไลบรารี JQuery ของคุณอยู่เหนือการเรียกนี้

อัปเดต:

หากด้วยเหตุผลบางประการโค้ดของคุณไม่โหลดพร้อมกัน (ซึ่งฉันไม่เคยเจอมาก่อน แต่ดูเหมือนว่าอาจเป็นไปได้จากความคิดเห็นด้านล่างไม่ควรเกิดขึ้น) คุณสามารถเขียนโค้ดดังต่อไปนี้

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
รอนี้จนกว่า dom จะพร้อมยังไม่โหลด jquery เขาอาจมีไฟล์สคริปต์ 2 ไฟล์ 1 ไฟล์จาก CDN (jquery จาก google และปลั๊กอินในเครื่อง) ซึ่ง 1 ถูกโหลดก่อนหน้าอื่น .... โค้ดของคุณไม่สามารถแก้ปัญหานี้ได้หากปลั๊กอินโหลดเร็วกว่า jquery เอง
Sander

2
ฉันไม่เห็นด้วยกับคุณ @Sander สิ่งนี้ควรใช้งานได้เนื่องจากการโหลดเป็นแบบซิงโครนัส
malko

คุณพูดถูกข้อสันนิษฐานของฉันเกี่ยวกับการโหลดแบบอะซิงโครนัสหากมาจากโดเมนอื่นนั้นผิดอย่างสิ้นเชิง! ขอโทษสำหรับสิ่งนั้น
Sander

สิ่งนี้จะทำให้เกิดข้อยกเว้นหากไม่มี jquery ทำให้ไม่เข้าสู่คำสั่งอื่นที่ดูเหมือน เดาว่าน่าจะเป็น try and catch หรืออะไรสักอย่าง
Sam Stoelinga

@SamStoelinga: ขอบคุณตอนนี้ควรได้รับการแก้ไขแล้ว
Briguy37

3

ฉันไม่ค่อยชอบช่วงเวลาเท่าไหร่ เมื่อฉันต้องการเลื่อน jquery หรืออะไรก็ตามมันมักจะเป็นแบบนี้

เริ่มกับ:

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

จากนั้น:

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

สุดท้ายแล้ว:

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

หรือรุ่นที่น่าเหลือเชื่อน้อยกว่า:

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

และในกรณีของ async คุณสามารถเรียกใช้ฟังก์ชันพุชบน jquery onload

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

อีกทางหนึ่ง:

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]();}});

3

มาขยายความdefer()จาก Dario ให้ใช้ซ้ำได้มากขึ้น

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

ซึ่งจะเรียกใช้:

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

2

ฉันไม่คิดว่านั่นเป็นปัญหาของคุณ การโหลดสคริปต์เป็นแบบซิงโครนัสตามค่าเริ่มต้นดังนั้นเว้นแต่คุณจะใช้deferแอตทริบิวต์หรือโหลด jQuery ผ่านคำขอ AJAX อื่นปัญหาของคุณอาจคล้ายกับ 404 มากกว่าคุณสามารถแสดงมาร์กอัปของคุณและแจ้งให้เราทราบหากคุณเห็นสิ่งที่น่าสงสัยใน firebug หรือผู้ตรวจสอบเว็บ?


1

ตรวจสอบสิ่งนี้:

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

มันจะสร้างวัตถุ jQuery ปลอมขึ้นมาซึ่งช่วยให้คุณใช้เมธอด onload ของ jquery และพวกมันจะถูกเรียกใช้งานทันทีที่โหลด jquery

มันไม่สมบูรณ์แบบ

// 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);


นี่เป็นวิธีแก้ปัญหาที่ไม่ยุ่งยากสำหรับการลงทะเบียนฟังก์ชันพร้อมเอกสารขอบคุณ
zanedev

0

บันทึกสัมผัสเกี่ยวกับแนวทางที่นี่ที่โหลดการใช้งานsetTimeoutหรือsetInterval. ในกรณีดังกล่าวเป็นไปได้ว่าเมื่อเช็คของคุณทำงานอีกครั้ง DOM จะโหลดแล้วและDOMContentLoadedเหตุการณ์ของเบราว์เซอร์จะเริ่มทำงานดังนั้นคุณจึงไม่สามารถตรวจพบเหตุการณ์นั้นได้อย่างน่าเชื่อถือโดยใช้วิธีการเหล่านี้ สิ่งที่ฉันพบคือ jQuery readyยังใช้งานได้ดังนั้นคุณสามารถฝังไฟล์

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

ภายในของคุณsetTimeoutหรือsetIntervalและทุกอย่างควรทำงานได้ตามปกติ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.