So lassen Sie die Skriptausführung warten, bis jquery geladen ist

106

Ich habe ein Problem, bei dem die Seite so schnell geladen wird, dass jquery noch nicht vollständig geladen wurde, bevor sie von einem nachfolgenden Skript aufgerufen wird. Gibt es eine Möglichkeit, die Existenz von jquery zu überprüfen, und wenn es nicht existiert, warten Sie einen Moment und versuchen Sie es dann erneut?


Als Antwort auf die Antworten / Kommentare unten poste ich einige der Markups.

Die Situation ... asp.net Masterseite und Kinderseite.

Auf der Masterseite habe ich einen Verweis auf jquery. Dann habe ich auf der Inhaltsseite einen Verweis auf das seitenspezifische Skript. Wenn das seitenspezifische Skript geladen wird, wird beschwert, dass "$ undefiniert ist".

Ich habe an mehreren Stellen im Markup Warnungen eingefügt, um die Reihenfolge zu sehen, in der die Dinge ausgelöst wurden, und habe bestätigt, dass sie in dieser Reihenfolge ausgelöst werden:

  1. Masterseitenkopf.
  2. Untergeordneter Seiteninhaltsblock 1 (befindet sich im Kopf der Masterseite, aber nachdem die Masterseiten-Skripte aufgerufen wurden).
  3. Untergeordneter Seiteninhaltsblock 2.

Hier ist das Markup oben auf der Masterseite:

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

Im Hauptteil der Masterseite befindet sich dann ein zusätzlicher ContentPlaceHolder:

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

Auf der untergeordneten Seite sieht es so aus:

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

Hier ist der Inhalt der Datei "../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
quelle
4
verwenden Sie $(document).ready(function(){...});?
Rownage
Wenn Sie Skripte mit einfachen <script src="...">Tags laden , kann dies nicht passieren. Verwenden Sie so etwas wie RequireJS oder LABjs?
Pointy
Führen Sie das nachfolgende Skript im $(document).ready()oder aus $(window).load()? Könnten wir ein Beispiel sehen?
John Hartsock
1
@AmandaMyer, ignorieren Sie jetzt alle Vorschläge zur Verwendung von document ready, da Sie dies bereits tun, aber ich wette, es ist der Mimetyp, der dazu führt, dass sie nicht synchron geladen werden
Sander
2
Versuchen Sie , type="text/Scripts"zu type="text/javascript", oder loszuwerden, alles zusammen ist es nicht mehr nötig, bekommen auch die loszuwerden language="javascript. Könnte auch anfangen, Dinge auszuprobieren.
Jack

Antworten:

11

bearbeiten

Könnten Sie den richtigen Typ für Ihre Skript-Tags ausprobieren? Ich sehe, dass Sie verwenden text/Scripts, was nicht der richtige Mimetyp für Javascript ist.

Benutze das:

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

Ende bearbeiten

Oder schauen Sie sich require.js an , einen Loader für Ihren Javascript-Code.

Abhängig von Ihrem Projekt kann dies jedoch etwas übertrieben sein

Sander
quelle
204

Spät zur Party und ähnlich wie Briguy37s Frage, aber zum späteren Nachschlagen verwende ich die folgende Methode und übergebe die Funktionen, die ich verschieben möchte, bis jQuery geladen ist:

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

Die Defer-Methode wird alle 50 ms rekursiv aufgerufen, bis sie window.jQueryexistiert. Zu diesem Zeitpunkt wird sie beendet und aufgerufenmethod()

Ein Beispiel mit einer anonymen Funktion:

defer(function () {
    alert("jQuery is now loaded");
});
Darbio
quelle
2
Das hat bei mir nicht funktioniert. Innerhalb der Methode wurde $ nicht definiert ... ich weiß noch nicht warum.
Aaron Lifshin
Dies war eine gute Lösung, um benutzerdefinierten Code auf einer Site zum Laufen zu bringen, die von einer anderen Person in Adobe Muse entworfen wurde.
Buggabill
@gidim Wird es nicht, weil es ein Timer und keine Schleife ist. Es wird jedoch so lange ausgeführt, wie der Benutzer auf der Seite bleibt.
Micros
Es wäre besser, die Ladereihenfolge von Skripten zu untersuchen. Ich fand heraus, dass wenn das scriptTag, das jQuery lädt, ein deferAttribut hätte, es das Problem verursachen würde, indem es trotz der vom Code definierten Reihenfolge der Skripte erst später geladen wird.
ADTC
19

Sie können das Attribut defer verwenden, um das Skript am Ende zu laden.

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

Normalerweise sollte das Laden Ihres Skripts in der richtigen Reihenfolge den Trick tun. Stellen Sie daher sicher, dass Sie die jquery-Aufnahme vor Ihrem eigenen Skript platzieren

Wenn sich Ihr Code auf der Seite und nicht in einer separaten js-Datei befindet, müssen Sie Ihr Skript erst ausführen, nachdem das Dokument fertig ist und die Kapselung Ihres Codes wie folgt funktioniert:

$(function(){
//here goes your code
});
malko
quelle
Dies ist in Ordnung, solange Sie eine einzige Abhängigkeit haben
Guillaume Massé
16

Ein weiterer Weg, dies zu tun, obwohl Darbios Aufschubmethode flexibler ist.

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

Der einfachste und sicherste Weg ist, so etwas zu verwenden:

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

        // place your code here.

        clearInterval(waitForJQuery);
    }
}, 10);
Moisrex
quelle
2
Geniale Lösung. Vielen Dank
Diego Fortes
13

Sie können das Onload-Ereignis ausprobieren. Es wird ausgelöst, wenn alle Skripte geladen wurden:

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

Beachten Sie jedoch, dass Sie möglicherweise andere Skripts überschreiben, in denen window.onload verwendet wird.

Nigrimmist
quelle
Dies funktioniert, aber Sie werden wahrscheinlich einen Blitz von nicht gestylten Inhalten sehen, wenn Ihre Abfrage das Erscheinungsbild der Seite ändert
Matthew Lock
1
Sie können einen Ereignis-Listener hinzufügen, um zu vermeiden, dass andere Skripte überschrieben werden: stackoverflow.com/a/15564394/32453 FWIW '
rogerdpack
@rogerdpack zustimmen, es ist eine bessere Lösung.
Nigrimmist
10

Ich habe festgestellt, dass die vorgeschlagene Lösung nur unter Berücksichtigung von asynchronem Code funktioniert. Hier ist die Version, die in beiden Fällen funktionieren würde:

document.addEventListener('DOMContentLoaded', function load() {
    if (!window.jQuery) return setTimeout(load, 50);
    //your synchronous or asynchronous jQuery-related code
}, false);
Annarfych
quelle
Vielen Dank, eine Überprüfung beim Laden und nur dann, wenn der Fallback alle 50 ms eine Schleife startet, ist viel besser, als sie nur mit setInterval wie die am häufigsten gewählte Antwort schleifen zu lassen. Im besten Fall haben Sie eine Verzögerung von 0 ms anstelle des durchschnittlichen Falls von 25 ms der obersten Antwort.
Luc
Schön und elegant - mir gefällt, wie Sie die loadFunktion übergeben, aber dann auch wiederverwenden setTimeout.
Nemesarial
6

Stellen Sie sich vor, Sie verwenden eine coole PHP-Template-Engine, damit Sie Ihr Basislayout haben:

HEADER
BODY ==> dynamic CONTENT/PAGE
FOOTER

Und natürlich lesen Sie irgendwo, dass es besser ist, Javascript am Ende der Seite zu laden, damit Ihr dynamischer Inhalt nicht weiß, wer jQuery (oder das $) ist.

Außerdem liest du irgendwo, wo es gut ist, kleines Javascript zu integrieren. Stell dir also vor, du brauchst jQuery auf einer Seite, baboom, $ ist nicht definiert (.. noch ^^).

Ich liebe die Lösung, die Facebook bietet

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

Als fauler Programmierer (ich sollte sagen ein guter Programmierer ^^) können Sie also ein Äquivalent (innerhalb Ihrer Seite) verwenden:

window.jqReady = function() {}

Und fügen Sie am Ende Ihres Layouts hinzu, nachdem Sie jQuery eingeschlossen haben

if (window.hasOwnProperty('jqReady')) $(function() {window.jqReady();});
Thomas Decaux
quelle
Vielen Dank! Das ist genau der Fall, vor dem ich stehe.
KumZ
Oder verschieben Sie die Abfrage nach oben :) stackoverflow.com/a/11012073/32453
rogerdpack
5

Anstatt zu warten (was normalerweise mit verwendet wird setTimeout), können Sie auch die Definition des jQuery-Objekts im Fenster selbst als Hook verwenden, um Ihren darauf basierenden Code auszuführen. Dies ist durch eine Eigenschaftsdefinition erreichbar, die mit definiert wird 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

    }
  });
})();
Beschützer eins
quelle
Dies wäre vage nützlich, wenn jQuery tatsächlich eine Fenstereigenschaft definiert hätte, aber es scheint nicht so?
Jim
Es wird nicht wirklich eine Eigenschaft festgelegt, sondern window.jQueryein Wert, indem die jQueryVariable im globalen Bereich (dh window) definiert wird. Dadurch wird die Eigenschaft zum Einstellen von Eigenschaften für das im Code definierte Fensterobjekt ausgelöst.
Beschützer ein
Dies funktioniert nicht, wenn die jQuery-Eigenschaft definiert wird, bevor dieser Code ausgeführt wird. dh wenn Ihre zurückgestellte jquery.js geladen wird, bevor dieser Code geladen wird. Eine Lösung besteht darin, Ihre zurückgestellte jquery.js vorher </body>statt vorher zu setzen</head>
user36388
@user: Führen Sie einfach den Code aus, bevor jQuery (verzögert oder anderweitig) geladen wird. Es spielt keine Rolle, wo Sie es laden, nur dass mein Codefragment davor steht.
Beschützer ein
1
Dies ist sehr nützlich, wenn Sie sicherstellen können, dass die Skripte, die dies verwenden, vor der Aufnahme von jQuery in Ihr Dokument erstellt werden. Keine der Ineffizienzen oder Race-Case-Szenarien aus den oben genannten Loop-Ansätzen.
RedYetiCo
4

Verwenden:

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

Weitere Informationen finden Sie hier: http://www.learningjquery.com/2006/09/introducing-document-ready

Hinweis: Dies sollte funktionieren, solange der Skriptimport für Ihre JQuery-Bibliothek über diesem Aufruf liegt.

Aktualisieren:

Wenn Ihr Code aus irgendeinem Grund nicht synchron geladen wird (was ich noch nie erlebt habe, aber anscheinend aus dem folgenden Kommentar möglich sein sollte, sollte dies nicht passieren), können Sie ihn wie folgt codieren.

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
quelle
6
Dieses Warten dauert, bis der Dom bereit ist, und nicht, bis die Abfrage geladen ist. er hat wahrscheinlich 2 - Skriptdateien, 1 von einem CDN (jquery von Google und ein Plugin lokal) , wo die 1 früher als die anderen geladen wird .... Ihr Code führt dies nicht zu lösen, wenn das Plugin geladen wird mich schneller als jquery
Sander
2
Ich bin nicht einverstanden mit Ihnen @Sander dies sollte funktionieren, da das Laden synchron ist
Malko
Sie haben Recht, meine Annahme, dass sie asynchron geladen werden, wenn sie aus einer anderen Domäne stammen, ist völlig falsch! Entschuldigung dafür
Sander
Dies löst eine Ausnahme aus, wenn jquery nicht verfügbar ist, sodass es anscheinend nie in die else-Anweisung geht. Ich denke, es sollte ein Versuch sein, etwas zu fangen oder so.
Sam Stoelinga
@ SamStoelinga: Danke, das sollte jetzt behoben sein.
Briguy37
3

Ich mag die Intervall-Dinger nicht besonders. Wenn ich jquery oder irgendetwas anderes aufschieben möchte, geht es normalerweise so.

Beginnen mit:

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

Dann:

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

Dann endlich:

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

Oder die weniger umwerfende Version:

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

Und im Falle von Async können Sie die Push-Funktionen bei jquery onload ausführen.

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

Alternative:

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

Ich denke nicht, dass das dein Problem ist. Das Laden von Skripten ist standardmäßig synchron. deferWenn Sie also nicht das Attribut verwenden oder jQuery selbst über eine andere AJAX-Anforderung laden, ähnelt Ihr Problem wahrscheinlich eher einem 404. Können Sie Ihr Markup anzeigen und uns mitteilen, wenn Sie etwas Verdächtiges sehen Firebug oder Web Inspector?

jmar777
quelle
2

Lassen Sie uns defer()von Dario aus erweitern , um wiederverwendbarer zu werden.

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

Welches wird dann ausgeführt:

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

Überprüfen Sie dies:

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

Es wird ein gefälschtes jQuery-Objekt erstellt, mit dem Sie die Onload-Methoden von jquery verwenden können. Diese werden ausgeführt, sobald jquery geladen wird.

Es ist nicht perfekt.

// 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
quelle
Dies ist eine problemlose Lösung für die Registrierung von Dokumenten bereit Funktionen danke
zanedev
0

Eine tangentiale Anmerkung zu den Ansätzen hier, die Last verwenden setTimeoutoder setInterval. In diesen Fällen ist es möglich, dass bei erneuter Ausführung Ihrer Prüfung das DOM bereits geladen wurde und das DOMContentLoadedEreignis des Browsers ausgelöst wurde, sodass Sie dieses Ereignis mit diesen Ansätzen nicht zuverlässig erkennen können. Was ich gefunden habe ist, dass jQuery immer readynoch funktioniert, so dass Sie Ihr übliches einbetten können

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

in deinem setTimeoutoder setIntervalund alles sollte wie gewohnt funktionieren.

Richard J.
quelle