$(документ). уже эквивалентно без jQuery

У меня есть скрипт, который использует $(document).ready, но он не использует ничего другого из jQuery. Я хотел бы облегчить это, удалив зависимость JQuery.

Как я могу реализовать свой собственный $(document).ready функциональность без использования JQuery? Я знаю что используя window.onload не будет таким же, как window.onload срабатывает после загрузки всех изображений, кадров и т. д.

42 ответа

Решение

Существует замена на основе стандартов,DOMContentLoaded это поддерживается более чем 98% браузеров, хотя не IE8:

document.addEventListener("DOMContentLoaded", function(event) { 
  //do work
});

Нативная функция jQuery намного сложнее, чем просто window.onload, как показано ниже.

function bindReady(){
    if ( readyBound ) return;
    readyBound = true;

    // Mozilla, Opera and webkit nightlies currently support this event
    if ( document.addEventListener ) {
        // Use the handy event callback
        document.addEventListener( "DOMContentLoaded", function(){
            document.removeEventListener( "DOMContentLoaded", arguments.callee, false );
            jQuery.ready();
        }, false );

    // If IE event model is used
    } else if ( document.attachEvent ) {
        // ensure firing before onload,
        // maybe late but safe also for iframes
        document.attachEvent("onreadystatechange", function(){
            if ( document.readyState === "complete" ) {
                document.detachEvent( "onreadystatechange", arguments.callee );
                jQuery.ready();
            }
        });

        // If IE and not an iframe
        // continually check to see if the document is ready
        if ( document.documentElement.doScroll && window == window.top ) (function(){
            if ( jQuery.isReady ) return;

            try {
                // If IE is used, use the trick by Diego Perini
                // http://javascript.nwbox.com/IEContentLoaded/
                document.documentElement.doScroll("left");
            } catch( error ) {
                setTimeout( arguments.callee, 0 );
                return;
            }

            // and execute any waiting functions
            jQuery.ready();
        })();
    }

    // A fallback to window.onload, that will always work
    jQuery.event.add( window, "load", jQuery.ready );
}

Редактировать:

Вот жизнеспособная замена для JQuery готов

function ready(callback){
    // in case the document is already rendered
    if (document.readyState!='loading') callback();
    // modern browsers
    else if (document.addEventListener) document.addEventListener('DOMContentLoaded', callback);
    // IE <= 8
    else document.attachEvent('onreadystatechange', function(){
        if (document.readyState=='complete') callback();
    });
}

ready(function(){
    // do something
});

Взято с https://plainjs.com/javascript/events/running-code-when-the-document-is-ready-15/

Еще одна хорошая функция domReady, взята с /questions/10638306/chistyij-javascript-ekvivalent-jquerys-ready-kak-vyizvat-funktsiyu-kogda-stranitsa-dom-gotova-k-etomu/10638313#10638313


Поскольку принятый ответ был очень далек от завершения, я соединил "готовую" функцию, такую ​​как jQuery.ready() основанный на источнике jQuery 1.6.2:

var ready = (function(){

    var readyList,
        DOMContentLoaded,
        class2type = {};
        class2type["[object Boolean]"] = "boolean";
        class2type["[object Number]"] = "number";
        class2type["[object String]"] = "string";
        class2type["[object Function]"] = "function";
        class2type["[object Array]"] = "array";
        class2type["[object Date]"] = "date";
        class2type["[object RegExp]"] = "regexp";
        class2type["[object Object]"] = "object";

    var ReadyObj = {
        // Is the DOM ready to be used? Set to true once it occurs.
        isReady: false,
        // A counter to track how many items to wait for before
        // the ready event fires. See #6781
        readyWait: 1,
        // Hold (or release) the ready event
        holdReady: function( hold ) {
            if ( hold ) {
                ReadyObj.readyWait++;
            } else {
                ReadyObj.ready( true );
            }
        },
        // Handle when the DOM is ready
        ready: function( wait ) {
            // Either a released hold or an DOMready/load event and not yet ready
            if ( (wait === true && !--ReadyObj.readyWait) || (wait !== true && !ReadyObj.isReady) ) {
                // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
                if ( !document.body ) {
                    return setTimeout( ReadyObj.ready, 1 );
                }

                // Remember that the DOM is ready
                ReadyObj.isReady = true;
                // If a normal DOM Ready event fired, decrement, and wait if need be
                if ( wait !== true && --ReadyObj.readyWait > 0 ) {
                    return;
                }
                // If there are functions bound, to execute
                readyList.resolveWith( document, [ ReadyObj ] );

                // Trigger any bound ready events
                //if ( ReadyObj.fn.trigger ) {
                //    ReadyObj( document ).trigger( "ready" ).unbind( "ready" );
                //}
            }
        },
        bindReady: function() {
            if ( readyList ) {
                return;
            }
            readyList = ReadyObj._Deferred();

            // Catch cases where $(document).ready() is called after the
            // browser event has already occurred.
            if ( document.readyState === "complete" ) {
                // Handle it asynchronously to allow scripts the opportunity to delay ready
                return setTimeout( ReadyObj.ready, 1 );
            }

            // Mozilla, Opera and webkit nightlies currently support this event
            if ( document.addEventListener ) {
                // Use the handy event callback
                document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
                // A fallback to window.onload, that will always work
                window.addEventListener( "load", ReadyObj.ready, false );

            // If IE event model is used
            } else if ( document.attachEvent ) {
                // ensure firing before onload,
                // maybe late but safe also for iframes
                document.attachEvent( "onreadystatechange", DOMContentLoaded );

                // A fallback to window.onload, that will always work
                window.attachEvent( "onload", ReadyObj.ready );

                // If IE and not a frame
                // continually check to see if the document is ready
                var toplevel = false;

                try {
                    toplevel = window.frameElement == null;
                } catch(e) {}

                if ( document.documentElement.doScroll && toplevel ) {
                    doScrollCheck();
                }
            }
        },
        _Deferred: function() {
            var // callbacks list
                callbacks = [],
                // stored [ context , args ]
                fired,
                // to avoid firing when already doing so
                firing,
                // flag to know if the deferred has been cancelled
                cancelled,
                // the deferred itself
                deferred  = {

                    // done( f1, f2, ...)
                    done: function() {
                        if ( !cancelled ) {
                            var args = arguments,
                                i,
                                length,
                                elem,
                                type,
                                _fired;
                            if ( fired ) {
                                _fired = fired;
                                fired = 0;
                            }
                            for ( i = 0, length = args.length; i < length; i++ ) {
                                elem = args[ i ];
                                type = ReadyObj.type( elem );
                                if ( type === "array" ) {
                                    deferred.done.apply( deferred, elem );
                                } else if ( type === "function" ) {
                                    callbacks.push( elem );
                                }
                            }
                            if ( _fired ) {
                                deferred.resolveWith( _fired[ 0 ], _fired[ 1 ] );
                            }
                        }
                        return this;
                    },

                    // resolve with given context and args
                    resolveWith: function( context, args ) {
                        if ( !cancelled && !fired && !firing ) {
                            // make sure args are available (#8421)
                            args = args || [];
                            firing = 1;
                            try {
                                while( callbacks[ 0 ] ) {
                                    callbacks.shift().apply( context, args );//shifts a callback, and applies it to document
                                }
                            }
                            finally {
                                fired = [ context, args ];
                                firing = 0;
                            }
                        }
                        return this;
                    },

                    // resolve with this as context and given arguments
                    resolve: function() {
                        deferred.resolveWith( this, arguments );
                        return this;
                    },

                    // Has this deferred been resolved?
                    isResolved: function() {
                        return !!( firing || fired );
                    },

                    // Cancel
                    cancel: function() {
                        cancelled = 1;
                        callbacks = [];
                        return this;
                    }
                };

            return deferred;
        },
        type: function( obj ) {
            return obj == null ?
                String( obj ) :
                class2type[ Object.prototype.toString.call(obj) ] || "object";
        }
    }
    // The DOM ready check for Internet Explorer
    function doScrollCheck() {
        if ( ReadyObj.isReady ) {
            return;
        }

        try {
            // If IE is used, use the trick by Diego Perini
            // http://javascript.nwbox.com/IEContentLoaded/
            document.documentElement.doScroll("left");
        } catch(e) {
            setTimeout( doScrollCheck, 1 );
            return;
        }

        // and execute any waiting functions
        ReadyObj.ready();
    }
    // Cleanup functions for the document ready method
    if ( document.addEventListener ) {
        DOMContentLoaded = function() {
            document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
            ReadyObj.ready();
        };

    } else if ( document.attachEvent ) {
        DOMContentLoaded = function() {
            // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
            if ( document.readyState === "complete" ) {
                document.detachEvent( "onreadystatechange", DOMContentLoaded );
                ReadyObj.ready();
            }
        };
    }
    function ready( fn ) {
        // Attach the listeners
        ReadyObj.bindReady();

        var type = ReadyObj.type( fn );

        // Add the callback
        readyList.done( fn );//readyList is result of _Deferred()
    }
    return ready;
})();

Как пользоваться:

<script>
    ready(function(){
        alert('It works!');
    });
    ready(function(){
        alert('Also works!');
    });
</script>

Я не уверен, насколько функционален этот код, но он отлично работал с моими поверхностными тестами. Это заняло довольно много времени, поэтому я надеюсь, что вы и другие можете извлечь из этого пользу.

PS: предлагаю компилировать.

Или вы можете использовать http://dustindiaz.com/smallest-domready-ever:

function r(f){/in/.test(document.readyState)?setTimeout(r,9,f):f()}
r(function(){/*code to run*/});

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

document.addEventListener('DOMContentLoaded',function(){/*fun code to run*/})

Три варианта:

  1. Если script является последним тегом тела, DOM будет готов перед выполнением тега скрипта
  2. Когда DOM готов, "readyState" изменится на "complete"
  3. Поместите все в обработчик событий 'DOMContentLoaded'

onreadystatechange

  document.onreadystatechange = function () {
     if (document.readyState == "complete") {
     // document is ready. Do your stuff here
   }
 }

Источник: MDN

DOMContentLoaded

document.addEventListener('DOMContentLoaded', function() {
   console.log('document is ready. I can sleep now');
});

Обеспокоены браузерами каменного века: перейдите к исходному коду jQuery и используйте ready функция. В этом случае вы не анализируете + не выполняете всю библиотеку, вы делаете только очень маленькую ее часть.

Поместите свой <script>/*JavaScript code*/</script> прямо перед закрытием </body> тег.

Следует признать, что это может не подходить для всех, поскольку требует изменения файла HTML, а не просто выполнения чего-либо в файле JavaScript. document.ready, но до сих пор...

Бедное мужское решение:

var checkLoad = function() {   
    document.readyState !== "complete" ? setTimeout(checkLoad, 11) : alert("loaded!");   
};  

checkLoad();  

Посмотреть скрипку

Добавил этот, немного лучше, я думаю, собственный объем и не рекурсивный

(function(){
    var tId = setInterval(function() {
        if (document.readyState == "complete") onComplete()
    }, 11);
    function onComplete(){
        clearInterval(tId);    
        alert("loaded!");    
    };
})()

Посмотреть скрипку

Я использую это:

document.addEventListener("DOMContentLoaded", function(event) { 
    //Do work
});

Примечание. Вероятно, это работает только в новых браузерах, особенно в следующих: http://caniuse.com/

Это 2020 год и <script> тег имеет defer атрибут.

например:

<script src="demo_defer.js" defer></script>

он указывает, что сценарий выполняется, когда страница завершает синтаксический анализ.

https://www.w3schools.com/tags/att_script_defer.asp

версия 2022 года

В 2022 году все, что вам нужно сделать, это добавить атрибут defer к вашему сценарию и загрузить его в голове!

Ссылка: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-defer

      <!doctype html>
<html>
<head>
  <script src="/script.js" defer></script>
</head>
<body>

 </p>In 2022, all you need to do is put the defer attribute on your script, and load it in the head!</p>

</body>
</html>

Действительно, если вы заботитесь только о Internet Explorer 9+, этого кода будет достаточно для замены jQuery.ready:

    document.addEventListener("DOMContentLoaded", callback);

Если вы беспокоитесь об Internet Explorer 6 и некоторых действительно странных и редких браузерах, это будет работать:

domReady: function (callback) {
    // Mozilla, Opera and WebKit
    if (document.addEventListener) {
        document.addEventListener("DOMContentLoaded", callback, false);
        // If Internet Explorer, the event model is used
    } else if (document.attachEvent) {
        document.attachEvent("onreadystatechange", function() {
            if (document.readyState === "complete" ) {
                callback();
            }
        });
        // A fallback to window.onload, that will always work
    } else {
        var oldOnload = window.onload;
        window.onload = function () {
            oldOnload && oldOnload();
            callback();
        }
    }
},

Этот вопрос был задан довольно давно. Для тех, кто только видит этот вопрос, теперь есть сайт под названием "вам может не понадобиться jquery", который разбит - по уровню требуемой поддержки IE - все функции jquery и предоставляют некоторые альтернативные, более мелкие библиотеки.

Скрипт, готовый для IE8, в соответствии с вашими требованиями, может не потребовать jquery

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

Кросс-браузер (тоже старые браузеры) и простое решение:

var docLoaded = setInterval(function () {
    if(document.readyState !== "complete") return;
    clearInterval(docLoaded);

    /*
        Your code goes here i.e. init()
    */
}, 30);

Отображение оповещения в jsfiddle

Я недавно использовал это для мобильного сайта. Это упрощенная версия Джона Ресига из "Техники Pro JavaScript". Это зависит от addEvent.

var ready = ( function () {
  function ready( f ) {
    if( ready.done ) return f();

    if( ready.timer ) {
      ready.ready.push(f);
    } else {
      addEvent( window, "load", isDOMReady );
      ready.ready = [ f ];
      ready.timer = setInterval(isDOMReady, 13);
    }
  };

  function isDOMReady() {
    if( ready.done ) return false;

    if( document && document.getElementsByTagName && document.getElementById && document.body ) {
      clearInterval( ready.timer );
      ready.timer = null;
      for( var i = 0; i < ready.ready.length; i++ ) {
        ready.ready[i]();
      }
      ready.ready = null;
      ready.done = true;
    }
  }

  return ready;
})();

Ответ jQuery был довольно полезен для меня. С небольшим заводом это соответствовало моим потребностям хорошо. Надеюсь, это кому-нибудь еще поможет.

function onReady ( callback ){
    var addListener = document.addEventListener || document.attachEvent,
        removeListener =  document.removeEventListener || document.detachEvent
        eventName = document.addEventListener ? "DOMContentLoaded" : "onreadystatechange"

    addListener.call(document, eventName, function(){
        removeListener( eventName, arguments.callee, false )
        callback()
    }, false )
}

Вот самый маленький фрагмент кода для проверки готовности DOM, который работает во всех браузерах (даже в IE 8):

r(function(){
    alert('DOM Ready!');
});
function r(f){/in/.test(document.readyState)?setTimeout('r('+f+')',9):f()}

Смотрите этот ответ.

Просто добавьте это внизу вашей HTML-страницы...

<script>
    Your_Function();
</script>

Потому что документы HTML анализируются сверху вниз.

Самый минимальный и 100% рабочий

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


Эта функция эквивалентна jQuery $(document).ready() метод:

document.addEventListener('DOMContentLoaded', function(){
    // do something
});

Однако, в отличие от jQuery, этот код будет правильно работать только в современных браузерах (IE > 8) и не будет работать в том случае, если документ уже отображается на момент вставки этого скрипта (например, через Ajax). Поэтому нам нужно немного расширить это:

function run() {
    // do something
}

// in case the document is already rendered
if (document.readyState!='loading') run();
// modern browsers
else if (document.addEventListener) 
document.addEventListener('DOMContentLoaded', run);
// IE <= 8
else document.attachEvent('onreadystatechange', function(){
    if (document.readyState=='complete') run();
});

Это покрывает практически все возможности и является жизнеспособной заменой помощника jQuery.

Самый простой способ с использованием чистого JavaScript. Без jQuery:

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

Стоит поискать в Rock Solid addEvent() и http://www.braksator.com/how-to-make-your-own-jquery.

Вот код на случай, если сайт не работает

function addEvent(obj, type, fn) {
    if (obj.addEventListener) {
        obj.addEventListener(type, fn, false);
        EventCache.add(obj, type, fn);
    }
    else if (obj.attachEvent) {
        obj["e"+type+fn] = fn;
        obj[type+fn] = function() { obj["e"+type+fn]( window.event ); }
        obj.attachEvent( "on"+type, obj[type+fn] );
        EventCache.add(obj, type, fn);
    }
    else {
        obj["on"+type] = obj["e"+type+fn];
    }
}

var EventCache = function(){
    var listEvents = [];
    return {
        listEvents : listEvents,
        add : function(node, sEventName, fHandler){
            listEvents.push(arguments);
        },
        flush : function(){
            var i, item;
            for(i = listEvents.length - 1; i >= 0; i = i - 1){
                item = listEvents[i];
                if(item[0].removeEventListener){
                    item[0].removeEventListener(item[1], item[2], item[3]);
                };
                if(item[1].substring(0, 2) != "on"){
                    item[1] = "on" + item[1];
                };
                if(item[0].detachEvent){
                    item[0].detachEvent(item[1], item[2]);
                };
                item[0][item[1]] = null;
            };
        }
    };
}();

// Usage
addEvent(window, 'unload', EventCache.flush);
addEvent(window, 'load', function(){alert("I'm ready");});

Всегда полезно использовать эквиваленты JavaScript по сравнению с jQuery. Одной из причин является то, что от библиотек зависит меньше библиотек, и они работают намного быстрее, чем эквиваленты jQuery.

Одна из фантастических ссылок на jQuery-эквиваленты - http://youmightnotneedjquery.com/.

Что касается вашего вопроса, я взял приведенный ниже код по ссылке выше:) Единственное предостережение: он работает только с Internet Explorer 9 и более поздними версиями.

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

Этот кросс-браузерный код вызовет функцию, как только DOM будет готов:

var domReady=function(func){
    var scriptText='('+func+')();';
    var scriptElement=document.createElement('script');
    scriptElement.innerText=scriptText;
    document.body.appendChild(scriptElement);
};

Вот как это работает:

  1. Первая строка domReady вызывает toString метод функции, чтобы получить строковое представление передаваемой функции и обернуть его в выражение, которое немедленно вызывает функцию.
  2. Остальная часть domReady создает элемент сценария с выражением и добавляет его к body документа.
  3. Браузер запускает теги скрипта, добавленные к body после того, как DOM готов.

Например, если вы делаете это: domReady(function(){alert();});следующее будет добавлено к body элемент:

 <script>(function (){alert();})();</script>

Обратите внимание, что это работает только для пользовательских функций. Следующее не будет работать: domReady(alert);

В настоящее время вы должны использовать модули. Поместите свой код в функцию модуля по умолчанию и импортируйте функцию в элемент скрипта.

client.js:

export default function ()
{
  alert ("test");
}

index.html:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>test</title>
  </head>
  <body>
    <script type="module">
      import main from './client.js';
      main ();
    </script>
  </body>
</html>

Я просто использую:

setTimeout(function(){
    //reference/manipulate DOM here
});

И в отличие от document.addEventListener("DOMContentLoaded" //etc как и в самом верхнем ответе, он работает еще в IE9 - http://caniuse.com/ указывает только на IE11.

Например, перейдите на https://netrenderer.com/index.php, выберите Internet Explorer 9 из раскрывающегося списка, введите https://dexygen.github.io/blog/oct-2017/jekyll/jekyll-categories/liquid-templates/2017/10/22/how-jekyll-builds-site-categories.html и нажмите "Render", и вы увидите что-то похожее на скриншот внизу этого поста.

Посмотрите следующий код Javascript, который я использую в заголовке, чтобы манипулировать стилем темы "хакер" Jekyll по моему вкусу - в частности, вы можете сослаться на if (location.pathname !== rootPath) блок, чтобы увидеть, как я вставляю Home а также Blog Home ссылки, которые отображаются IE9 на сайте NetRenderer.

Интересно я наткнулся на это setTimeout Решение 2009 года: проверяется ли готовность DOM к перерасходу? что, вероятно, можно было бы сформулировать немного лучше, как я имел в виду, используя более сложные подходы различных структур.

setTimeout(function() {//delay execution until after dom is parsed
    var containerEls = document.getElementsByClassName('container');
    var headingEl = containerEls[0].getElementsByTagName('h1')[0];
    var headerEl = document.getElementsByTagName('header')[0];
    var downloadsSectionEl = document.getElementById('downloads');
    var rootPath = "/";
    var blogRootPath = "/blog/";

    containerEls[0].style.maxWidth = '800px';
    containerEls[1].style.maxWidth = '800px';
    headingEl.style.margin = '0';
    headerEl.style.marginBottom = '7px';
    downloadsSectionEl.style.margin = '0';

    if (location.pathname !== rootPath) {
        downloadsSectionEl.appendChild(generateNavLink('Home', rootPath));
        if (location.pathname !== blogRootPath) {
            downloadsSectionEl.appendChild(document.createTextNode(' | '));
            downloadsSectionEl.appendChild(generateNavLink('Blog Home', blogRootPath));
        }
    }

    function generateNavLink(linkText, hrefPath) {
        var navLink = document.createElement('a');
        var linkTextNode = document.createTextNode(linkText);
        navLink.setAttribute('href', hrefPath);
        navLink.appendChild(linkTextNode);
        return navLink;
    }
});

dexygen.github.io на IE9

Мы нашли простую кросс-браузерную реализацию, которая может помочь в большинстве простых случаев с минимальной реализацией:

window.onReady = function onReady(fn){
    document.body ? fn() : setTimeout(function(){ onReady(fn);},50);
};

Как насчет этого решения?

// other onload attached earlier
window.onload=function() {
   alert('test');
};

tmpPreviousFunction=window.onload ? window.onload : null;

// our onload function
window.onload=function() {
   alert('another message');

   // execute previous one
   if (tmpPreviousFunction) tmpPreviousFunction();
};

Сравнение

Здесь (во фрагменте ниже) сравниваются выбранные доступные "встроенные" методы браузера и последовательность их выполнения. Замечания

  • то document.onload(X) не поддерживается ни одним современным браузером (событие никогда не запускается)
  • если вы используете <body onload="bodyOnLoad()"> (F) и одновременно window.onload (E) тогда будет выполнен только первый (потому что он перекрывает второй)
  • обработчик событий, указанный в <body onload="..."> (F) обернут дополнительным onload функция
  • document.onreadystatechange (D) не отменять document .addEventListener('readystatechange'...) (C) вероятно cecasue onXYZevent-like методы независимы, чем addEventListenerочереди (что позволяет добавлять несколько слушателей). Вероятно, между выполнением этих двух обработчиков ничего не происходит.
  • все сценарии записывают свою метку времени в консоли, но сценарии, которые также имеют доступ к div пишите их временные метки также в теле (щелкните ссылку "Полная страница" после выполнения скрипта, чтобы увидеть ее).
  • решения readystatechange (C,D) выполняются браузером несколько раз, но для разных состояний документа:
  • loading - документ загружается (в сниппете не запускается)
  • интерактивный - документ анализируется, запускается передDOMContentLoaded
  • Complete - документ и ресурсы загружаются, запускаются передbody/window onload

<html>

<head>
  <script>
    // solution A
    console.log(`[timestamp: ${Date.now()}] A: Head script`) ;
    
    // solution B
    document.addEventListener("DOMContentLoaded", () => {
      print(`[timestamp: ${Date.now()}] B: DOMContentLoaded`);
    });

    // solution C
    document.addEventListener('readystatechange', () => {
      print(`[timestamp: ${Date.now()}] C: ReadyState: ${document.readyState}`);
    });
   
    // solution D
    document.onreadystatechange = s=> {print(`[timestamp: ${Date.now()}] D: document.onreadystatechange ReadyState: ${document.readyState}`)};
    
    // solution E (never executed)
    window.onload = () => {
      print(`E: <body onload="..."> override this handler`);
    };

    // solution F
    function bodyOnLoad() {
      print(`[timestamp: ${Date.now()}] F: <body onload='...'>`);      
      infoAboutOnLoad(); // additional info
    }
    
    // solution X
    document.onload = () => {print(`document.onload is never fired`)};



    // HELPERS

    function print(txt) { 
      console.log(txt);
      if(mydiv) mydiv.innerHTML += txt.replace('<','&lt;').replace('>','&gt;') + '<br>';
    }
    
    function infoAboutOnLoad() {
      console.log("window.onload (after  override):", (''+document.body.onload).replace(/\s+/g,' '));
      console.log(`body.onload==window.onload --> ${document.body.onload==window.onload}`);
    }
            
    console.log("window.onload (before override):", (''+document.body.onload).replace(/\s+/g,' '));

  </script>
</head>

<body onload="bodyOnLoad()">
  <div id="mydiv"></div>

  <!-- this script must te at the bottom of <body> -->
  <script>
    // solution G
    print(`[timestamp: ${Date.now()}] G: <body> bottom script`);
  </script>
</body>

</html>

Вот то, что я использую, это быстро и охватывает все основы, я думаю; работает для всего, кроме IE<9.

(() => { function fn() {
    // "On document ready" commands:
    console.log(document.readyState);
};  
  if (document.readyState != 'loading') {fn()}
  else {document.addEventListener('DOMContentLoaded', fn)}
})();

Это похоже на все случаи:

  • срабатывает немедленно, если DOM уже готов (если DOM не "загружается", а "интерактивен" или "завершен")
  • если DOM все еще загружается, он устанавливает прослушиватель событий, когда DOM доступен (интерактивно).

Событие DOMContentLoaded доступно в IE9 и во всем остальном, поэтому я лично считаю, что это нормально, использовать это. Перепишите объявление функции стрелки в обычную анонимную функцию, если вы не переносите свой код из ES2015 в ES5.

Если вы хотите дождаться загрузки всех ресурсов, отображения всех изображений и т. Д., Используйте вместо этого window.onload.

Если вам не нужно поддерживать очень старые браузеры, вот способ сделать это, даже если ваш внешний скрипт загружен с атрибутом async:

HTMLDocument.prototype.ready = new Promise(function(resolve) {
   if(document.readyState != "loading")
      resolve();
   else
      document.addEventListener("DOMContentLoaded", function() {
         resolve();
      });
});

document.ready.then(function() {
   console.log("document.ready");
});

Представленные здесь решения setTimeout/setInterval будут работать только при определенных обстоятельствах.

Проблема проявляется особенно в старых версиях Internet Explorer до 8.

Переменные, влияющие на успех этих решений setTimeout/setInterval:

1) dynamic or static HTML
2) cached or non cached requests
3) size of the complete HTML document
4) chunked or non chunked transfer encoding

оригинальный (нативный Javascript) код для решения этой конкретной проблемы находится здесь:

https://github.com/dperini/ContentLoaded
http://javascript.nwbox.com/ContentLoaded (test)

это код, из которого команда jQuery создала свою реализацию.

Для IE9+:

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

Работает во всех известных браузерах (проверено через BrowserStack). IE6+, Safari 1+, Chrome 1+, Opera и т. Д. ИспользуетDOMContentLoaded, с откатами на document.documentElement.doScroll() а также window.onload.

/*! https://github.com/Kithraya/DOMContentLoaded v1.2.6 | MIT License */

DOMContentLoaded.version = "1.2.6";

function DOMContentLoaded() { "use strict";
    
    var ael = 'addEventListener', rel = 'removeEventListener', aev = 'attachEvent', dev = 'detachEvent';
    var alreadyRun = false, // for use in the idempotent function ready()
        funcs = arguments;
    
    // old versions of JS return '[object Object]' for null.
    function type(obj) { return (obj === null) ? 'null' : Object.prototype.toString.call(obj).slice(8,-1).toLowerCase() }
    function microtime() { return + new Date() } 
    
     /* document.readyState === 'complete' reports correctly in every browser I have tested, including IE.
        But IE6 to 10 don't return the correct readyState values as per the spec:
        readyState is sometimes 'interactive', even when the DOM isn't accessible in IE6/7 so checking for the onreadystatechange event like jQuery does is not optimal
        readyState is complete at basically the same time as 'window.onload' (they're functionally equivalent, within a few tenths of a second)
        Accessing undefined properties of a defined object (document) will not throw an error (in case readyState is undefined).
     */
    
    // Check for IE < 11 via conditional compilation
    /// values: 5?: IE5, 5.5?: IE5.5, 5.6/5.7: IE6/7, 5.8: IE8, 9: IE9, 10: IE10, 11*: (IE11 older doc mode), undefined: IE11 / NOT IE
    var jscript_version = Number( new Function("/*@cc_on return @_jscript_version; @*\/")() ) || NaN;
    
    // check if the DOM has already loaded
    if (document.readyState === 'complete') { ready(null); return; }  // here we send null as the readyTime, since we don't know when the DOM became ready.
    
    if (jscript_version < 9) { doIEScrollCheck(); return; } // For IE<9 poll document.documentElement.doScroll(), no further actions are needed.
    
     /* 
        Chrome, Edge, Firefox, IE9+, Opera 9+, Safari 3.1+, Android Webview, Chrome for Android, Edge Mobile, 
        Firefox for Android 4+, Opera for Android, iOS Safari, Samsung Internet, etc, support addEventListener
        And IE9+ supports 'DOMContentLoaded' 
     */
        
    if (document[ael]) {
        document[ael]("DOMContentLoaded", ready, false); 
        window[ael]("load", ready, false); // fallback to the load event in case addEventListener is supported, but not DOMContentLoaded
    } else 
    if (aev in window) { window[aev]('onload', ready);
        /* Old Opera has a default of window.attachEvent being falsy, so we use the in operator instead
           https://dev.opera.com/blog/window-event-attachevent-detachevent-script-onreadystatechange/

           Honestly if somebody is using a browser so outdated AND obscure (like Opera 7 where neither addEventListener 
           nor "DOMContLoaded" is supported, they deserve to wait for the full page).
           I CBA testing whether readyState === 'interactive' is truly interactive in browsers designed in 2003. I just assume it isn't (like in IE6-8). 
        */
    } else { // fallback to queue window.onload that will always work
       addOnload(ready);
    }
    
    
    // This function allows us to preserve any original window.onload handlers (in super old browsers where this is even necessary), 
    // while keeping the option to chain onloads, and dequeue them.
    
    function addOnload(fn) { var prev = window.onload; // old window.onload, which could be set by this function, or elsewhere
        
        // we add a function queue list to allow for dequeueing 
        // addOnload.queue is the queue of functions that we will run when when the DOM is ready
        if ( type( addOnload.queue ) !== 'array') { addOnload.queue = [];
            if ( type(prev) === 'function') { addOnload.queue.push( prev ); } // add the previously defined event handler
        }
        
        if (typeof fn === 'function') { addOnload.queue.push(fn) }

        window.onload = function() { // iterate through the queued functions
            for (var i = 0; i < addOnload.queue.length; i++) { addOnload.queue[i]() } 
        };
    }   

    // remove a queued window.onload function from the chain (simplified); 
    
    function dequeueOnload(fn) { var q = addOnload.queue, i = 0;
    
        // sort through the queued functions in addOnload.queue until we find `fn`
        if (type( q ) === 'array') {        // if found, remove from the queue
            for (; i < q.length; i++) { ;;(fn === q[i]) ? q.splice(i, 1) : 0; } // void( (fn === q[i]) ? q.splice(i, 1) : 0 ) 
        }
    }
    
    function ready(ev) { // idempotent event handler function
        if (alreadyRun) {return} alreadyRun = true; 
        
        // this time is when the DOM has loaded (or if all else fails, when it was actually possible to inference the DOM has loaded via a 'load' event)
        // perhaps this should be `null` if we have to inference readyTime via a 'load' event, but this functionality is better.
        var readyTime = microtime(); 
        
        detach(); // detach any event handlers
                        
        // run the functions
        for (var i=0; i < funcs.length; i++) {  var func = funcs[i];
            
            if (type(func) === 'function') {
                func.call(document, { 'readyTime': (ev === null ? null : readyTime), 'funcExecuteTime': microtime() }, func); 
                // jquery calls 'ready' with `this` being set to document, so we'll do the same. 
            }       
        }
    }

    function detach() {
        if (document[rel]) { 
            document[rel]("DOMContentLoaded", ready); window[rel]("load", ready);
        } else
        if (dev in window) { window[dev]("onload", ready); } 
        else {
            dequeueOnload(ready);
        }                                                               
    }
    
    function doIEScrollCheck() { // for use in IE < 9 only.
        if ( window.frameElement ) { 
            // we're in an <iframe> or similar
            // the document.documentElemeent.doScroll technique does not work if we're not at the top-level (parent document)

            try { window.attachEvent("onload", ready); } catch (e) { } // attach to onload if were in an <iframe> in IE as there's no way to tell otherwise
            
            return;
        } 
        try {
            document.documentElement.doScroll('left');  // when this statement no longer throws, the DOM is accessible in old IE
        } catch(error) {
            setTimeout(function() {
                (document.readyState === 'complete') ? ready() : doIEScrollCheck();
            }, 50);
            return;
        }
        ready();
    }
}

Применение:

<script>
DOMContentLoaded(function(e) { console.log(e) });
</script>
Другие вопросы по тегам