Come fare in modo che l'esecuzione dello script attenda fino al caricamento di jquery


106

Sto riscontrando un problema in cui la pagina viene caricata così velocemente, che jquery non ha terminato il caricamento prima di essere chiamato da uno script successivo. C'è un modo per verificare l'esistenza di jquery e se non esiste, aspetta un momento e poi riprova?


In risposta alle risposte / commenti di seguito, sto postando parte del markup.

La situazione ... asp.net masterpage e childpage.

Nella masterpage ho un riferimento a jquery. Quindi nella pagina del contenuto, ho un riferimento allo script specifico della pagina. Quando lo script specifico della pagina viene caricato, si lamenta che "$ non è definito".

Ho inserito avvisi in diversi punti del markup per vedere l'ordine in cui le cose stavano sparando e ho confermato che si attiva in questo ordine:

  1. Intestazione della pagina master.
  2. Blocco di contenuto della pagina figlio 1 (situato all'interno dell'header della pagina master, ma dopo la chiamata degli script della pagina master).
  3. Blocco del contenuto della pagina secondaria 2.

Ecco il markup nella parte superiore della pagina 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>

Quindi nel corpo della pagina master, c'è un ContentPlaceHolder aggiuntivo:

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

Nella pagina figlio, sembra così:

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

Di seguito è riportato il contenuto del 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();
    });

});

4
stai usando $(document).ready(function(){...});?
rownage

Se stai caricando script con <script src="...">tag semplici , ciò non può accadere. Stai usando qualcosa come RequireJS o LABjs?
Pointy

stai eseguendo lo script successivo in $(document).ready()o $(window).load()? Potremmo vedere un esempio?
John Hartsock,

1
@AmandaMyer ora, ignora tutti i suggerimenti sull'utilizzo del documento pronto, dal momento che lo stai già facendo, ma scommetto che è il tipo MIME che sta facendo sì che non vengano caricati in modo sincrono
Sander

2
Provare a cambiare type="text/Scripts"a type="text/javascript", o sbarazzarsi di tutto insieme non è più necessaria, anche sbarazzarsi della language="javascript. Tanto vale iniziare a provare le cose.
Jack

Risposte:


11

modificare

Potresti provare il tipo corretto per i tuoi tag di script? Vedo che usi text/Scripts, che non è il tipo MIME giusto per javascript.

Usa questo:

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

fine modifica

oppure potresti dare un'occhiata a require.js che è un caricatore per il tuo codice javascript.

a seconda del progetto, questo potrebbe tuttavia essere un po 'eccessivo


205

In ritardo alla festa e simile alla domanda di Briguy37, ma per riferimento futuro utilizzo il seguente metodo e passo le funzioni che voglio differire fino al caricamento di jQuery:

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

Chiamerà ricorsivamente il metodo defer ogni 50 ms finché non window.jQueryesiste, momento in cui esce e chiamamethod()

Un esempio con una funzione anonima:

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

2
Questo non ha funzionato per me. All'interno del metodo, $ non è stato definito ... non sono ancora sicuro del perché.
Aaron Lifshin

Questa è stata una bella soluzione per far funzionare del codice personalizzato in un sito progettato da qualcun altro in Adobe Muse.
Buggabill

@gidim Non lo farà, perché è un timer e non un loop. Ma continuerà a funzionare finché l'utente rimane sulla pagina.
Micros

Sarebbe meglio esaminare l'ordine di caricamento degli script. Ho scoperto che se il scripttag che carica jQuery avesse un deferattributo, causerebbe il problema non caricandosi più tardi, nonostante l'ordine definito dal codice degli script.
ADTC

19

puoi usare l'attributo defer per caricare lo script alla fine.

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

ma normalmente caricare lo script nell'ordine corretto dovrebbe fare il trucco, quindi assicurati di inserire l'inclusione di jquery prima del tuo script

Se il tuo codice è nella pagina e non in un file js separato, devi eseguire lo script solo dopo che il documento è pronto e anche l'incapsulamento del codice in questo modo dovrebbe funzionare:

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

Va bene finché hai una sola dipendenza
Guillaume Massé

17

Ancora un altro modo per farlo, sebbene il metodo di differimento di Darbio sia più flessibile.

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

16

il modo più semplice e sicuro è usare qualcosa del genere:

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

        // place your code here.

        clearInterval(waitForJQuery);
    }
}, 10);

2
Soluzione geniale. Grazie
Diego Fortes

13

Puoi provare onload event. Viene generato quando tutti gli script sono stati caricati:

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

Ma tieni presente che puoi sovrascrivere altri script in cui window.onload utilizza.


funziona ma probabilmente vedrai un lampo di contenuto non stilato se il tuo jquery cambia l'aspetto della pagina
Matthew Lock

1
È possibile aggiungere un listener di eventi per evitare l'override altri script: stackoverflow.com/a/15564394/32453 FWIW'
rogerdpack

@rogerdpack d'accordo, è una soluzione migliore.
Nigrimmist

10

Ho scoperto che la soluzione suggerita funziona solo tenendo conto del codice asincrono. Ecco la versione che funzionerebbe in entrambi i casi:

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

Grazie, controllare una volta al caricamento e solo quando il fallback inizia a ripetersi ogni 50 ms è molto meglio che lasciarlo andare in loop con setInterval come la risposta più votata. Nel migliore dei casi si ha un ritardo di 0 ms, invece del caso medio di 25 ms della risposta più alta.
Luc

Bello ed elegante - Mi piace come passi la loadfunzione ma poi la riutilizzi anche in setTimeout.
Nemesarial

6

È un problema comune, immagina di utilizzare un fantastico motore di modelli PHP, quindi hai il tuo layout di base:

HEADER
BODY ==> dynamic CONTENT/PAGE
FOOTER

E, naturalmente, leggi da qualche parte che è meglio caricare Javascript in fondo alla pagina, quindi il tuo contenuto dinamico non sa chi è jQuery (o $).

Inoltre, leggi da qualche parte è bene incorporare piccoli Javascript, quindi immagina di aver bisogno di jQuery in una pagina, baboom, $ non è definito (.. ancora ^^).

Adoro la soluzione fornita da Facebook

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

Quindi, come programmatore pigro (dovrei dire un buon programmatore ^^), puoi usare un equivalente (all'interno della tua pagina):

window.jqReady = function() {}

E aggiungi in fondo al layout, dopo che jQuery include

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

Grazie! Questo è esattamente il caso che sto affrontando.
KumZ

O spostare il carico jquery verso l'alto :) stackoverflow.com/a/11012073/32453
rogerdpack

5

Piuttosto che "wait" (che di solito viene fatto usando setTimeout), potresti anche usare la definizione dell'oggetto jQuery nella finestra stessa come un hook per eseguire il tuo codice che si basa su di esso. Ciò è ottenibile attraverso una definizione di proprietà, definita utilizzando 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

    }
  });
})();

questo sarebbe vagamente utile se jQuery definisse effettivamente una proprietà della finestra, ma non sembra?
Jim

Non imposta realmente una proprietà, ma imposta window.jQueryun valore, definendo la jQueryvariabile nell'ambito globale (cioè window). Ciò attiverà la funzione di impostazione delle proprietà sull'oggetto finestra definito nel codice.
Protector one

Questo non funziona se la proprietà jQuery viene definita prima che questo codice venga eseguito; cioè se il tuo jquery.js differito viene caricato prima che questo codice venga caricato. Una soluzione è inserire il tuo jquery.js differito prima </body>invece che prima</head>
user36388

@user: Esegui semplicemente il codice prima che jQuery (differito o meno) venga caricato. Non importa dove lo carichi, solo che il mio frammento di codice viene prima di esso.
Protettore uno il

1
Questo è molto utile se puoi garantire che gli script che lo utilizzano vengono prima dell'inclusione di jQuery nel tuo documento. Nessuna delle inefficienze o degli scenari di gara dagli approcci di loop di cui sopra neanche.
RedYetiCo

4

Uso:

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

Dai un'occhiata a questo per maggiori informazioni: http://www.learningjquery.com/2006/09/introducing-document-ready

Nota: questo dovrebbe funzionare fintanto che l'importazione dello script per la tua libreria JQuery si trova sopra questa chiamata.

Aggiornare:

Se per qualche motivo il tuo codice non si carica in modo sincrono (cosa che non ho mai incontrato, ma a quanto pare potrebbe essere possibile dal commento qui sotto non dovrebbe accadere), potresti codificarlo come segue.

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
questa attesa fino a quando il dom è pronto, non finché jquery non viene caricato. probabilmente ha 2 file di script, 1 da un CDN (jquery da google e un plugin in locale) dove l'1 viene caricato prima dell'altro .... il tuo codice non risolve questo problema, se il plugin viene caricato più velocemente di jquery stesso
Sander

2
non sono d'accordo con te @Sander questo dovrebbe funzionare poiché il caricamento è sincrono
malko,

hai ragione, la mia ipotesi sul fatto che vengano caricati in modo asincrono se provengono da un dominio diverso è completamente sbagliata! scusatemi per questo
Sander

Questo genererà un'eccezione se jquery non è disponibile, facendo sì che non vada mai nell'istruzione else che sembra. Immagino che dovrebbe essere una prova e cattura o qualcosa del genere.
Sam Stoelinga,

@SamStoelinga: Grazie, ora dovrebbe essere risolto.
Briguy37

3

Non mi piacciono molto le cose a intervalli. Quando voglio rimandare jquery, o qualsiasi altra cosa, di solito va qualcosa del genere.

Iniziare con:

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

Poi:

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

Poi finalmente:

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

O la versione meno sbalorditiva:

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

E nel caso di async è possibile eseguire le funzioni inviate su jquery onload.

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

In alternativa:

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

Non credo che sia il tuo problema. Il caricamento dello script è sincrono per impostazione predefinita, quindi a meno che tu non stia utilizzando l' deferattributo o caricando jQuery stesso tramite un'altra richiesta AJAX, il tuo problema è probabilmente qualcosa di più simile a un 404. Puoi mostrare il tuo markup e facci sapere se vedi qualcosa di sospetto in firebug o web inspector?


2

Espandiamoci defer()da Dario per essere più riutilizzabili.

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

Che viene quindi eseguito:

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

1

Controllare questo:

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

Creerà un falso oggetto jQuery, che ti consente di utilizzare i metodi onload di jquery, e verranno eseguiti non appena jquery viene caricato.

Non è perfetto.

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


questa è una soluzione senza problemi per la registrazione delle funzioni pronte per i documenti grazie
zanedev

0

Una nota tangenziale sugli approcci qui che caricano l'uso setTimeouto setInterval. In questi casi è possibile che quando il controllo viene eseguito di nuovo, il DOM sarà già stato caricato e l' DOMContentLoadedevento del browser sarà stato attivato, quindi non è possibile rilevare quell'evento in modo affidabile utilizzando questi approcci. Quello che ho scoperto è che jQuery readyfunziona ancora, quindi puoi incorporare il tuo solito

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

dentro il tuo setTimeouto setIntervale tutto dovrebbe funzionare normalmente.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.