Як змусити виконання сценарію чекати, поки завантажується jquery


106

У мене виникає проблема, коли сторінка завантажується так швидко, що 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();
    });

});

4
ти використовуєш $(document).ready(function(){...});?
гранд

Якщо ви завантажуєте сценарії простими <script src="...">тегами, цього не може статися. Ви використовуєте щось на зразок RequireJS або LABjs?
Pointy

чи виконується наступний скрипт у $(document).ready()або $(window).load()? Чи можемо ми побачити приклад?
Джон Харцок

1
@AmandaMyer зараз ігноруйте всі пропозиції щодо використання документа готові, оскільки ви вже робите це, але я думаю, що саме міметик змушує їх не завантажуватися синхронно
Sander

2
Спробуйте перейти type="text/Scripts"на type="text/javascript"або позбутися цього разом, це більше не потрібно, також позбудьтесь цього language="javascript. Можна також почати пробувати речі.
Джек

Відповіді:


11

редагувати

Чи можете ви спробувати правильний тип тегів сценарію? Я бачу, що ви використовуєте 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>

завершити редагування

або ви можете поглянути на Requ.js, який є завантажувачем вашого коду javascript.

залежно від вашого проекту, однак це може бути трохи надмірним


205

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

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

Він буде рекурсивно викликати метод відкладання кожні 50 мс, поки не window.jQueryіснує, в який час він виходить і викликаєmethod()

Приклад з анонімною функцією:

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

2
Це для мене не вийшло. Всередині методу $ не було визначено ... не знаю, чому ще.
Аарон Ліфшин

Це було приємним рішенням отримати якийсь спеціальний код, який працює на сайті, який розробив хтось інший в Adobe Muse.
Buggabill

@gidim Це не стане, тому що це таймер, а не цикл. Але він буде тривати до тих пір, поки користувач залишиться на сторінці.
Мікрос

Краще було б дослідити порядок завантаження сценаріїв. Я виявив, що якщо scriptтег, що завантажує jQuery, мав deferатрибут, це спричинило б проблему, не завантажуючи його пізніше, незважаючи на визначений кодом порядок сценаріїв.
ADTC

19

ви можете використовувати атрибут defer, щоб завантажити сценарій насправді в кінці.

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

але зазвичай завантаження сценарію у правильному порядку повинно зробити свою справу, тому обов'язково розмістіть jquery перед своїм власним сценарієм

Якщо ваш код знаходиться на сторінці, а не в окремому js-файлі, тому вам доведеться виконувати свій скрипт лише після того, як документ буде готовий і інкапсульований ваш код так, як це має працювати:

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

Це нормально, якщо у вас єдина залежність
Гійом Масме

17

Ще один спосіб зробити це, хоча метод відстрочки Дарбіо є більш гнучким.

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

16

найпростіший і безпечний спосіб - використовувати щось подібне:

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

        // place your code here.

        clearInterval(waitForJQuery);
    }
}, 10);

2
Геніальне рішення. Дякую
Дієго Фортес

13

Ви можете спробувати подія завантаження. Він піднімався, коли всі сценарії завантажені:

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

Але майте на увазі, що ви можете змінювати інші сценарії, де window.onload використовується.


це працює, але ви, мабуть, побачите спалах невтішного контенту, якщо ваш jquery змінить зовнішній вигляд сторінки
Matthew Lock

1
Ви можете додати прослуховувач подій , щоб уникнути перевизначення інших сценаріїв: stackoverflow.com/a/15564394/32453 FWIW »
rogerdpack

@rogerdpack згоден, це краще рішення.
Nigrimmist

10

Я виявив, що запропоноване рішення працює лише під час врахування асинхронного коду. Ось версія, яка працюватиме в будь-якому випадку:

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

Дякую, перевірити один раз на завантаження і тільки тоді, коли резервний початок циклу кожні 50 мс набагато краще, ніж просто пускати його на цикл із setInterval, як відповідь вгорі. У кращому випадку у вас затримка 0мс, а не середній випадок 25мс головної відповіді.
Люк

Приємно та елегантно - мені подобається, як ви передаєте loadфункцію, але потім також повторно її використовуєте setTimeout.
Nemesarial

6

Це поширена проблема, уявіть, що ви використовуєте класний двигун шаблонів PHP, тому у вас є базовий макет:

HEADER
BODY ==> dynamic CONTENT/PAGE
FOOTER

І звичайно, ви десь прочитали, що краще завантажити Javascript внизу сторінки, щоб ваш динамічний вміст не знав, хто є jQuery (або $).

Також ви десь читаєте, що добре вбудувати невеликий Javascript, тому уявіть, що вам потрібен jQuery на сторінці, baboom, $ не визначено (.. поки ^^).

Я люблю рішення, яке надає Facebook

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

Тож як ледачий програміст (я повинен сказати, хороший програміст ^^), ви можете використовувати еквівалент (на своїй сторінці):

window.jqReady = function() {}

І додайте внизу свого макета, після того, як jQuery включіть

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

Дякую! Саме з цим я стикаюся.
KumZ

Або перемістити вантаж Jquery до початку :) stackoverflow.com/a/11012073/32453
rogerdpack

5

Замість того, щоб "зачекати" (що зазвичай робиться за допомогою 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

    }
  });
})();

це було б невимушено корисно, якщо jQuery насправді визначив властивість вікна, але, здається, це не так?
Джим,

Він насправді не встановлює властивість, але встановлює window.jQueryзначення, визначаючи jQueryзмінну в глобальній області (тобто window). Це запустить функцію встановлення властивостей на об'єкті вікна, визначеному в коді.
Захисник один

Це не працює, якщо властивість jQuery буде визначено перед виконанням цього коду; тобто якщо ваш відкладений jquery.js завантажується до завантаження цього коду. Одне рішення - поставити відкладений jquery.js раніше, </body>ніж раніше</head>
user36388

@user: Просто виконайте код перед завантаженням jQuery (відкладеного чи іншого). Не має значення, куди ви його завантажуєте, тільки що перед цим постає мій фрагмент коду.
Захисник один

1
Це дуже корисно, якщо ви можете гарантувати, що сценарії, які використовують це, надходять до включення jQuery у ваш документ. Жоден із сценаріїв неефективності чи випадку з расових випадків, наведених вище, не застосовується.
RedYetiCo

4

Використання:

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

Перевірте це для отримання додаткової інформації: http://www.learningjquery.com/2006/09/introducing-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();

6
це зачекання, поки не буде готовий dom, а не завантажений jquery. він, ймовірно, має 2 файли сценаріїв, 1 з CDN (jquery від google та плагіну локально), де 1 завантажується раніше, ніж інший .... ваш код цього не вирішує, якщо плагін завантажується швидше, ніж сам jquery
Сандер

2
Я не згоден з вами @Sander, це повинно працювати, оскільки завантаження синхронне
malko

Ви маєте рацію, моє припущення про завантаження їх асинхронним, якщо вони походять з іншого домену, абсолютно неправильно! вибачте за це
Сандер

Це викине виняток, якщо jquery недоступний, що призведе до того, що він ніколи не перейде в інше, як здається. Здогадайтесь, це має бути спроба лову чи щось таке.
Сем Столінга

@SamStoelinga: Дякую, це зараз має бути виправлено.
Briguy37

3

Мені не дуже подобаються інтервальні речі. Коли я хочу відкласти 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>

І у випадку з асинхронією ви можете виконувати натиснуті функції при завантаженні 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]();}});

2

Я не думаю, що це твоя проблема. Завантаження сценарію за замовчуванням синхронічне, тому, якщо ви не використовуєте deferатрибут або не завантажуєте jQuery через інший запит AJAX, ваша проблема, мабуть, щось на кшталт 404. Чи можете ви показати свою розмітку, і повідомте нам, якщо ви бачите щось підозріле в firebug чи веб-інспектор?


2

Давайте розширимось defer()від Даріо, щоб бути більш багаторазовим.

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

1

Перевір це:

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

Це створить підроблений jQuery об'єкт, який дозволяє використовувати методи завантаження 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);



0

Тангенціальна примітка щодо підходів, які використовують навантаження setTimeoutабо setInterval. У цих випадках можливо, що коли ваш чек запуститься знову, DOM вже завантажиться, а DOMContentLoadedподія браузера буде звільнена, тому ви не зможете надійно виявити цю подію, використовуючи ці підходи. Що я знайшов, це те, що jQuery readyвсе ще працює, тому ви можете вбудовувати звичне

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

всередині вашого setTimeoutабо setIntervalі все має працювати як нормально.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.