Питання $ (document). вже еквівалент без jQuery


У мене є скрипт, який використовує $(document).ready, але він не використовує щось інше з jQuery. Я хотів би його полегшити, видаливши залежність jQuery.

Як я можу реалізувати своє власне $(document).ready функціональність без використання jQuery? Я знаю, що використовую window.onload не буде таким же, як window.onload пожежі після завантаження всіх зображень, кадрів тощо.


1627
2018-04-28 21:51


походження


<body onload = "yourFunc ()">, безумовно, найлегший;) - cgp
... а також, безумовно, не однакова функціональність. - Joel Mueller
Як ця відповідь states, якщо все, що ви хочете від jQuery, є $(document).ready, ви можете легко вирішити цю проблему, запустивши код у самому нижньому куті сторінки, а не у верхній частині сторінки. HTML5Boilerplate використовує цей точний підхід. - Blazemonger
Чому б не використовувати DOMContentLoaded? Це IE9 + caniuse.com/domcontentloaded  developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded - Brock
Я розмістив свій дзвінок на останньому документі, і це вирішить мою проблему. Коли викликається функція, все завантажується. - IgniteCoders


Відповіді:


Існує стандартна заміна,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 );
}

1053
2018-04-28 21:59



bindReady: github.com/jquery/jquery/blob/master/src/core.js - XP1
Реальна робота простою реалізацією JavaScript, якщо хтось хоче код, який він може просто впасти: stackoverflow.com/questions/9899372/... - jfriend00
Готовий код jQuery DOM спрощений: github.com/jquery/jquery/blob/master/src/core/ready.js - Jose Nobile
Я думаю, ми всі готові перейти від IE8 ...;). Дякуємо за посилання, @JoseNobile. - Con Antonakos
DOMContentLoaded не буде працювати, якщо скрипт буде завантажений пізніше. JQuery документ готовий виконувати завжди. - Jared Insel


Редагувати:

Ось життєздатна заміна для 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/


Оскільки прийнята відповідь була дуже далекою від повної, я зшив разом "готові" функції, як 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*/})

290
2017-08-13 20:52



О, Боже. Я буду замислюватися багато разів, перш ніж знімати джеміральну дієгодію наступного разу. - Johnny_D
@Johnny_D Не додавайте jQuery залежність в першу чергу = біль пішов! - Frederik Krautwald
@FrederikKrautwald незалежно від того, що говорять люди, концептуально jQuery це добре, адже DOM API дуже роздутий, багатослівний і непослідовний, я просто хочу, щоб була доступна Lite версія - Timo Huovinen
@TimoHuovinen Альтернативи: Zepto.js (9,1 Кб), Snack.js (8,1 Кб), $ dom (2,3 Кб) та 140 Медлей (0,5 Кб). Редагувати: Ви також можете поглянути на Ендер. - Frederik Krautwald
@FrederikKrautwald $ dom звучить так, як я би хотів, але не впевнений, що він відповідає вексель. Zepto також виглядає дійсно перспективним, дякую вам за поділ! - Timo Huovinen


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

  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 функція У цьому випадку ви не аналізуєте + виконуєте всю бібліотеку, яку ви виконуєте, лише дуже невелику його частину.


184
2017-09-12 22:33



Цей другий приклад набагато більш елегантний та стислий, ніж виражені відповіді. Чому це не позначено як правильний? - 0112
Ще +1 для об'єкта DOMContentLoaded це робило саме те, що я хотів. - tripleee
onreadystatechange зробив трюк для мене ... потрібно запустити який-небудь сценарій після асинхронного завантаження jquery. - Abram
Так само, як FYI, №1 не цілком правда. Цілком можливо, що сценарій в кінці сторінки буде завантажений до завершення DOM. Тому слухачі є вищими. Вони слухають, коли браузер закінчується. У кінцевому підсумку це перетинає пальці, що завантаження сценарію було повільніше, ніж може показати браузер. - Machavity
цей варіант також буде працювати, коли документ вже буде завантажений, будь ласка, оновіть свою (найкращу) відповідь, якщо зможете: if (document.readyState == 'complete') {init (); } else {document.onreadystatechange = function () {if (document.readyState == 'complete') {init (); }}} - ZPiDER


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

Треба визнати, що це може не відповідати усім цілям, оскільки для цього потрібно змінити HTML-файл, а не робити щось у файлі JavaScript a la document.ready, але все ж...


81
2017-12-07 16:46



Мені здається, що існують проблеми сумісності, наприклад, оскільки сторінка ще не готова, ви не можете зробити це в цих і тих веб-переглядачах. На жаль, я не пам'ятаю більш чітко. Тим не менш +1 для способу, який є досить близьким у 99% всіх випадків (і пропонує Yahoo!). - Boldewyn
Фактично, розміщення елемента сценарію в нижній частині сторінки є практично ідеальним рішенням. Він працює в крос-браузері та імітує документ. Тепер ідеально. Єдиний недолік полягає в тому, що це трохи нав'язливо, ніж використання деякого інтелектуального коду, вам доведеться попросити користувача скрипту, який ви створюєте, додати додатковий фрагмент сценарію, щоб викликати свою готовність або функцію init. - Stijn de Witt
@StijndeWitt - Що ви хочете сказати про необхідність викликати функцію init? Сценарій, який використовує document.ready, не потребує іншого клієнтського коду для його виклику, він самодостатній і еквівалентний тому, де код включений в кінці тіла, також може бути самодостатнім і не вимагати іншого коду, щоб називати його або. - nnnnnn
Чому б не поставити сценарій після закриваюче тіло, і до закриття </html> тег? - Charles Holbrow


Погане рішення людини:

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

Переглянути скрипку


67
2017-08-04 18:13



@ ФіліпЛанфорд Або просто покладіть його всередині setInterval і повністю зняти рекурсію. - Alex W
@ Раверен, ну, ти правий, я впевнений, що випробував це, коли я опублікував його. у будь-якому випадку, він став ще простішим, тепер функція просто називається, не обертається. - Jakob Sternberg
Це не сексуально. Ні вибач. Використання таймерів / інтервалів для виявлення речей може "працювати", але якщо ви продовжуєте програмувати подібне, будь-який великий проект, вартий його солі, буде занурення в нос. Не робіть так, як це. Зробіть це правильно. Будь ласка Цей тип коду завдає шкоди розвитку екосистеми, тому що є краще рішення, і ви знаєте це. - dudewad
Я думаю, що ця відповідь набагато ближче dustindiaz.com/smallest-domready-ever Тому я поліпшив сценарій: jsfiddle.net/iegik/PT7x9 - iegik
@ReidBlomquist Так, і це "неправильний" спосіб, і це те, що я вказую (хоча і трохи нахабно, я знаю). Ви могли б сказати, що, роблячи це неправильно, це якимось чином "допомагає" екосистемі, але проблема полягає в тому, що з великою кількістю поганого коду там, що люди приймають за "хороший" код, тому що вони не мають досвіду знати краще НЕ допомагає екосистемі, тому що тоді вони збираються прийняти цей поганий код і впровадити його в фактичне виробниче архітектурне рішення. Отже, я думаю, нам просто доведеться відрізнятись думкою про цю «помилковість». - dudewad


Я використовую це:

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

Примітка. Це, ймовірно, працює тільки з новими браузерами, особливо цими: http://caniuse.com/#feat=domcontentloaded


31
2017-12-23 19:14



IE9 і вище фактично - Pascalius


Дійсно, якщо ви турбуєтеся 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();
        }
    }
},

19
2017-11-07 07:45





Це питання було задано досить давно. Для тих, хто тільки бачить це питання, зараз є сайт з назвою "вам може не знадобитися 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();
        });
}

15
2018-02-16 14:15



Цікаво, чому 'onreadystatechange' це необхідно, а не document.attachEvent('onload', fn); - Luke


Нещодавно я використовував це для мобільного сайту. Це спрощена версія Джона Ресіга з "Pro JavaScript Techniques". Це залежить від 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;
})();

13
2017-09-22 01:29



Будьте обережні з цим кодом. Це НЕ еквівалентно $ (document) .ready. Цей код викликає зворотний виклик, коли файл document.body готовий, що не гарантує повного завантаження DOM. - Karolis


Відповідь 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 )
}

11
2017-09-05 16:56



на деяких браузерах removeListener потрібно буде викликати з документом як контекст, тобто. removeListener.call(document, ... - Ron


Перехресні браузери (також старі браузери) та просте рішення:

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

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

Показано сповіщення в jsfiddle


9
2017-11-18 21:58



За винятком випадків, коли завантаження DOM займає більше 30 мс, код не буде запускатися. - Quelklef
@Quelklef це setInterval не встановленоTimeout - Pawel
Ти маєш рацію, мій поганий. - Quelklef