$ (เอกสาร) เทียบเท่าโดยไม่มี jQuery


2016

ฉันมีสคริปต์ที่ใช้$(document).readyแต่ไม่ได้ใช้อย่างอื่นจาก jQuery ฉันต้องการทำให้เบาขึ้นโดยการลบการพึ่งพา jQuery

ฉันจะใช้$(document).readyฟังก์ชันการทำงานของตัวเองโดยไม่ใช้ jQuery ได้อย่างไร ฉันรู้ว่าการใช้งานwindow.onloadจะไม่เหมือนกันเพราะwindow.onloadไฟหลังจากโหลดภาพเฟรมและอื่น ๆ ทั้งหมดแล้ว


296
... และยังไม่ได้ทำงานเหมือนกันแน่นอน
Joel Mueller

40
ในฐานะที่เป็นคำตอบนี้รัฐถ้าสิ่งที่คุณต้องการจาก jQuery คือ$(document).readyคุณสามารถแก้ปัญหาที่ได้อย่างง่ายดายโดยใช้รหัสของคุณที่ด้านล่างสุดของหน้าแทนการที่ด้านบน HTML5Boilerplateใช้วิธีการที่แน่นอนนี้
Blazemonger

3
ทำไมไม่ลองใช้ DOMContentLoaded? มันคือ IE9 + caniuse.com/domcontentloaded developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded
Brock

ฉันโทรไปที่เอกสารล่าสุดและแก้ปัญหาของฉันได้ เมื่อเรียกใช้ฟังก์ชันทุกอย่างจะถูกโหลด
IgniteCoders

ควรค่าแก่การดู: developer.mozilla.org/en-US/docs/Web/API/Document/readyState
Andrew

คำตอบ:


1440

มีการแทนที่ตามมาตรฐาน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 );
}

19
การใช้งานจาวาสคริปต์ธรรมดาที่เกิดขึ้นจริงที่นี่หากมีคนต้องการรหัสพวกเขาก็สามารถวางใน: stackoverflow.com/questions/9899372/…
jfriend00

4
รหัส jQuery DOM พร้อมที่จะง่ายขึ้น: github.com/jquery/jquery/blob/master/src/core/ready.js
Jose Nobile

2
@JoseNobile เพราะพวกเขาละทิ้งการสนับสนุนเบราว์เซอร์รุ่นเก่า
huysentruitw

16
ฉันคิดว่าเราพร้อมที่จะก้าวต่อไปจาก IE8 ... ;) ขอบคุณสำหรับลิงค์ @JoseNobile
Con Antonakos

13
DOMContentLoaded จะไม่ทำงานหากโหลดสคริปต์หลังจากนั้น เอกสาร JQuery พร้อมเรียกใช้งานเสมอ
Jared Insel

343

แก้ไข:

นี่คือสิ่งที่ทดแทนได้สำหรับ 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 ที่ดีมาที่นี่จากhttps://stackoverflow.com/a/9899701/175071


เนื่องจากคำตอบที่ยอมรับนั้นยังห่างไกลจากความสมบูรณ์ฉันจึงประสานฟังก์ชั่น "พร้อม" เช่นเดียว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*/})

14
@TimoHuovinen ทางเลือก: Zepto.js (9.1 kb), Snack.js (8.1 kb), $ dom (2.3 kb), และ 140 Medley (0.5 kb) แก้ไข:คุณสามารถดู Ender ได้
Frederik Krautwald

2
@FrederikKrautwald $ dom ดูเหมือนสิ่งที่ฉันต้องการ แต่ไม่แน่ใจว่าเหมาะสมกับใบเสร็จหรือไม่ Zepto ดูเหมือนจะมีแนวโน้มที่ดีจริงๆขอบคุณสำหรับการแบ่งปัน!
Timo Huovinen

@TimoHuovinen หากคุณยังไม่ได้ดู Ender คุณควรดูอย่างแน่นอน enderjs.com
Frederik Krautwald

2
@Timo Huovinen: คำถามของคุณกว้างมากจริงๆ! เมื่อสร้าง jQuery ขึ้นมามันทำให้เกิดปัญหาเบราว์เซอร์ข้ามจำนวนมากที่สร้างโดยเบราว์เซอร์ที่มีความสำคัญน้อยกว่าในปัจจุบัน วันนี้ "จาวาสคริปต์เท่านั้น" ง่ายกว่าที่เคยเป็น ในเวลานี้การสร้าง "บีบอัดขนาดใหญ่ 20kb ที่มีทั้งหมด" เป็นความคิดที่ดีด้วยเหตุผลมากมายที่ฉันไม่ต้องการแสดงรายการทั้งหมด
dotpush

1
ฉันไม่ชอบสิ่งนี้ หากผู้คนต้องการคำตอบนี้ถามตัวเองว่าทำไมคุณถึงต้องการวาง jQuery ตั้งแต่แรก มันไม่มีประโยชน์อะไรเลยถ้าคุณเพียงแค่ดึงฟังก์ชั่นที่เหมือนกันกับเบราว์เซอร์สำรองทั้งหมดกลับเข้าไปในชุดรวมของคุณ นั่นเป็นจุดเริ่มต้นของการหลีกเลี่ยง jQuery หรือเปล่า?
ฟิล

208

สามตัวเลือก:

  1. หากscriptเป็นแท็กสุดท้ายของเนื้อความ DOM จะพร้อมก่อนที่แท็กสคริปต์จะทำงาน
  2. เมื่อ DOM พร้อมแล้ว "readyState" จะเปลี่ยนเป็น "สมบูรณ์"
  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ฟังก์ชัน ในกรณีนี้คุณจะไม่แยกวิเคราะห์ + ดำเนินการกับไลบรารีทั้งหมดคุณกำลังทำส่วนเล็ก ๆ ของมัน


3
ตัวอย่างที่สองนี้มีความงดงามและรวบรัดกว่าคำตอบที่ทำเครื่องหมายไว้มากมาย เหตุใดจึงไม่ทำเครื่องหมายสิ่งนี้ว่าถูกต้อง
0112

2
ยังคง +1 สิ่ง DOMContentLoaded มันทำสิ่งที่ฉันต้องการ
tripleee

1
onreadystatechange ได้ทำเคล็ดลับสำหรับฉัน ... จำเป็นต้องเรียกใช้สคริปต์หลังจากโหลด async jquery
Abram

2
ในฐานะที่เป็น FYI อันดับ 1 นั้นไม่เป็นความจริงทั้งหมด ค่อนข้างเป็นไปได้ที่สคริปต์ที่ส่วนท้ายของหน้าจะโหลดก่อนที่ DOM จะเสร็จสิ้น นั่นเป็นเหตุผลที่ผู้ฟังยอดเยี่ยม พวกเขาฟังเมื่อเสร็จเบราว์เซอร์ การวางไว้ท้ายที่สุดคือการข้ามนิ้วของคุณที่โหลดสคริปต์ช้ากว่าที่เบราว์เซอร์สามารถแสดงผลได้
Machavity

1
ตัวแปรนี้จะทำงานเมื่อเอกสารโหลดเสร็จแล้วโปรดอัปเดตคำตอบ (imo ที่ดีที่สุด) ของคุณหากคุณ: ถ้า (document.readyState == 'สมบูรณ์') {init (); } else {document.onreadystatechange = function () {ถ้า (document.readyState == 'เสร็จสมบูรณ์') {init (); }}}
ZPiDER

87

วางของคุณไว้<script>/*JavaScript code*/</script>ข้างหน้า </body>แท็กปิด

เป็นที่ยอมรับว่าอาจไม่เหมาะกับวัตถุประสงค์ของทุกคนเพราะต้องการเปลี่ยนไฟล์ HTML แทนที่จะทำอะไรบางอย่างในไฟล์ JavaScript document.readyแต่ยังคง ...


ดูเหมือนว่ามีปัญหาความเข้ากันได้เช่นเนื่องจากหน้ายังไม่พร้อมคุณไม่สามารถทำได้หรือในเบราว์เซอร์เหล่านี้และเหล่านั้น น่าเสียดายที่ฉันจำไม่ได้ชัดเจนขึ้น อย่างไรก็ตาม +1 สำหรับวิธีที่ใกล้พอใน 99% ของทุกกรณี (และแนะนำโดย Yahoo!)
Boldewyn

7
ที่จริงแล้วการวางองค์ประกอบสคริปต์ที่ด้านล่างของหน้าเป็นโซลูชั่นที่สมบูรณ์แบบเกือบ มันทำงานข้ามเบราว์เซอร์และจำลอง document.ready สมบูรณ์แบบ ข้อเสียเปรียบเพียงอย่างเดียวคือมันค่อนข้างจะเสือกกว่าการใช้สมาร์ทโค้ดเล็กน้อยคุณจะต้องถามผู้ใช้ของสคริปต์ที่คุณกำลังสร้างเพื่อเพิ่มส่วนของสคริปต์พิเศษเพื่อเรียกใช้ฟังก์ชัน Ready หรือ init ของคุณ
Stijn de Witt

@StijndeWitt - คุณหมายถึงอะไรเกี่ยวกับการเรียกฟังก์ชั่น init? สคริปต์ที่ใช้ document.ready ไม่จำเป็นต้องมีรหัสลูกค้าอื่นเพื่อเรียกมันว่ามันมีอยู่ในตัวเองและเทียบเท่ากับที่รหัสรวมอยู่ที่ส่วนท้ายของร่างกายยังสามารถอยู่ในตัวเองและไม่ ต้องใช้รหัสอื่นในการโทรด้วย
nnnnnn

1
ทำไมไม่ใส่สคริปต์หลังแท็กปิดเนื้อหาและหน้า</html>แท็กปิด?
Charles Holbrow

1
@CharlesHolbrow แม้ว่าเบราว์เซอร์ทั้งหมดจะแปลความหมายได้อย่างถูกต้องถ้าคุณอยากให้มันเป็น html ที่ถูกต้องที่htmlแท็กควรมีเฉพาะและhead body
Alvaro Montoro

66

วิธีการแก้ปัญหาของคนจน:

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

ดูซอ


8
@PhilipLangford หรือเพียงแค่ใส่ข้างในsetIntervalและลบการสอบถามซ้ำทั้งหมด
Alex W

1
@ Raveren อืมคุณพูดถูกฉันแน่ใจว่าฉันทดสอบเมื่อฉันโพสต์ อย่างไรก็ตามมันง่ายขึ้นตอนนี้ฟังก์ชั่นเพิ่งถูกเรียกไม่มีการห่อหุ้ม
Jakob Sternberg

24
มันไม่เซ็กซี่ ไม่ล่ะขอบคุณ. การใช้ตัวจับเวลา / ช่วงเวลาในการตรวจสอบสิ่งต่าง ๆ อาจ "ทำงาน" แต่ถ้าคุณทำโปรแกรมแบบนี้โครงการที่ใหญ่กว่าที่มีค่าเกลือจะไปดำน้ำที่จมูก อย่าแฮ็คข้อมูลร่วมกันเช่นนี้ ทำถูกต้องแล้ว โปรด. รหัสประเภทนี้ทำร้ายระบบนิเวศการพัฒนาเพราะมีวิธีแก้ปัญหาที่ดีกว่าและคุณก็รู้
dudewad

1
ฉันคิดว่าคำตอบนี้ใกล้กว่ามาก dustindiaz.com/smallest-domready-everมากดังนั้นฉันจึงปรับปรุงสคริปต์: jsfiddle.net/iegik/PT7x9
iegik

1
@ ReidBlomquist ใช่และนี่คือวิธีที่ "ผิด" และนั่นคือสิ่งที่ฉันชี้ให้เห็น (แม้ว่าจะค่อนข้างยืนกรานฉันรู้) คุณสามารถพูดได้ว่าโดยการทำผิดมันก็คือ "ช่วย" ระบบนิเวศ แต่ปัญหาก็คือมีจำนวนรหัสที่ไม่ดีออกมาที่นั่นผู้คนรับรหัส "ดี" เพราะพวกเขาไม่มีประสบการณ์ที่จะรู้ดีกว่า ไม่ได้ช่วยระบบนิเวศเพราะพวกเขาจะใช้รหัสที่ไม่ดีนั้นและนำไปใช้เป็นโซลูชั่นสถาปัตยกรรมการผลิตจริง ดังนั้นฉันเดาว่าเราจะต้องเห็นด้วยกับ "การเข้าใจผิด" นี้
dudewad

34

ฉันใช้สิ่งนี้:

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

หมายเหตุ: สิ่งนี้อาจใช้ได้กับเบราว์เซอร์ที่ใหม่กว่าโดยเฉพาะอย่างยิ่ง: http://caniuse.com/#feat=domcontentloaded


13
IE9 ขึ้นไปจริง ๆ แล้ว
Pascalius

สิ่งนี้ยังใช้งานได้ดีในสคริปต์เนื้อหาส่วนขยายของ Chrome หากคุณกำลังเชื่อมโยง document_start หรือ document_idle เหตุการณ์
Volomike

21

จริงๆถ้าคุณสนใจ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();
        }
    }
},

18

คำถามนี้ถูกถามมานานแล้ว สำหรับทุกคนที่เพิ่งเห็นคำถามนี้ตอนนี้มีไซต์ที่ชื่อว่า"คุณอาจไม่ต้องการ 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();
        });
}

ฉันสงสัยว่าทำไมจึง'onreadystatechange'มีความจำเป็นมากกว่าdocument.attachEvent('onload', fn);
ลุค

13

ฉันเพิ่งใช้สิ่งนี้กับไซต์มือถือ นี่เป็นเวอร์ชั่นย่อของ John Resig จาก "เทคนิค JavaScript Pro" มันขึ้นอยู่กับ 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
ระวังด้วยรหัสนี้ ไม่เท่ากับ $ (เอกสาร). Ready รหัสนี้จะทริกเกอร์การโทรกลับเมื่อเอกสารพร้อมใครซึ่งไม่รับประกันว่า DOM จะโหลดเต็ม
Karolis

12

ข้ามเบราว์เซอร์ (เบราว์เซอร์เก่าด้วย) และวิธีแก้ปัญหาง่ายๆ:

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

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

แสดงการแจ้งเตือนใน jsfiddle


ยกเว้นว่าจะใช้เวลามากกว่า 30ms ในการโหลด DOM รหัสของคุณจะไม่ทำงาน
Quelklef

1
@Quelklef นั่นคือ setInterval not setTimeout
Pawel

11

คำตอบ 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 )
}

ในบางเบราว์เซอร์removeListenerจะต้องมีการเรียกเอกสารพร้อมบริบทเช่น removeListener.call(document, ...
Ron

9

นี่เป็นข้อมูลโค้ดขนาดเล็กที่สุดในการทดสอบ DOM ที่พร้อมใช้งานบนเบราว์เซอร์ทั้งหมด (แม้แต่ IE 8):

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

ดูคำตอบนี้


6

เพียงเพิ่มส่วนนี้ลงที่ด้านล่างของหน้า HTML ของคุณ ...

<script>
    Your_Function();
</script>

เนื่องจากเอกสาร HTML มีการแยกวิเคราะห์โดยด้านบนล่าง


7
คุณจะรู้ได้อย่างไรว่า DOM นั้นถูกสร้างขึ้นเมื่อรันโค้ดนี้? รวม CSS ที่โหลดและแยกวิเคราะห์แล้วหรือยัง เบราว์เซอร์ API DOMContentLoaded ได้รับการออกแบบมาสำหรับสิ่งนั้น
ด่าน

มันขึ้นอยู่กับสิ่งที่เขาต้องการจะทำกับ js หากเขาต้องการดำเนินการบางอย่างเมื่อเพจเสร็จสิ้นหรือไม่
davefrassoni

5

เป็นมูลค่าการมองในร็อค 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");});

ลิงก์ที่สองเสีย
Peter Mortensen


4

รหัสข้ามเบราว์เซอร์นี้จะเรียกใช้ฟังก์ชันเมื่อ 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);


3

วิธีการแก้ปัญหานี้?

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

3
คุณสามารถใช้ addEventListener บนหน้าต่างด้วย "load" ผู้ฟังจะถูกดำเนินการทีละคนและไม่จำเป็นต้องผูกมัดด้วยตนเอง
Zaffy

1
แต่การโหลดนั้นแตกต่างจากความพร้อม 'โหลด' จะเกิดขึ้นก่อนที่เอกสารจะ 'พร้อม' เอกสารพร้อมโหลด DOM แล้วหน้าต่างโหลดไม่จำเป็นต้องมี DOM พร้อม คำตอบที่ดีแม้ว่า
MZN

1
@ Mzn: ฉันคิดว่ามันถอยหลัง ฉันคิดว่าเอกสารพร้อมเกิดขึ้นก่อนเหตุการณ์โหลดหน้าต่าง "โดยทั่วไปไม่จำเป็นต้องรอให้โหลดภาพทั้งหมดถ้ารหัสสามารถดำเนินการได้ก่อนหน้านี้มักจะวางไว้ในตัวจัดการที่ส่งไปยังวิธีการ. Ready ()" ( api.jquery.com/load-event )
Tyler Rick

สิ่งนี้จะแทนที่ส่วนที่เหลือของ window.onload เหตุการณ์ในหน้าและจะทำให้เกิดปัญหา ควรเพิ่มกิจกรรมที่อยู่ด้านบนของกิจกรรมที่มีอยู่
Teoman shipahi

เหตุการณ์โหลดอาจเกิดขึ้นช้าเกินไป มันเจ็บปวดที่จะใช้เมื่อขึ้นอยู่กับ js / images ภายนอกของบุคคลที่สาม ... เซิร์ฟเวอร์ที่ไม่ตอบสนองที่คุณไม่ได้ควบคุมและทุกอย่างล้มเหลว การใช้ DOMContentLoaded ไม่ได้เป็นเพียงการเพิ่มประสิทธิภาพ แต่ยังปลอดภัยกว่า!
dotpush

3

เป็นการดีที่จะใช้ JavaScript ที่เทียบเท่ากับ jQuery เหตุผลหนึ่งคือห้องสมุดที่น้อยกว่าหนึ่งขึ้นอยู่กับพวกเขาและพวกเขาจะเร็วกว่า jQuery เทียบเท่า

หนึ่งที่ยอดเยี่ยมสำหรับการอ้างอิงเทียบเท่า jQuery เป็นhttp://youmightnotneedjquery.com/

เท่าที่คำถามของคุณเกี่ยวข้องฉันเอาโค้ดด้านล่างจากลิงค์ด้านบน :) มีข้อแม้เท่านั้นที่ใช้ได้กับInternet Explorer 9และใหม่กว่าเท่านั้น

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

3

น้อยที่สุดและใช้งานได้ 100%

ฉันเลือกคำตอบจากPlainJSและมันใช้ได้ดีสำหรับฉัน มันขยายDOMContentLoadedเพื่อให้สามารถยอมรับได้ในทุกเบราว์เซอร์


ฟังก์ชั่นนี้เทียบเท่ากับ$(document).ready()วิธีการของ jQuery :

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 helper


3

มันเป็นปี 2020 และ<script>แท็กมีdeferคุณสมบัติ

ตัวอย่างเช่น:

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

มันระบุว่าสคริปต์จะดำเนินการเมื่อหน้าได้เสร็จสิ้นการแยก

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


2

เราพบการใช้ข้ามเบราว์เซอร์ที่รวดเร็วและสกปรกของเราซึ่งอาจทำเคล็ดลับสำหรับกรณีที่ง่ายที่สุดด้วยการใช้งานที่น้อยที่สุด:

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

อะไรนะdoc.body?
Nabi KAZ

2

โซลูชัน 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 สร้างขึ้นเพื่อนำไปปฏิบัติ


1

นี่คือสิ่งที่ฉันใช้มันเร็วและครอบคลุมทุกฐานที่ฉันคิด ใช้ได้กับทุกอย่างยกเว้น 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 แทน


1

หากคุณไม่จำเป็นต้องสนับสนุนเบราว์เซอร์ที่เก่ามากนี่เป็นวิธีที่ทำได้แม้ว่าสคริปต์ภายนอกของคุณจะถูกโหลดด้วยแอตทริบิวต์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");
});


0

หากคุณกำลังโหลด jQuery ใกล้กับด้านล่างของ BODY แต่มีปัญหากับรหัสที่เขียน jQuery (<func>) หรือ jQuery (document) .ready (<func>) ให้ตรวจสอบjqShimบน Github

แทนที่จะสร้างเอกสารพร้อมฟังก์ชั่นของตัวเองมันก็เก็บไว้ในฟังก์ชั่นจน jQuery สามารถใช้ได้แล้วดำเนินการกับ jQuery ตามที่คาดไว้ จุดของการย้าย jQuery ไปที่ด้านล่างของร่างกายคือเพื่อเพิ่มความเร็วในการโหลดหน้าและคุณยังสามารถทำได้โดยการใส่ jqShim.min.js ไว้ในส่วนหัวของเทมเพลตของคุณ

ฉันลงเอยด้วยการเขียนโค้ดนี้เพื่อย้ายสคริปต์ทั้งหมดในWordPressไปยังส่วนท้ายและตอนนี้โค้ด shim นี้ก็อยู่ที่ส่วนหัวโดยตรง


0

ลองสิ่งนี้:

function ready(callback){
    if(typeof callback === "function"){
        document.addEventListener("DOMContentLoaded", callback);
        window.addEventListener("load", callback);
    }else{
        throw new Error("Sorry, I can not run this!");
    }
}
ready(function(){
    console.log("It worked!");
});

ฮ่า ๆ ๆ คุณจะเรียกใช้การโทรกลับสองครั้ง
แอนดรูว์

0
function onDocReady(fn){ 
    $d.readyState!=="loading" ? fn():document.addEventListener('DOMContentLoaded',fn);
}

function onWinLoad(fn){
    $d.readyState==="complete") ? fn(): window.addEventListener('load',fn);
} 

onDocReadyจัดให้มีการเรียกกลับเมื่อ dom HTML พร้อมที่จะเข้าถึง / วิเคราะห์ / จัดการอย่างเต็มที่

onWinLoadจัดให้มีการโทรกลับเมื่อทุกอย่างโหลด (ภาพ ฯลฯ )

  • สามารถเรียกใช้ฟังก์ชันเหล่านี้ได้ทุกเมื่อที่คุณต้องการ
  • รองรับ "ฟัง" หลายรายการ
  • จะทำงานในเบราว์เซอร์ใด ๆ

0
(function(f){
  if(document.readyState != "loading") f();
  else document.addEventListener("DOMContentLoaded", f);
})(function(){
  console.log("The Document is ready");
});

สิ่งนี้เพิ่มอะไรที่คำตอบอื่น ๆ ไม่ได้?
dwjohnston

มันใช้การปิดตัวเอง (ไม่ได้เติมขอบเขต "หน้าต่าง" ทั่วโลก) ทำงานบนเบราว์เซอร์ทั้งหมดและมีขนาดกะทัดรัดมาก ฉันไม่เห็นคำตอบอื่น ๆ เช่นนี้
ดัสติน Poissant

นอกจากนี้ยังใช้งานได้แม้หลังจากที่ DOM โหลดไปแล้ว (เช่น jQuery.ready ทำ) ซึ่งคำตอบส่วนใหญ่ไม่สามารถทำได้
ดัสติน Poissant

0

ส่วนใหญ่ฟังก์ชั่นวานิลลา JS พร้อมไม่ได้พิจารณาสถานการณ์ที่DOMContentLoadedจัดการการตั้งค่าหลังจากที่ เอกสารถูกโหลดแล้ว - ฟังก์ชั่นซึ่งหมายความว่าจะไม่ทำงาน นี้สามารถเกิดขึ้นถ้าคุณมองหาDOMContentLoadedภายในasyncสคริปต์ภายนอก ( <script async src="file.js"></script>)

โค้ดด้านล่างตรวจสอบสำหรับDOMContentLoadedเฉพาะในกรณีที่เอกสารที่เป็นreadyStateไม่ได้แล้วหรือinteractivecomplete

var DOMReady = function(callback) {
  document.readyState === "interactive" || document.readyState === "complete" ? callback() : document.addEventListener("DOMContentLoaded", callback());
};
DOMReady(function() {
  //DOM ready!
});

หากคุณต้องการสนับสนุน IE เช่นกัน:

var DOMReady = function(callback) {
    if (document.readyState === "interactive" || document.readyState === "complete") {
        callback();
    } else if (document.addEventListener) {
        document.addEventListener('DOMContentLoaded', callback());
    } else if (document.attachEvent) {
        document.attachEvent('onreadystatechange', function() {
            if (document.readyState != 'loading') {
                callback();
            }
        });
    }
};

DOMReady(function() {
  // DOM ready!
});

0

ฉันแค่ใช้:

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

และแตกต่างจากdocument.addEventListener("DOMContentLoaded" //etcในคำตอบที่ดีที่สุดมันทำงานได้ไกลเท่าที่ IE9 - http://caniuse.com/#search=DOMContentLoadedระบุเพียงเมื่อเร็ว ๆ นี้เป็น IE11

ที่น่าสนใจฉันสะดุดในsetTimeoutการแก้ปัญหานี้ในปี 2009: การตรวจสอบความพร้อมของ DOM overkill หรือไม่? ซึ่งอาจจะเป็นคำที่ดีขึ้นเล็กน้อยเพราะฉันหมายถึง "มันเกินความจริงที่จะใช้วิธีการที่ซับซ้อนมากขึ้นของกรอบต่าง ๆ เพื่อตรวจสอบความพร้อมของ DOM"

คำอธิบายที่ดีที่สุดของฉันว่าทำไมเทคนิคนี้ถึงใช้ได้เมื่อสคริปต์มาถึง setTimeout แล้ว DOM อยู่ในระหว่างการแยกวิเคราะห์ดังนั้นการดำเนินการของรหัสภายใน setTimeout จึงถูกเลื่อนออกไปจนกว่าการดำเนินการจะเสร็จสิ้น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.