Bagaimana membuat eksekusi skrip menunggu sampai jquery dimuat

106

Saya mengalami masalah saat halaman dimuat dengan sangat cepat, sehingga jquery belum selesai memuat sebelum dipanggil oleh skrip berikutnya. Adakah cara untuk mengecek keberadaan jquery dan jika tidak ada, tunggu sebentar lalu coba lagi?


Menanggapi jawaban / komentar di bawah ini, saya memposting beberapa markup.

Situasi ... asp.net masterpage dan childpage.

Di halaman master, saya memiliki referensi ke jquery. Kemudian di halaman konten, saya memiliki referensi ke skrip khusus halaman. Ketika script khusus halaman sedang dimuat, ia mengeluh bahwa "$ tidak ditentukan".

Saya menempatkan peringatan di beberapa titik di markup untuk melihat urutan pengaktifan, dan mengonfirmasi bahwa pengaktifan dalam urutan ini:

  1. Header halaman master.
  2. Blok konten halaman anak 1 (terletak di dalam kepala halaman master, tetapi setelah skrip halaman master dipanggil).
  3. Blok konten halaman anak 2.

Berikut adalah markup di bagian atas halaman master:

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

Kemudian di badan halaman master, ada ContentPlaceHolder tambahan:

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

Di halaman anak, terlihat seperti ini:

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

Berikut adalah konten dari file "../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();
    });

});
Amanda Kitson
sumber
4
yang Anda gunakan $(document).ready(function(){...});?
rownage
Jika Anda memuat skrip dengan <script src="...">tag sederhana , itu tidak dapat terjadi. Apakah Anda menggunakan sesuatu seperti RequireJS atau LABjs?
Pointy
apakah Anda menjalankan skrip berikutnya di $(document).ready()atau $(window).load()? Bisakah kita melihat contohnya?
John Hartsock
1
@AmandaMyer sekarang, abaikan semua saran tentang penggunaan dokumen siap, karena Anda sudah melakukannya, tetapi saya yakin itu adalah mimetype yang menyebabkan mereka tidak memuat secara sinkron
Sander
2
Coba ubah type="text/Scripts"menjadi type="text/javascript", atau singkirkan semuanya bersama-sama yang tidak diperlukan lagi, juga singkirkan language="javascript. Sebaiknya mulai mencoba sesuatu.
Jack

Jawaban:

11

edit

Bisakah Anda mencoba jenis yang benar untuk tag skrip Anda? Saya melihat Anda menggunakan text/Scripts, yang bukan mimetype yang tepat untuk javascript.

Gunakan ini:

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

akhir edit

atau Anda bisa melihat require.js yang merupakan loader untuk kode javascript Anda.

tergantung pada proyek Anda, ini bisa jadi sedikit berlebihan

Sander
sumber
205

Terlambat ke pesta, dan mirip dengan pertanyaan Briguy37, tetapi untuk referensi di masa mendatang saya menggunakan metode berikut dan meneruskan fungsi yang ingin saya tunda hingga jQuery dimuat:

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

Ini akan secara rekursif memanggil metode defer setiap 50ms sampai window.jQueryada saat itu keluar dan memanggilmethod()

Contoh dengan fungsi anonim:

defer(function () {
    alert("jQuery is now loaded");
});
Darbio
sumber
2
Ini tidak berhasil untuk saya. Di dalam metode, $ belum ditentukan ... belum yakin mengapa.
Aaron Lifshin
Ini adalah solusi yang bagus untuk mendapatkan beberapa kode khusus yang berfungsi di situs yang dirancang oleh orang lain di Adobe Muse.
Buggabill
@ gidim Ini tidak akan, karena ini adalah timer dan bukan loop. Tapi itu akan terus berjalan selama pengguna tetap berada di halaman tersebut.
Micros
Akan lebih baik untuk menyelidiki urutan pemuatan skrip. Saya menemukan bahwa jika scripttag yang memuat jQuery memiliki deferatribut, itu akan menyebabkan masalah dengan tidak memuat sampai nanti, terlepas dari urutan skrip yang ditentukan kode.
ADTC
19

Anda dapat menggunakan atribut defer untuk memuat skrip di bagian akhir.

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

tetapi biasanya memuat skrip Anda dalam urutan yang benar harus melakukan trik, jadi pastikan untuk menempatkan penyertaan jquery sebelum skrip Anda sendiri

Jika kode Anda ada di halaman dan tidak dalam file js yang terpisah sehingga Anda harus mengeksekusi skrip Anda hanya setelah dokumen siap dan mengenkapsulasi kode Anda seperti ini seharusnya juga berfungsi:

$(function(){
//here goes your code
});
malko
sumber
Ini tidak masalah selama Anda memiliki ketergantungan tunggal
Guillaume Massé
17

Cara lain untuk melakukan ini, meskipun metode penundaan Darbio lebih fleksibel.

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

cara termudah dan teraman adalah dengan menggunakan sesuatu seperti ini:

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

        // place your code here.

        clearInterval(waitForJQuery);
    }
}, 10);
moisrex.dll
sumber
2
Solusi jenius. Terima kasih
Diego Fortes
13

Anda dapat mencoba acara onload. Itu muncul ketika semua skrip telah dimuat:

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

Namun perlu diingat, bahwa Anda dapat menimpa skrip lain yang menggunakan window.onload.

Nigrimmist
sumber
ini berfungsi tetapi Anda mungkin akan melihat sekilas konten tanpa gaya jika jquery Anda mengubah tampilan halaman
Matthew Lock
1
Anda dapat menambahkan event listener untuk menghindari menimpa skrip lain: stackoverflow.com/a/15564394/32453 FWIW '
rogerdpack
@rogerdpack setuju, ini adalah solusi yang lebih baik.
Nigrimmist
10

Saya telah menemukan bahwa solusi yang disarankan hanya berfungsi sambil memperhatikan kode asynchronous. Berikut adalah versi yang akan berfungsi dalam kedua kasus tersebut:

document.addEventListener('DOMContentLoaded', function load() {
    if (!window.jQuery) return setTimeout(load, 50);
    //your synchronous or asynchronous jQuery-related code
}, false);
Annarfych
sumber
Terima kasih, memeriksa sekali saat memuat dan hanya saat fallback mulai mengulang setiap 50 ms jauh lebih baik daripada hanya membiarkannya berputar dengan setInterval seperti jawaban pilihan teratas. Dalam kasus terbaik Anda memiliki penundaan 0ms, bukan kasus rata-rata 25ms dari jawaban teratas.
Luc
Bagus dan elegan - Saya suka cara Anda meneruskan loadfungsinya tetapi juga menggunakannya kembali setTimeout.
Nemesarial
6

Ini masalah umum, bayangkan Anda menggunakan mesin templating PHP yang keren, jadi Anda memiliki tata letak dasar:

HEADER
BODY ==> dynamic CONTENT/PAGE
FOOTER

Dan tentu saja, Anda membaca di suatu tempat lebih baik memuat Javascript di bagian bawah halaman, sehingga konten dinamis Anda tidak tahu siapa itu jQuery (atau $).

Juga Anda membaca di suatu tempat itu baik untuk Javascript kecil sebaris, jadi bayangkan Anda membutuhkan jQuery di halaman, baboom, $ belum ditentukan (.. yet ^^).

Saya menyukai solusi yang disediakan Facebook

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

Jadi, sebagai pemrogram yang malas (menurut saya programmer yang baik ^^), Anda dapat menggunakan padanan (di dalam halaman Anda):

window.jqReady = function() {}

Dan tambahkan di bagian bawah tata letak Anda, setelah jQuery menyertakan

if (window.hasOwnProperty('jqReady')) $(function() {window.jqReady();});
Thomas Decaux
sumber
Terima kasih! Itulah kasus yang saya hadapi.
KumZ
Atau pindahkan beban jquery ke atas :) stackoverflow.com/a/11012073/32453
rogerdpack
5

Daripada "menunggu" (yang biasanya dilakukan dengan menggunakan setTimeout), Anda juga dapat menggunakan definisi objek jQuery di jendela itu sendiri sebagai pengait untuk mengeksekusi kode Anda yang bergantung padanya. Ini dapat dicapai melalui definisi properti, yang ditentukan menggunakan 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

    }
  });
})();
Pelindung satu
sumber
ini akan berguna secara samar jika jQuery benar-benar mendefinisikan properti jendela, tetapi sepertinya tidak?
Jim
Itu tidak benar-benar mengatur properti, tetapi mengatur window.jQueryke nilai, dengan mendefinisikan jQueryvariabel dalam lingkup global (yaitu window). Ini akan memicu fungsi penyetel properti pada objek jendela yang ditentukan dalam kode.
Pelindung satu
Ini tidak berfungsi jika properti jQuery ditentukan sebelum kode ini dijalankan; yaitu jika jquery.js Anda yang ditangguhkan dimuat sebelum kode ini dimuat. Salah satu solusinya adalah menempatkan jquery.js yang ditangguhkan sebelumnya, </body>bukan sebelumnya</head>
user36388
@user: Cukup jalankan kode sebelum jQuery (ditangguhkan atau sebaliknya) dimuat. Tidak masalah di mana Anda memuatnya, hanya saja fragmen kode saya muncul sebelumnya.
Pelindung satu
1
Ini sangat berguna jika Anda dapat menjamin bahwa skrip yang menggunakan ini datang sebelum penyertaan jQuery dalam dokumen Anda. Tak satu pun dari inefisiensi atau skenario kasus balapan dari pendekatan perulangan di atas.
RedYetiCo
4

Menggunakan:

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

Lihat ini untuk info lebih lanjut: http://www.learningjquery.com/2006/09/introducing-document-ready

Catatan: Ini seharusnya berfungsi selama impor skrip untuk perpustakaan JQuery Anda di atas panggilan ini.

Memperbarui:

Jika karena alasan tertentu kode Anda tidak memuat secara serempak (yang belum pernah saya temui, tetapi tampaknya mungkin dari komentar di bawah ini tidak terjadi), Anda dapat mengkodekannya seperti berikut.

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();
Briguy37
sumber
6
ini tunggu sampai dom siap, bukan sampai jquery dimuat. dia mungkin memiliki 2 file skrip, 1 dari CDN (jquery dari google dan plugin secara lokal) di mana 1 dimuat lebih awal dari yang lain .... kode Anda tidak menyelesaikan ini, jika plugin dimuat lebih cepat dari jquery itu sendiri
Sander
2
saya tidak setuju dengan Anda @Sander ini harus berfungsi karena pemuatan sinkron
malko
Anda benar, asumsi saya tentang file tersebut dimuat secara asinkron jika berasal dari domain yang berbeda sepenuhnya salah! permisi untuk itu
Sander
Ini akan memunculkan pengecualian jika jquery tidak tersedia menyebabkannya tidak pernah masuk ke pernyataan lain yang terlihat. Kira itu harus mencoba dan menangkap atau sesuatu.
Sam Stoelinga
@ SamStoelinga: Terima kasih, ini sekarang harus diperbaiki.
Briguy37
3

Saya tidak terlalu menyukai hal-hal interval. Ketika saya ingin menunda jquery, atau apapun sebenarnya, biasanya berjalan seperti ini.

Dimulai dari:

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

Kemudian:

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

Lalu akhirnya:

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

Atau versi yang tidak terlalu membingungkan:

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

Dan dalam kasus asinkron, Anda dapat menjalankan fungsi yang didorong pada jquery onload.

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

Kalau tidak:

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

Saya tidak berpikir itu masalah Anda. Pemuatan skrip sinkron secara default, jadi kecuali Anda menggunakan deferatribut atau memuat jQuery itu sendiri melalui permintaan AJAX lain, masalah Anda mungkin lebih seperti 404. Dapatkah Anda menunjukkan markup Anda, dan memberi tahu kami jika Anda melihat sesuatu yang mencurigakan di firebug atau inspektur web?

jmar777
sumber
2

Mari kembangkan defer()dari Dario menjadi lebih dapat digunakan kembali.

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

Yang kemudian dijalankan:

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

Periksa ini:

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

Ini akan membuat objek jQuery palsu, yang memungkinkan Anda untuk menggunakan metode onload jquery, dan mereka akan dieksekusi segera setelah jquery dimuat.

Itu tidak sempurna.

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

Arnold Roa
sumber
ini adalah solusi yang tidak merepotkan untuk mendaftarkan fungsi siap dokumen terima kasih
zanedev
0

Catatan tangensial tentang pendekatan di sini yang menggunakan beban setTimeoutatau setInterval. Dalam kasus tersebut, mungkin saja saat pemeriksaan Anda dijalankan lagi, DOM sudah dimuat, dan peristiwa browser DOMContentLoadedakan diaktifkan, sehingga Anda tidak dapat mendeteksi peristiwa tersebut dengan andal menggunakan pendekatan ini. Apa yang saya temukan adalah bahwa jQuery readymasih berfungsi, jadi Anda dapat menyematkan seperti biasa

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

di dalam Anda setTimeoutatau setIntervaldan semuanya harus bekerja seperti biasa.

Richard J.
sumber