Как заставить выполнение скрипта ждать загрузки jquery

У меня проблема, когда страница загружается так быстро, что jquery не завершил загрузку до того, как он будет вызван последующим скриптом. Есть ли способ проверить наличие jquery, и если он не существует, подождите немного, а затем повторите попытку?


В ответ на ответы / комментарии ниже я публикую часть разметки.

Ситуация ... главная страница asp.net и дочерняя страница.

На главной странице у меня есть ссылка на jquery. Затем на странице содержимого у меня есть ссылка на скрипт для конкретной страницы. Когда загружается скрипт для конкретной страницы, он жалуется, что «$ не определено».

Я разместил предупреждения в нескольких местах разметки, чтобы увидеть порядок срабатывания объектов, и подтвердил, что они срабатывают в следующем порядке:

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

});

person Amanda Kitson    schedule 20.09.2011    source источник
comment
вы используете $(document).ready(function(){...});?   -  person rownage    schedule 20.09.2011
comment
Если вы загружаете скрипты с простыми тегами <script src="...">, этого не произойдет. Вы используете что-то вроде RequireJS или LABjs?   -  person Pointy    schedule 20.09.2011
comment
выполняете ли вы последующий сценарий в $(document).ready() или $(window).load()? Можно ли увидеть пример?   -  person John Hartsock    schedule 20.09.2011
comment
Я не могу использовать $ (document) .ready, потому что он использует jquery ... Я получаю сообщение о том, что $ не определено. Я использую его, но он не работает, потому что jquery еще не загружен полностью.   -  person Amanda Kitson    schedule 20.09.2011
comment
не могли бы вы также показать нам содержимое файла javascript, который дает ошибку $ is undefined? это Dashboard.js?   -  person Sander    schedule 20.09.2011
comment
@Sander Я добавил содержимое файла Dashboard.js.   -  person Amanda Kitson    schedule 20.09.2011
comment
@AmandaMyer теперь игнорируйте все предложения об использовании готового документа, поскольку вы уже делаете это, но я уверен, что это mimetype, из-за которого они не загружаются синхронно   -  person Sander    schedule 20.09.2011
comment
Попробуйте изменить type="text/Scripts" на type="text/javascript", или избавьтесь от всего этого вместе, оно больше не нужно, также избавьтесь от language="javascript. С таким же успехом можно начать пробовать.   -  person Jack    schedule 20.09.2011
comment
Да, это сработало ... большое спасибо, ребята!   -  person Amanda Kitson    schedule 20.09.2011


Ответы (15)


изменить

Не могли бы вы попробовать правильный тип для ваших тегов скрипта? Я вижу, вы используете text/Scripts, который не подходит для 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, который является загрузчиком вашего кода javascript.

в зависимости от вашего проекта, это может быть немного излишним

person Sander    schedule 20.09.2011

Поздно к вечеринке и аналогично вопросу Briguy37, но для справки в будущем я использую следующий метод и передаю функции, которые я хочу отложить до загрузки jQuery:

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

Он будет рекурсивно вызывать метод defer каждые 50 мс, пока не будет window.jQuery, когда он завершится и вызовет method()

Пример с анонимной функцией:

defer(function () {
    alert("jQuery is now loaded");
});
person Darbio    schedule 29.07.2013
comment
У меня это не сработало. Внутри метода $ не было определено ... пока не знаю, почему. - person Aaron Lifshin; 05.05.2015
comment
Это было отличное решение для того, чтобы заставить некоторый пользовательский код работать на сайте, который был разработан кем-то другим в Adobe Muse. - person Buggabill; 19.08.2016
comment
@gidim Не будет, потому что это таймер, а не цикл. Но он будет работать, пока пользователь остается на странице. - person Micros; 31.05.2017
comment
Было бы лучше разобраться с порядком загрузки скриптов. Я обнаружил, что если у тега script, который загружает jQuery, был атрибут defer, это вызвало бы проблему, не загрузив его позже, несмотря на определенный в коде порядок скриптов. - person ADTC; 21.01.2018
comment
нет ли риска переполнения стека, если библиотека по какой-то причине не загружается? - person polvoazul; 12.05.2021

вы можете использовать атрибут defer, чтобы загрузить скрипт в самом конце.

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

но обычно загрузка вашего скрипта в правильном порядке должна помочь, поэтому обязательно поместите включение jquery перед своим собственным скриптом

Если ваш код находится на странице, а не в отдельном js-файле, вы должны выполнить свой скрипт только после того, как документ будет готов, и инкапсуляция вашего кода, как это, тоже должно работать:

$(function(){
//here goes your code
});
person malko    schedule 20.09.2011
comment
Это нормально, если у вас есть единственная зависимость - person Guillaume Massé; 10.08.2012

Еще один способ сделать это, хотя метод отсрочки Darbio более гибкий.

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

Самый простой и безопасный способ - использовать что-то вроде этого:

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

        // place your code here.

        clearInterval(waitForJQuery);
    }
}, 10);
person moisrex    schedule 26.03.2017
comment
Гениальное решение. Спасибо - person Diego Fortes; 24.10.2018
comment
Вы будете вознаграждены бонусными баллами, продемонстрировав, можно ли записать это как анонимную функцию. - person edwardsmarkf; 17.03.2021
comment
Это элегантное решение отлично работает и для других ситуаций загрузки javascript, помимо jquery. Спасибо за то, что поделился этим! - person edwardsmarkf; 17.03.2021

Вы можете попробовать событие onload. Он возникает, когда все скрипты загружены:

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

Но имейте в виду, что вы можете переопределить другие скрипты, в которых используется window.onload.

person Nigrimmist    schedule 20.05.2015
comment
это работает, но вы, вероятно, увидите вспышку нестилизованного контента, если ваш jquery изменит внешний вид страницы - person Matthew Lock; 23.12.2016
comment
Вы можете добавить прослушиватель событий, чтобы избежать переопределения других сценариев: stackoverflow.com/a/15564394/32453 FWIW ' - person rogerdpack; 24.03.2020
comment
@rogerdpack согласен, это лучшее решение. - person Nigrimmist; 24.03.2020
comment
по опыту, это всегда было лучшее решение, которое я использовал. - person Bishoy Hanna; 08.07.2021

Я обнаружил, что предлагаемое решение работает только с учетом асинхронного кода. Вот версия, которая подойдет в любом случае:

document.addEventListener('DOMContentLoaded', function load() {
    if (!window.jQuery) return setTimeout(load, 50);
    //your synchronous or asynchronous jQuery-related code
}, false);
person Annarfych    schedule 30.11.2017
comment
Спасибо, проверка один раз при загрузке и только в качестве резервного запуска цикла каждые 50 мс намного лучше, чем просто позволить ему зацикливаться с setInterval, как ответ, получивший наибольшее количество голосов. В лучшем случае у вас будет задержка в 0 мс вместо среднего 25 мс для верхнего ответа. - person Luc; 24.04.2019
comment
Красиво и элегантно - мне нравится, как вы передаете функцию load, но затем повторно используете ее в setTimeout. - person Nemesarial; 08.01.2020
comment
Есть ли способ исключить использование load и записать его как анонимную функцию? - person edwardsmarkf; 17.03.2021

Это обычная проблема, представьте, что вы используете классный шаблонизатор PHP, поэтому у вас есть базовый макет:

HEADER
BODY ==> dynamic CONTENT/PAGE
FOOTER

И, конечно, вы где-то читали, что лучше загружать Javascript внизу страницы, чтобы ваш динамический контент не знал, кто такой jQuery (или $).

Также вы где-то читали, что полезно встроить небольшой Javascript, поэтому представьте, что вам нужен jQuery на странице, baboom, $ не определен (.. пока ^^).

Мне нравится решение, которое предоставляет Facebook

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

Итак, как ленивый программист (я бы сказал, хороший программист ^^), вы можете использовать эквивалент (на своей странице):

window.jqReady = function() {}

И добавьте внизу вашего макета после того, как jQuery include

if (window.hasOwnProperty('jqReady')) $(function() {window.jqReady();});
person Thomas Decaux    schedule 28.08.2014
comment
Спасибо! Это именно тот случай, с которым я столкнулся. - person KumZ; 19.04.2017
comment
Или переместите загрузку jquery наверх :) stackoverflow.com/a/11012073/32453 - person rogerdpack; 24.03.2020

Вместо «ожидания» (что обычно делается с использованием 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

    }
  });
})();
person Protector one    schedule 18.05.2016
comment
это было бы весьма полезно, если бы jQuery действительно определял свойство окна, но, похоже, это не так? - person Jim; 08.06.2016
comment
На самом деле он не устанавливает свойство, но устанавливает window.jQuery в значение, определяя переменную jQuery в глобальной области (т.е. window). Это вызовет функцию установки свойств для объекта окна, определенного в коде. - person Protector one; 08.06.2016
comment
Это не сработает, если свойство jQuery определяется до выполнения этого кода; т.е. если отложенный jquery.js загружается до загрузки этого кода. Одно из решений - поместить отложенный jquery.js перед </body>, а не перед </head> - person user36388; 21.08.2017
comment
@user: просто выполните код до загрузки jQuery (отложенного или другого). Неважно, куда вы его загружаете, просто перед ним идет мой фрагмент кода. - person Protector one; 21.08.2017
comment
Это очень полезно, если вы можете гарантировать, что скрипты, использующие это, появятся до включения jQuery в ваш документ. Ни неэффективности, ни сценариев гонок из описанных выше циклов тоже нет. - person MLK.DEV; 15.01.2019

Использовать:

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

Ознакомьтесь с этим для получения дополнительной информации: http://www.learningjquery.com/2006/09/introduction-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();
person Briguy37    schedule 20.09.2011
comment
это ожидание до тех пор, пока dom не будет готов, а не до тех пор, пока не будет загружен jquery. у него, вероятно, есть 2 файла сценария, 1 из CDN (jquery из Google и плагин локально), где 1 загружается раньше, чем другой .... ваш код не решает это, если плагин загружается быстрее, чем сам jquery - person Sander; 20.09.2011
comment
я не согласен с вами @Sander, это должно работать, так как загрузка синхронная - person malko; 20.09.2011
comment
вы правы, мое предположение о том, что они загружаются асинхронно, если они происходят из другого домена, совершенно неверно! простите меня за это - person Sander; 20.09.2011
comment
Это вызовет исключение, если jquery недоступен, из-за чего он никогда не перейдет в оператор else, как кажется. Думаю, это должна быть попытка поймать или что-то в этом роде. - person Sam Stoelinga; 23.06.2012
comment
@SamStoelinga: Спасибо, теперь это должно быть исправлено. - person Briguy37; 25.06.2012
comment
@ Briguy37 вам нужно добавить вызов этой функции проверки, чтобы запустить ее, например runYourFunctionWhenJQueryIsLoaded (); после того, как это определено - person Dan Searle; 07.03.2014

Я не очень люблю интервальные штучки. Когда я хочу отложить 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 вы можете выполнять push-функции при загрузке jquery.

<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]();}});
person Simon    schedule 04.08.2017

Давайте расширим 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')});
}
person mewc    schedule 23.10.2019

Я не думаю, что это твоя проблема. По умолчанию загрузка скрипта выполняется синхронно, поэтому, если вы не используете атрибут defer или не загружаете сам jQuery через другой запрос AJAX, ваша проблема, вероятно, больше похожа на ошибку 404. Можете ли вы показать свою разметку и сообщить нам, если вы заметите что-нибудь подозрительное в firebug или веб-инспекторе?

person jmar777    schedule 20.09.2011

Проверь это:

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

person Arnold Roa    schedule 07.01.2016
comment
это простое решение для регистрации функций, готовых к работе с документами, спасибо - person zanedev; 26.02.2016

Косвенное замечание о подходах здесь, которые используют нагрузку setTimeout или setInterval. В этих случаях возможно, что при повторном запуске проверки DOM уже будет загружена, а событие DOMContentLoaded в браузере будет запущено, поэтому вы не сможете надежно обнаружить это событие, используя эти подходы. Я обнаружил, что jQuery ready все еще работает, поэтому вы можете встроить свой обычный

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

внутри вашего setTimeout или setInterval, и все должно работать как обычно.

person Richard J    schedule 29.10.2018