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

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

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

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

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

window.onload="myFunction()";

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

<body onload="myFunction()">

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

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

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

Ответы

Ответ 1

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

<!doctype html>
<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>

Для современных браузеров (от IE9 и новее и любой версии Chrome, Firefox или Safari), если вы хотите иметь возможность реализовывать jQuery-подобный метод $(document).ready(), который можно вызывать из любого места (не беспокоясь о том, где находится вызывающий скрипт) позиционировать), вы можете просто использовать что-то вроде этого:

function docReady(fn) {
    // see if DOM is already available
    if (document.readyState === "complete" || document.readyState === "interactive") {
        // call on next available tick
        setTimeout(fn, 1);
    } else {
        document.addEventListener("DOMContentLoaded", fn);
    }
}    

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

docReady(function() {
    // DOM is loaded and ready for manipulation here
});

Если вам нужна полная кросс-браузерная совместимость (включая старые версии IE) и вы не хотите ждать 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/


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

  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 завершит свою работу, и он сохранит порядок вызова.

Ответ 2

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

Ответ 3

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

НЕ ИСПОЛЬЗУЙТЕ IIFE, подобный этому, который выполняется самостоятельно:

 Example:

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

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

Ответ 4

Протестировано в 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 или что-то вроде браузера для обработки зависимостей.

Ответ 5

У хороших людей в 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'); });

Ответ 6

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

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

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

Ответ 7

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

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

}

Или:

window.onload = main;

function main(){
    // Code. . .

}

Ответ 8

Готово

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 +

Ответ 9

Здесь вычищенная, не использующая eval-версия версия 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 без фреймворка?.

Ответ 10

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

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