Cómo hacer que la ejecución del script espere hasta que se cargue jquery

106

Tengo un problema en el que la página se carga tan rápido que jquery no ha terminado de cargarse antes de que un script posterior lo llame. ¿Hay alguna manera de verificar la existencia de jquery y, si no existe, espere un momento y vuelva a intentarlo?


En respuesta a las respuestas / comentarios a continuación, estoy publicando algunas de las marcas.

La situación ... asp.net masterpage y childpage.

En la página maestra, tengo una referencia a jquery. Luego, en la página de contenido, tengo una referencia al script específico de la página. Cuando se carga el script específico de la página, se queja de que "$ no está definido".

Puse alertas en varios puntos del marcado para ver el orden en el que se disparaban las cosas y confirmé que se dispara en este orden:

  1. Encabezado de la página maestra.
  2. Bloque de contenido de la página secundaria 1 (ubicado dentro del encabezado de la página maestra, pero después de que se llaman los scripts de la página maestra).
  3. Bloque de contenido de la página secundaria 2.

Aquí está el marcado en la parte superior de la página maestra:

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

Luego, en el cuerpo de la página maestra, hay un ContentPlaceHolder adicional:

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

En la página secundaria, se ve así:

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

Aquí está el contenido del archivo "../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
fuente
4
está usando $(document).ready(function(){...});?
Rownage
Si está cargando scripts con <script src="...">etiquetas simples , eso no puede suceder. ¿Estás usando algo como RequireJS o LABjs?
Puntiagudo
¿Está ejecutando el siguiente script en $(document).ready()o $(window).load()? ¿Podríamos ver un ejemplo?
John Hartsock
1
@AmandaMyer ahora, ignore todas las sugerencias sobre el uso de documentos listos, ya que ya lo está haciendo, pero apuesto a que es el tipo MIME lo que hace que no se carguen de forma sincrónica
Sander
2
Intente cambiar type="text/Scripts"a type="text/javascript", o deshacerse de todo junto, ya no es necesario, también deshágase del language="javascript. Bien podría empezar a probar cosas.
Jack

Respuestas:

11

editar

¿Podría probar el tipo correcto para sus etiquetas de script? Veo que usa text/Scripts, que no es el tipo MIME correcto para javascript.

Utilizar este:

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

fin editar

o puede echar un vistazo a require.js, que es un cargador para su código javascript.

dependiendo de su proyecto, esto podría ser un poco exagerado

Lijadora
fuente
205

Tarde para la fiesta, y similar a la pregunta de Briguy37, pero para referencia futura utilizo el siguiente método y paso las funciones que quiero aplazar hasta que se cargue jQuery:

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

Llamará recursivamente al método deferir cada 50 ms hasta que window.jQueryexista, momento en el que sale y llamamethod()

Un ejemplo con una función anónima:

defer(function () {
    alert("jQuery is now loaded");
});
Darbio
fuente
2
Esto no funcionó para mí. Dentro del método, $ no estaba definido ... todavía no estoy seguro de por qué.
Aaron Lifshin
Esta fue una buena solución para hacer funcionar un código personalizado en un sitio que fue diseñado por otra persona en Adobe Muse.
Buggabill
@gidim No lo hará, porque es un temporizador y no un bucle. Pero seguirá ejecutándose mientras el usuario permanezca en la página.
Micros
Sería mejor investigar el orden de carga de los scripts. Descubrí que si la scriptetiqueta que carga jQuery tenía un deferatributo, causaría el problema al no cargarse hasta más tarde, a pesar del orden definido por el código de los scripts.
ADTC
19

puede utilizar el atributo defer para cargar el script al final.

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

pero normalmente cargar su script en el orden correcto debería funcionar, así que asegúrese de colocar la inclusión de jquery antes de su propio script

Si su código está en la página y no en un archivo js separado, entonces debe ejecutar su script solo después de que el documento esté listo y encapsular su código de esta manera también debería funcionar:

$(function(){
//here goes your code
});
malko
fuente
Esto está bien siempre que tenga una sola dependencia
Guillaume Massé
17

Otra forma más de hacer esto, aunque el método de aplazamiento de Darbio es más flexible.

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

la forma más fácil y segura es usar algo como esto:

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

        // place your code here.

        clearInterval(waitForJQuery);
    }
}, 10);
moisrex
fuente
2
Solución genial. Gracias
Diego Fortes
13

Puedes probar el evento onload. Surgió cuando se cargaron todos los scripts:

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

Pero tenga en cuenta que puede anular otros scripts donde window.onload usando.

Nigrimmist
fuente
esto funciona, pero probablemente verá un destello de contenido sin estilo si su jquery cambia la apariencia de la página
Matthew Lock
1
Puede agregar un detector de eventos para evitar anular otros scripts: stackoverflow.com/a/15564394/32453 FWIW '
rogerdpack
@rogerdpack de acuerdo, es una mejor solución.
Nigrimmist
10

He descubierto que la solución sugerida solo funciona teniendo en cuenta el código asincrónico. Aquí está la versión que funcionaría en cualquier caso:

document.addEventListener('DOMContentLoaded', function load() {
    if (!window.jQuery) return setTimeout(load, 50);
    //your synchronous or asynchronous jQuery-related code
}, false);
Annarfych
fuente
Gracias, verificar una vez en carga y solo cuando el respaldo comience a repetirse cada 50 ms es mucho mejor que simplemente dejarlo en bucle con setInterval como la respuesta más votada. En el mejor de los casos, tiene un retraso de 0 ms, en lugar del caso promedio de 25 ms de la respuesta principal.
Luc
Agradable y elegante: me gusta cómo se pasa la loadfunción, pero también se reutiliza setTimeout.
Nemesarial
6

Es un problema común, imagina que usas un motor de plantillas PHP genial, por lo que tienes tu diseño base:

HEADER
BODY ==> dynamic CONTENT/PAGE
FOOTER

Y, por supuesto, si lee en algún lugar, es mejor cargar Javascript en la parte inferior de la página, para que su contenido dinámico no sepa quién es jQuery (o el $).

También leíste en alguna parte que es bueno insertar un pequeño Javascript, así que imagina que necesitas jQuery en una página, baboom, $ no está definido (.. todavía ^^).

Me encanta la solución que ofrece Facebook

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

Entonces, como programador vago (debería decir un buen programador ^^), puedes usar un equivalente (dentro de tu página):

window.jqReady = function() {}

Y agregue en la parte inferior de su diseño, después de que jQuery incluya

if (window.hasOwnProperty('jqReady')) $(function() {window.jqReady();});
Thomas Decaux
fuente
¡Gracias! Ese es exactamente el caso al que me enfrento.
KumZ
O mueva la carga de jquery a la parte superior :) stackoverflow.com/a/11012073/32453
rogerdpack
5

En lugar de "esperar" (que generalmente se hace usando setTimeout), también puede usar la definición del objeto jQuery en la propia ventana como un gancho para ejecutar su código que se basa en él. Esto se puede lograr mediante una definición de propiedad, definida mediante 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

    }
  });
})();
Protector uno
fuente
esto sería vagamente útil si jQuery realmente definiera una propiedad de ventana, pero no parece hacerlo.
Jim
Realmente no establece una propiedad, pero sí establece window.jQueryun valor, definiendo la jQueryvariable en el alcance global (es decir window). Esto activará la función de establecimiento de propiedades en el objeto de ventana definido en el código.
Protector uno
Esto no funciona si la propiedad jQuery se define antes de que se ejecute este código; es decir, si su jquery.js diferido se carga antes de que se cargue este código. Una solución es poner su jquery.js diferido antes en </body>lugar de antes</head>
user36388
@user: Simplemente ejecute el código antes de que se cargue jQuery (diferido o no). No importa dónde lo cargue, solo que mi fragmento de código venga antes.
Protector uno
1
Esto es muy útil si puede garantizar que los scripts que usan esto vengan antes de la inclusión de jQuery en su documento. Ninguna de las ineficiencias o escenarios de casos de carrera de los enfoques de bucle anteriores tampoco.
RedYetiCo
4

Utilizar:

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

Mira esto para más información: http://www.learningjquery.com/2006/09/introducing-document-ready

Nota: Esto debería funcionar siempre que la importación del script para su biblioteca JQuery esté por encima de esta llamada.

Actualizar:

Si por alguna razón su código no se carga sincrónicamente (con lo que nunca me he encontrado, pero aparentemente es posible que el comentario a continuación no suceda), puede codificarlo como se muestra a continuación.

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
fuente
6
esta espera es hasta que el dom esté listo, no hasta que se cargue jquery. probablemente tiene 2 archivos de script, 1 de un CDN (jquery de google y un complemento localmente) donde el 1 se carga antes que el otro ... su código no resuelve esto, si el complemento se carga más rápido que jquery mismo
Sander
2
No estoy de acuerdo contigo @Sander, esto debería funcionar ya que la carga es sincrónica
Malko
tiene razón, mi suposición de que se cargan de forma asincrónica si provienen de un dominio diferente es completamente incorrecta. disculpe por eso
Sander
Esto generará una excepción si jquery no está disponible, lo que hará que nunca entre en la declaración else que parece. Supongo que debería ser un intento de atrapar o algo así.
Sam Stoelinga
@SamStoelinga: Gracias, esto debería arreglarse ahora.
Briguy37
3

No me gustan mucho las cositas de los intervalos. Cuando quiero aplazar jquery, o cualquier cosa en realidad, suele ser algo como esto.

Empezar con:

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

Luego:

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

Entonces finalmente:

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

O la versión menos alucinante:

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

Y en el caso de async, podría ejecutar las funciones empujadas en jquery onload.

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

Alternativamente:

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]();}});
Simón
fuente
2

No creo que ese sea tu problema. La carga de scripts es sincrónica de forma predeterminada, por lo que a menos que esté usando el deferatributo o cargando jQuery a través de otra solicitud AJAX, su problema probablemente sea algo más como un 404. ¿Puede mostrar su marcado y hacernos saber si ve algo sospechoso en ¿Firebug o inspector web?

jmar777
fuente
2

Vamos a expandirnos defer()de Dario para que sean más reutilizables.

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

Que luego se ejecuta:

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

Mira esto:

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

Creará un objeto jQuery falso, que le permite usar los métodos onload de jquery, y se ejecutarán tan pronto como se cargue jquery.

No es perfecto

// 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
fuente
esta es una solución sencilla para registrar funciones listas para documentos gracias
zanedev
0

Una nota tangencial sobre los enfoques aquí que cargan use setTimeouto setInterval. En esos casos, es posible que cuando su verificación se ejecute nuevamente, el DOM ya se haya cargado y el DOMContentLoadedevento del navegador se haya activado, por lo que no puede detectar ese evento de manera confiable utilizando estos enfoques. Lo que encontré es que jQuery readytodavía funciona, por lo que puede incrustar su habitual

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

dentro de tu setTimeouto setIntervaly todo debería funcionar normalmente.

Richard J
fuente