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

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

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

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

Я знаю, что могу сделать:

window.onload="myFunction()";

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

<body onload="myFunction()">

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

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

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

10 ответов

Решение

Самое простое, что можно сделать в отсутствие фреймворка, который обеспечивает всю кросс-браузерную совместимость, - это просто вызвать вызов кода в конце тела. Это быстрее выполнить, чем 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's .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/


Вот краткое изложение того, как это работает:

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

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

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

Если вы используете простой JavaScript VANILLA без 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 готов):

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

 Example:

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

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

Я хотел бы упомянуть некоторые из возможных способов здесь вместе с чистым трюком 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's 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 для хитрости, которая работает во всех браузерах.

Спасибо http://tutorialzine.com/ за включение этого в их книгу.

Протестировано в 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/

ОБНОВЛЕНИЕ - многоразовая версия

Я только что разработал следующее. Это довольно упрощенный эквивалент 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, но вы можете сначала синхронизировать загрузку этого скрипта, если вы не минифицируете. Я нашел это полезным в разработке.

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

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

http://youmightnotneedjquery.com/

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

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

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

}

Или же:

window.onload = main;

function main(){
    // Code. . .

}

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

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

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

готовы

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+

Вот исправленная версия Ram-swaroop "работает во всех браузерах", которая не использует eval - работает во всех браузерах!

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 без фреймворка?,

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

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

Другие вопросы по тегам