Dapatkah saya menjalankan javascript sebelum seluruh halaman dimuat?

Jawaban:

186

Anda tidak hanya dapat melakukannya , tetapi Anda harus melakukan upaya khusus untuk tidak melakukannya jika Anda tidak menginginkannya. :-)

Saat browser menemukan scripttag klasik saat mengurai HTML, browser akan berhenti mengurai dan menyerahkan penerjemah JavaScript, yang menjalankan skrip tersebut. Parser tidak melanjutkan hingga eksekusi skrip selesai (karena skrip mungkin melakukan document.writepanggilan ke markup keluaran yang harus ditangani oleh parser).

Itu adalah perilaku default, tetapi Anda memiliki beberapa opsi untuk menunda eksekusi skrip:

  1. Gunakan modul JavaScript. Sebuah type="module"skrip ditangguhkan hingga HTML telah diurai sepenuhnya dan DOM awal dibuat. Ini bukan alasan utama untuk menggunakan modul, tetapi itu salah satu alasannya:

    <script type="module" src="./my-code.js"></script>
    <!-- Or -->
    <script type="module">
    // Your code here
    </script>
    

    Kode akan diambil (jika terpisah) dan diurai secara paralel dengan penguraian HTML, tetapi tidak akan dijalankan hingga penguraian HTML selesai. (Jika kode modul Anda sebaris dan bukan dalam filenya sendiri, itu juga ditangguhkan hingga penguraian HTML selesai.)

    Ini tidak tersedia ketika saya pertama kali menulis jawaban ini pada tahun 2010, tetapi di sini pada tahun 2020, semua modul dukungan browser modern utama secara native, dan jika Anda perlu mendukung browser lama, Anda dapat menggunakan bundler seperti Webpack dan Rollup.js.

  2. Gunakan deferatribut pada tag skrip klasik:

    <script defer src="./my-code.js"></script>

    Seperti halnya modul, kode dalam my-code.jsakan diambil dan diurai secara paralel dengan penguraian HTML, tetapi tidak akan dijalankan hingga penguraian HTML selesai. Namun , defertidak berfungsi dengan konten skrip sebaris, hanya dengan file eksternal yang dirujuk melalui src.

  3. Menurut saya bukan itu yang Anda inginkan, tetapi Anda dapat menggunakan asyncatribut untuk memberi tahu browser agar mengambil kode JavaScript secara paralel dengan penguraian HTML, tetapi kemudian menjalankannya sesegera mungkin, bahkan jika penguraian HTML belum selesai . Anda dapat meletakkannya di type="module"tag, atau menggunakannya sebagai pengganti deferdi scripttag klasik .

  4. Letakkan scripttag di akhir dokumen, tepat sebelum </body>tag penutup :

    <!doctype html>
    <html>
    <!-- ... -->
    <body>
    <!-- The document's HTML goes here -->
    <script type="module" src="./my-code.js"></script><!-- Or inline script -->
    </body>
    </html>
    

    Dengan begitu, meskipun kode dijalankan segera setelah ditemukan, semua elemen yang ditentukan oleh HTML di atasnya ada dan siap digunakan.

    Biasanya hal ini menyebabkan penundaan tambahan pada beberapa browser karena browser tidak akan mulai mengambil kode hingga scripttag ditemukan, tetapi browser modern memindai dan mulai mengambil terlebih dahulu. Namun, ini adalah pilihan ketiga pada saat ini, baik modul dan deferpilihan yang lebih baik.

Spec memiliki diagram yang berguna menunjukkan baku scripttag, defer, async, type="module", dan type="module" asyncdan waktu ketika kode JavaScript diambil dan dijalankan:

masukkan deskripsi gambar di sini

Berikut adalah contoh perilaku default, scripttag mentah :

.found {
    color: green;
}
<p>Paragraph 1</p>
<script>
    if (typeof NodeList !== "undefined" && !NodeList.prototype.forEach) {
        NodeList.prototype.forEach = Array.prototype.forEach;
    }
    document.querySelectorAll("p").forEach(p => {
        p.classList.add("found");
    });
</script>
<p>Paragraph 2</p>

(Lihat jawaban saya di sini untuk detail seputar NodeListkode itu.)

Ketika Anda menjalankannya, Anda melihat "Paragraf 1" berwarna hijau tetapi "Paragraf 2" berwarna hitam, karena skrip berjalan serentak dengan penguraian HTML, sehingga hanya menemukan paragraf pertama, bukan yang kedua.

Sebaliknya, inilah type="module"skripnya:

.found {
    color: green;
}
<p>Paragraph 1</p>
<script type="module">
    document.querySelectorAll("p").forEach(p => {
        p.classList.add("found");
    });
</script>
<p>Paragraph 2</p>

Perhatikan bagaimana keduanya sekarang hijau; kode tidak berjalan sampai penguraian HTML selesai. Itu juga berlaku dengan defer scriptkonten eksternal (tetapi tidak dengan konten inline).

(Tidak perlu melakukan NodeListpemeriksaan di sana karena modul pendukung browser modern sudah forEachaktif NodeList.)

Di dunia modern ini, tidak ada nilai nyata untuk DOMContentLoadedacara fitur "siap" yang PrototypeJS, jQuery, ExtJS, Dojo, dan sebagian besar lainnya disediakan kembali pada hari itu (dan masih disediakan); cukup gunakan modul atau defer. Bahkan di masa lalu, tidak banyak alasan untuk menggunakannya (dan mereka sering digunakan secara tidak benar, menahan presentasi halaman sementara seluruh pustaka jQuery dimuat karena scriptada di dalam headalih - alih setelah dokumen), sesuatu beberapa pengembang di Google menandai sejak awal. Ini juga bagian dari alasan rekomendasi YUI untuk meletakkan skrip di akhir body, kembali pada hari itu.

TJ Crowder
sumber
1
@FeRD - Terima kasih atas pengeditannya. Peninjau menolaknya, tetapi Anda benar. :-)
TJ Crowder
Atau letakkan semuanya dalam satu fungsi dan masukkan ke dalamnya <body onload="doIt()>".
Justin Liu
@JustinLiu - loadAcara terjadi sangat terlambat dalam siklus pemuatan halaman, hampir tidak pernah praktik terbaik untuk menunggu untuk menjalankan JavaScript Anda sampai saat itu. Tapi ya, itu adalah pilihan. Saya sudah merombak jawabannya untuk tahun 2020, btw.
TJ Crowder
Atau Anda bisa menggunakandocument.addEventListener('DOMContentLoaded', function(){ //doyour stuff })
Justin Liu
@JustinLiu - Ya, lihat paragraf terakhir. :-) Saya belum pernah melihat kebutuhan untuk itu, tapi Anda bisa.
TJ Crowder
6

Anda dapat menjalankan kode javascript kapan saja. AFAIK itu dijalankan pada saat browser mencapai tag <script> tempatnya berada. Tetapi Anda tidak dapat mengakses elemen yang belum dimuat.

Jadi jika Anda membutuhkan akses ke elemen, Anda harus menunggu sampai DOM dimuat (ini tidak berarti seluruh halaman dimuat, termasuk gambar dan lainnya. Ini hanya struktur dokumen, yang dimuat lebih awal, jadi Anda biasanya menang tidak melihat penundaan), menggunakan DOMContentLoadedacara atau fungsi seperti $.readydi jQuery.

Marian
sumber
2
Peristiwa DOMContentLoaded akan diaktifkan segera setelah hierarki DOM selesai dibuat, peristiwa pemuatan akan melakukannya ketika semua gambar dan sub-bingkai selesai dimuat.
BeauCielBleu