Чистый JavaScript-эквивалент jQuery's $ .ready () - как вызывать функцию, когда страница / DOM готова к ней

752

Хорошо, это может быть глупый вопрос, хотя я уверен, что есть много других людей, которые задают один и тот же вопрос время от времени. Я, я просто хочу сделать 100% уверенным в этом в любом случае. С jQuery мы все знаем замечательные

$('document').ready(function(){});

Однако, скажем, я хочу запустить функцию, написанную в стандартном JavaScript, без поддержки библиотеки, и что я хочу запустить функцию, как только страница будет готова к ее обработке. Каким образом можно подойти к этому?

Я знаю, что могу:

window.onload="myFunction()";

... или я могу использовать тег body:

<body onload="myFunction()">

... или я могу даже попробовать в нижней части страницы после всего, но тег конца body или html, например:

<script type="text/javascript">
   myFunction();
</script>

Что такое кросс-браузерный (старый/новый) -комплексный метод выдачи одной или нескольких функций таким образом, как jQuery $.ready()?

  • 1
    поддержка старых браузеров хороша, но с той скоростью, с которой технологии продвигаются вперед, и люди, кажется, быстрее догоняют ее, это не на 100% необходимо, а хороший бонус, если это возможно. В целом, я пытаюсь понять, что если какой-либо из них является стандартом для всех браузеров. Они все работают? Имеет ли значение, какой я выберу? Если они все работают, что будет предложено как лучший против остальных?
  • 8
    Смотрите это: stackoverflow.com/questions/799981/…
Показать ещё 7 комментариев
Теги:

10 ответов

1066
Лучший ответ

Простейшая вещь, которую нужно сделать в отсутствие рамки, которая делает всю кросс-браузерную совместимость для вас, - это просто позвонить вашему коду в конце тела. Это выполняется быстрее, чем обработчик onload, потому что он ожидает только готовности DOM, а не для загрузки всех изображений. И это работает в каждом браузере.

<html>
<head>
</head>
<body>
Your HTML here

<script>
// self executing function here
(function() {
   // your page initialization code here
   // the DOM will be available here

})();
</script>
</body>
</html>

Если вы действительно не хотите этого делать, и вам нужна кросс-браузерная совместимость, и вы не хотите ждать window.onload, тогда вам, вероятно, стоит взглянуть на то, как реализует его инфраструктура, например jQuery $(document).ready() метод. Это справедливо связано с возможностями браузера.

Чтобы дать вам представление о том, что делает jQuery (который будет работать везде, где размещается тег script).

Если поддерживается, он пытается выполнить стандарт:

document.addEventListener('DOMContentLoaded', fn, false);

с отступлением:

window.addEventListener('load', fn, false )

или для более старых версий IE, он использует:

document.attachEvent("onreadystatechange", fn);

с отступлением:

window.attachEvent("onload", fn);

И в IE-коде есть некоторые обходные пути, которые я не совсем понимаю, но похоже, что это имеет какое-то отношение к кадрам.


Вот полная замена jQuery .ready(), написанная на простом javascript:

(function(funcName, baseObj) {
    // The public function name defaults to window.docReady
    // but you can pass in your own object and own function name and those will be used
    // if you want to put them in a different namespace
    funcName = funcName || "docReady";
    baseObj = baseObj || window;
    var readyList = [];
    var readyFired = false;
    var readyEventHandlersInstalled = false;

    // call this when the document is ready
    // this function protects itself against being called more than once
    function ready() {
        if (!readyFired) {
            // this must be set to true before we start calling callbacks
            readyFired = true;
            for (var i = 0; i < readyList.length; i++) {
                // if a callback here happens to add new ready handlers,
                // the docReady() function will see that it already fired
                // and will schedule the callback to run right after
                // this event loop finishes so all handlers will still execute
                // in order and no new ones will be added to the readyList
                // while we are processing the list
                readyList[i].fn.call(window, readyList[i].ctx);
            }
            // allow any closures held by these functions to free
            readyList = [];
        }
    }

    function readyStateChange() {
        if ( document.readyState === "complete" ) {
            ready();
        }
    }

    // This is the one public interface
    // docReady(fn, context);
    // the context argument is optional - if present, it will be passed
    // as an argument to the callback
    baseObj[funcName] = function(callback, context) {
        if (typeof callback !== "function") {
            throw new TypeError("callback for docReady(fn) must be a function");
        }
        // if ready has already fired, then just schedule the callback
        // to fire asynchronously, but right away
        if (readyFired) {
            setTimeout(function() {callback(context);}, 1);
            return;
        } else {
            // add the function and context to the list
            readyList.push({fn: callback, ctx: context});
        }
        // if document already ready to go, schedule the ready function to run
        if (document.readyState === "complete") {
            setTimeout(ready, 1);
        } else if (!readyEventHandlersInstalled) {
            // otherwise if we don't have event handlers installed, install them
            if (document.addEventListener) {
                // first choice is DOMContentLoaded event
                document.addEventListener("DOMContentLoaded", ready, false);
                // backup is window load event
                window.addEventListener("load", ready, false);
            } else {
                // must be IE
                document.attachEvent("onreadystatechange", readyStateChange);
                window.attachEvent("onload", ready);
            }
            readyEventHandlersInstalled = true;
        }
    }
})("docReady", window);

Последняя версия кода общедоступна на GitHub по адресу https://github.com/jfriend00/docReady

Использование:

// pass a function reference
docReady(fn);

// use an anonymous function
docReady(function() {
    // code here
});

// pass a function reference and a context
// the context will be passed to the function as the first argument
docReady(fn, context);

// use an anonymous function with a context
docReady(function(context) {
    // code here that can use the context argument that was passed to docReady
}, ctx);

Это было протестировано в:

IE6 and up
Firefox 3.6 and up
Chrome 14 and up
Safari 5.1 and up
Opera 11.6 and up
Multiple iOS devices
Multiple Android devices

Рабочая реализация и тестовое постель: http://jsfiddle.net/jfriend00/YfD3C/


Вот краткое описание того, как это работает:

  • Создайте IIFE (сразу вызываемое выражение функции), чтобы мы могли иметь переменные состояния, отличные от него.
  • Объявить публичную функцию docReady(fn, context)
  • Когда вызывается docReady(fn, context), проверьте, уже готов ли готовый обработчик. Если да, просто назначьте вновь добавленный обратный вызов для запуска сразу после того, как этот поток JS заканчивается с помощью setTimeout(fn, 1).
  • Если готовый обработчик еще не запущен, добавьте этот новый обратный вызов в список обратных вызовов, которые будут вызваны позже.
  • Проверьте, готов ли документ. Если это так, выполните все готовые обработчики.
  • Если мы еще не установили прослушиватели событий, чтобы узнать, когда документ будет готов, а затем установите их сейчас.
  • Если существует document.addEventListener, установите обработчики событий, используя .addEventListener() для событий "DOMContentLoaded" и "load". "Нагрузка" является резервным событием для безопасности и не требуется.
  • Если document.addEventListener не существует, установите обработчики событий, используя .attachEvent() для "onreadystatechange" и "onload" событий.
  • В событии onreadystatechange проверьте, есть ли document.readyState === "complete", и если да, вызовите функцию, чтобы запустить все готовые обработчики.
  • Во всех других обработчиках событий вызовите функцию для запуска всех готовых обработчиков.
  • В функции для вызова всех готовых обработчиков проверьте переменную состояния, чтобы увидеть, уже уволен ли мы. Если мы имеем, ничего не делаем. Если мы еще не были вызваны, перейдите через массив готовых функций и вызовите их в том порядке, в котором они были добавлены. Установите флаг, чтобы указать, что все они были вызваны, поэтому они никогда не выполняются более одного раза.
  • Очистите массив функций, чтобы можно было освободить любые закрытые блоки, которые они могут использовать.

Обработчики, зарегистрированные в docReady(), должны быть уволены в том порядке, в котором они были зарегистрированы.

Если вы вызываете docReady(fn) после того, как документ уже готов, обратный вызов будет запланирован для выполнения, как только текущий поток выполнения завершится с помощью setTimeout(fn, 1). Это позволяет вызывающему коду всегда предполагать, что они являются асинхронными обратными вызовами, которые будут вызваны позже, даже если позже это произойдет, как только текущий поток JS завершится, и он сохранит порядок вызова.

  • 0
    Не думал об этом. Будут ли старые браузеры поддерживать это? Или это более современный подход?
  • 3
    Размещение кода инициализации страницы после основного содержимого работает везде, даже в старых браузерах. Из-за возможности использования document.write() в вашем javascript и простоты, к которой это приводит при кодировании / реализации, весь javascript, который явно не помечен как отложенный или асинхронный, загружается последовательно, как он встречается на странице, и все то есть до того, как оно в файле уже выполнено или проанализировано.
Показать ещё 11 комментариев
85

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

// with jQuery 
$(document).ready(function(){ /* ... */ });

// shorter jQuery version 
$(function(){ /* ... */ });

// without jQuery (doesn't work in older IEs)
document.addEventListener('DOMContentLoaded', function(){ 
    // your code goes here
}, false);

// and here the trick (works everywhere)
function r(f){/in/.test(document.readyState)?setTimeout('r('+f+')',9):f()}
// use like
r(function(){
    alert('DOM Ready!');
});

Трюк здесь, как объяснил оригинальный автор, заключается в том, что мы проверяем свойство document.readyState, Если он содержит строку in (как в uninitialized и loading, первые два состояния готовности DOM из 5), мы установили тайм-аут и повторить проверку. В противном случае мы выполним переданную функцию.

И здесь jsFiddle для трюка, который работает во всех браузерах.

Благодаря Tutorialzine для включения этого в свою книгу.

  • 21
    Очень плохой подход, использование цикла ожидания с произвольным интервалом 9 мс и использование eval. Также проверка только / в / не имеет особого смысла.
  • 0
    @Ramswaroop Я только что сделал stackoverflow.com/a/33885930/938822
Показать ещё 8 комментариев
66

Протестировано в IE9, а также последние версии Firefox и Chrome, а также поддерживается в IE8.

document.onreadystatechange = function () {
  var state = document.readyState;
  if (state == 'interactive') {
      init();
  } else if (state == 'complete') {
      initOnCompleteLoad();
  }
}​;

Пример: http://jsfiddle.net/electricvisions/Jacck/

UPDATE - версия многократного использования

Я только что разработал следующее. Это довольно упрощенный эквивалент jQuery или Dom, готовый без обратной совместимости. Это, вероятно, нуждается в дальнейшей доработке. Протестировано в последних версиях Chrome, Firefox и IE (10/11) и должно работать в старых браузерах с комментариями. Я буду обновлять, если найду какие-либо проблемы.

window.readyHandlers = [];
window.ready = function ready(handler) {
  window.readyHandlers.push(handler);
  handleState();
};

window.handleState = function handleState () {
  if (['interactive', 'complete'].indexOf(document.readyState) > -1) {
    while(window.readyHandlers.length > 0) {
      (window.readyHandlers.shift())();
    }
  }
};

document.onreadystatechange = window.handleState;

Использование:

ready(function () {
  // your code here
});

Он написан для обработки асинхронной загрузки JS, но вы можете синхронизировать загрузку этого script сначала, если вы не уменьшаете. Я нашел это полезным в разработке.

Современные браузеры также поддерживают асинхронную загрузку скриптов, что еще больше увеличивает опыт. Поддержка async означает, что сразу несколько сценариев могут быть загружены при показе страницы. Просто следите, в зависимости от других сценариев, загружаемых асинхронно, или используйте minifier или что-то вроде браузера для обработки зависимостей.

  • 1
    Это также работает в IE8. Не будет работать в более низких версиях или любом IE в режиме совместимости. Благодарю.
  • 1
    Вы имеете в виду addEventListener @rogerdpack? attachEvent больше не поддерживается ни в одном браузере, но я понимаю вашу точку зрения. Я обновлю.
54

Если вы используете VANILLA обычный JavaScript без jQuery, то вы должны использовать (Internet Explorer 9 или новее):

document.addEventListener("DOMContentLoaded", function(event) {
    // Your code to run since DOM is loaded and ready
});

Выше соответствует эквивалент jQuery .ready:

$(document).ready(function() {
    console.log("Ready!");
});

К какой ТАКЖЕ можно было бы написать SHORTHAND, как это, какой jQuery будет запущен после того, как будет готов даже .

$(function() {
    console.log("ready!");
});

НЕ ПОДТВЕРЖДАЕТСЯ НИЖЕ (что не предназначено для готовности DOM):

НЕ используйте IFFI, который выполняется самостоятельно:

 Example:

(function() {
   // Your page initialization code here  - WRONG
   // The DOM will be available here   - WRONG
})();

Этот IFFI НЕ будет ждать загрузки DOM. (Я даже говорю о последней версии браузера Chrome!)

  • 2
    Если последний находится в теге script в самом низу вашей веб-страницы, это произойдет после загрузки всех элементов dom над ним.
  • 3
    ИСТИНА, но OP вопрос был «чистый JavaScript-эквивалент jQuery's $ .ready (), как вызвать функцию, когда страница / dom готова к этому»
Показать ещё 1 комментарий
15

У хороших людей в HubSpot есть ресурс, где вы можете найти чистые Javascript-методологии для достижения большого успеха jQuery - в том числе ready

http://youmightnotneedjquery.com/#ready

function ready(fn) {
  if (document.readyState != 'loading'){
    fn();
  } else if (document.addEventListener) {
    document.addEventListener('DOMContentLoaded', fn);
  } else {
    document.attachEvent('onreadystatechange', function() {
      if (document.readyState != 'loading')
        fn();
    });
  }
}

пример встроенного использования:

ready(function() { alert('hello'); });
  • 1
    Это для IE9 +
  • 1
    Ты прав. Это явно говорит об этом, когда вы нажимаете на ссылку. Это не означает, что это не кросс-браузер, он пытается смягчить некоторые особенности, которые все еще имеет IE9 +. Я никогда не претендовал на что-либо другое в моем ответе :)
Показать ещё 1 комментарий
6

Ваш метод (размещение script перед тегом закрывающего тела)

<script>
   myFunction()
</script>
</body>
</html>

- надежный способ поддержки старых и новых браузеров.

  • 0
    Я сказал, что это «единственный надежный путь», а не «единственный путь». Я сказал «единственный надежный способ», потому что это был единственный способ сделать это в Netscape 2.0.
  • 2
    Согласно истории изменений, вы фактически сказали, что «это единственный (один из) надежный (ые) способ поддержки старых и новых браузеров». Такой язык легко неверно истолковать как единственный способ интерпретации. Я не забираю то, что я сказал, извините.
4

Готово

function ready(fn){var d=document;(d.readyState=='loading')?d.addEventListener('DOMContentLoaded',fn):fn();}

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

ready(function(){
    //some code
});

Для кода самозапуска

(function(fn){var d=document;(d.readyState=='loading')?d.addEventListener('DOMContentLoaded',fn):fn();})(function(){

    //Some Code here
    //DOM is avaliable
    //var h1s = document.querySelector("h1");

});

Поддержка: IE9 +

3

Я не совсем уверен, что вы спрашиваете, но, может быть, это может помочь:

window.onload = function(){
    // Code. . .

}

Или:

window.onload = main;

function main(){
    // Code. . .

}
  • 3
    Это заменит любые прослушиватели событий, уже подключенные к window.onload
  • 0
    @ 99Problems-Syntaxain'tone, почему у вас есть более одного слушателя событий?
Показать ещё 1 комментарий
1

Здесь используется очищенная, неэвалевая версия версии Ram-swaroop "работает во всех браузерах":

function onReady(yourMethod) {
  var readyStateCheckInterval = setInterval(function() {
    if (document && document.readyState === 'complete') { // Or 'interactive'
      clearInterval(readyStateCheckInterval);
      yourMethod();
    }
  }, 10);
}
// use like
onReady(function() { alert('hello'); } );

Это требует дополнительных 10 мс для запуска, поэтому здесь более сложный путь, который не должен:

function onReady(yourMethod) {
  if (document.readyState === 'complete') { // Or also compare to 'interactive'
    setTimeout(yourMethod, 1); // Schedule to run immediately
  }
  else {
    readyStateCheckInterval = setInterval(function() {
      if (document.readyState === 'complete') { // Or also compare to 'interactive'
        clearInterval(readyStateCheckInterval);
        yourMethod();
      }
    }, 10);
  }
}

// Use like
onReady(function() { alert('hello'); } );

// Or
onReady(functionName);

См. также Как проверить, готова ли DOM без рамки?.

1

document.ondomcontentready=function(){} должен сделать трюк, но он не имеет полной совместимости с браузером.

Похоже, вы должны просто использовать jQuery min

  • 0
    О, я не могу с тобой согласиться. Я большой поклонник jQuery, использую его практически во всем, что связано с javascript. Тем не менее, я просто пытаюсь выучить JavaScript из ядра, так что я могу быть еще лучше с приложениями, которые разрабатываю, включая то, что я буду делать с jQuery для его поддержки.
  • 0
    тогда вы можете использовать это или window.onload, но не существует хорошего кросс-браузерного решения. Вот чтение о том, как jQuery делает это, если вам интересно: docs.jquery.com/…
Показать ещё 3 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню