Tentang Tag Script di Dalam HTML
Tulisan ini saya buat untuk mengulas pertanyaan
- Bagaimana praktik terbaik dalam mereferensikan / menulis kode JavaScript di dalam HTML?
- Kenapa umumnya
<script>
diletakkan di akhir<body>
?
Penulisan Kode JavaScript
Salah satu alasan dipilihnya penulisan kode JavaScript biasanya mempertimbangkan terkait dengan semakin banyaknya kode yang ditulisakan dalam satu file (pengelolaan kode). Terdapat 2 cara yang umum digunakan
Pertama, menuliskan langsung di dalam tag <script>
atau disebut dengan metode inline
<script> // kode JavaScript</script>
Kedua adalah metode external, yaitu menuliskan kode JavaScript pada file terpisah. Sama seperti sebelumnya, menggunakan tag <script>
tapi pada kasus ini perlu menambahkan attribute src
yang mereferensikan nama dari file JavaScript yang ingin dimuat, sehingga akan terlihat seperti ini:
<script src="file.js"></script>
Penempatan <script>
Salah satu alasan dipilihnya penempatan <script>
biasanya mempertimbangkan terkait dengan urutan dimuat ketika halaman akan ditampilkan.
Terdapat 2 cara yang umum digunakan yaitu diletakkan di dalam tag<head>
dan di akhir tag<body>
<html>
<head> // JavaScript di dalam <head>
<script src="file.js"></script> </head>
<body>
<div id="dom"></div> // JavaScript di akhir <body>
<script src="file.js"><script> </body>
</html>
Proses Parsing HTML
Faktanya, mengelola file JavaScript di browser cukup rumit, karena eksekusi kode memblokir proses lain pada browser, seperti UI painting. Setiap kali <script>
ditemukan, halaman harus berhenti dan menunggu kode untuk diunduh (jika eksternal) dan dijalankan sebelum melanjutkan untuk memproses sisa halaman.
Hal tersebut dikarenakan sebagian besar browser menggunakan satu proses untuk pembaruan User Interface (UI) dan eksekusi JavaScript, jadi pada waktu yang sama hanya terjadi satu proses saja.
Contoh:
<html>
<head>
<title>Script Example</title>
</head>
<body>
<p>
<script>
document.write("The date is " + (new Date()).toDateString());
</script>
</p>
</body>
</html>
Ketika browser mulai membaca satu persatu kode HTML dan menemukan <script>
(seperti pada kode diatas) maka browser akan menunggu untuk melihat perilaku dari kode JavaScript yang disematkan.
Tanpa menunggu, browser tidak tahu apakah kode tersebut menambahkan konten, menambahkan elemen lain, atau bahkan menutup tag.
Setelah browser tahu perilaku dari kode tersebut, maka browser akan melanjutkan untuk me-parsing dan me-render halaman.
Hal yang sama terjadi untuk JavaScript yang dimuat dengan attribute src
, browser harus terlebih dahulu mengunduh kode dari file eksternal, lalu me-parsing dan mengeksekusi kode.
Render halaman dan interaksi pengguna sepenuhnya di blokir selama waktu tersebut
Contoh lain:
<html>
<head>
<title>Document</title> <script src="file1.js"></script>
<script src="file2.js"></script>
<link rel="stylesheet" href="styles.css"> </head>
<body>
<p>Hello World!</p>
</body>
</html>
Browser tidak akan merender apapun di halaman hingga menemui pembuka tag
<body>
Menempatkan script
di dalam tag <head>
biasanya memperlihatkan proses yang cukup lambat, seringkali halaman hanya berwarna putih untuk beberapa saat, bahkan sebelum user dapat mulai membaca atau berinteraksi dengan halaman.
Pada diagram waterfall diatas, file JavaScript pertama mulai diunduh dan memblokir file lain mana pun, untuk sementara.
Selanjutnya, terdapat penundaan ketika file1.js
selesai diunduh dan ketika file2.js
mulai diunduh. Ruang kosong tersebut merupakan waktu yang dibutuhkan kode yang berada di file1.js
untuk dieksekusi sepenuhnya.
Setiap file harus menunggu hingga file sebelumnya telah diunduh dan dieksekusi, sebelum unduhan berikutnya dapat dimulai. Sementara itu, user akan melihat layar putih kosong karena file sedang diunduh satu per satu.
Masalah Penempatan
Sebelumnya, kita sudah mengetahui bahwa script
dapat diletakkan pada head
atau body
<html>
<head>
<script>
// document.head is available
// document.body is not!
</script>
</head>
<body>
<script>
// document.head is available
// document.body is available
</script>
</body>
</html>
Pada kode diatas kita melihat terdapat masalah dengan penempatan. Ketika kode JavaScript diletakkan di head
, kode tersebut tidak dapat mengakses body
, ini merupakan masalah serius jika kode tersebut memiliki fungsi untuk mengelola element di dalam body
.
Seperti yang sudah dibahas sebelumnya, hal ini terjadi karena browser akan membaca satu persatu element HTML dari atas ke bawah, sehingga ketika script
di dalam head
dimuat, browser belum merender apapun element HTML di dalam tag <body>
.
Solusi umum yang digunakan adalah meletakkan script
di akhir body
<html>
<head>
</head>
<body>
// ...
// ... <script>
// kode ...
</script>
<script src="file.js"></script>
</body>
</html>
Hal ini dilakukan agar browser memuat element HTML di dalam body
dahulu dan memuat script
diakhir. Sampai titik ini kita dapat melihat bahwa solusi ini cukup menyelesaikan masalah sebelumnya.
Dengan solusi ini browser akan memperhatikan script
dan mulai mengunduhnya hanya setelah mengunduh documen HTML secara lengkap.
Namun, hal ini dapat sedikit menimbulkan masalah untuk document HTML yang sangat panjang, hal tersebut dapat menyebabkan delay yang cukup lama.
Hal ini sebenarnya tidak akan terlihat jika koneksi internet sangat cepat, namun banyak orang yang masih memiliki kecepatan internet yang lambat dan menggunakan koneksi internet seluler yang jauh dari sempurna.
Defer
Sejak HTML 4, attribute baru untuk <script>
ditambahkan, yaitu defer
. Attribute defer
digunakan untuk memberitahu browser agar tidak menunggu script
.
Browser akan terus memproses HTML, membangun DOM. script
dimuat di ‘background’, kemudian berjalan saat DOM sepenuhnya selesai dibuat.
script
dengan attributedefer
dapat diletakkan dimana saja di dalam HTML, namun defer
tidak berfungsi dengan baik jika kode Javascript dituliskan inline atau internal
// defer berfungsi ketika menggunakan eksternal file
<script defer src="file.js"> </script>// defer tidak berfungsi ketika dituliskan inline
<script defer>
// Kode..
</script>
script
dengan defer
selalu dijalankan saat DOM sudah sepenuhnya dimuat, tapi sebelum event DOMContentLoaded
<p> ... </p><script>
document.addEventListener('DOMContentLoaded', () =>
alert("DOM siap setelah defer")
);
</script><script defer src"file.js"></script>
script
dengan attributedefer
akan dijalankan sesuai dengan urutannya, sama dengan script
biasa.
<script defer src"file1.js"></script>
<script defer src"file2.js"></script>
<script defer src"file3.js"></script>
Browser akan memindai halaman untuk mencari script
dan mengunduhnya secara paralel. Jadi pada contoh diatas, ketiga script
diunduh secara paralel, hal ini memungkinkan file2.js
atau file3.js
selesai pertama kali sebelum file1.js
Namun, selain memberi tahu browser untuk tidak memblokir, attribute defer
juga memastikan bahwa urutan relatif tetap terjaga.
Jadi, meskipun file2.js
atau file3.js
dimuat terlebih dahulu, browser akan menunggu dan menjalankannya sesuai urutan pada contoh kode diatas.
Async
Attribute async
hampir sama dengan defer
. async
juga memiliki fungsi agar script
tidak memblokir proses lain, perbedaan mendasarnya adalah perilaku dari kedua attribute tersebut.
defer
akan diunduh secara paralel, tapi pada akhirnya akan dimuat sesuai dengan urutan kode, dari atas ke bawah. Sedangkanasync
tidak, async akan mengunduh dan memuat secara paralel, jadi async
cocok digunakan ketika satu script
dengan yang lain tidak memiliki ketergantungan (independen).
Selain itu, DOMContentLoaded
dan async
tidak saling menunggu, artinya keduanya tidak dapat dipastikan mana yang akan dimuat terlebih dahulu.
Browser Compatibility
Penting untuk mengetahui apakah async
dan defer
sudah didukung sepenuhnya oleh browser.
Dari gambar diatas, bisa kita lihat async
dan defer
hampir didukung secara penuh oleh kebanyakan browser saat ini, kecuali Opera Mini.
Kembali ke pertanyaan
- Manakah cara yang baik untuk menuliskan / mereferensikan kode JavaScript di HTML?
— Menggunakan script
di akhir body
jika user yang ditargetkan memiliki koneksi internet yang baik.
— Menggunakan defer
jika file satu dan file lain memiliki ketergantungan sehingga dimuat secara berurut merupakan kebutuhan.
— Menggunakan async
jika file satu dan file lain tidak memiliki ketergantungan (independen)
—Menggunakan eventDOMContentLoaded
jika kode JavaScript di tuliskan secara inline dan berada di dalam <head>
, agar dimuat setelah DOM tersedia.
- Kenapa umumnya
<script>
diletakkan di akhir<body>
?
Asumsi saya, hal ini dilakukan karena sebelumnya async
dan defer
belum berjalan dengan baik di semua browser, agar script
dimuat setelah DOM di muat terlebih dahulu.
Dengan mengetahui hal di atas, jika menggunakan defer
, async
ataupun event DOMContentLoaded
bukan tidak mungkin kita meletakkan script
di dalam head
bukan?
<html>
<head> <script defer src="file1.js"></script>
<script async src="file2.js"></script>
<script>
document.addEventListener('DOMContentLoaded', () => {
// kode..
});
</script> </head>
<body> ...
... </body>
</html>
Catatan
Dengan menggunakan defer
atau async
, memungkinkan user akan melihat halaman sebelum script
dimuat.
Pada beberapa kasus, tidak akan menampilkan beberapa komponen grafis sebelum JavaScript diinisiasi.
Dengan begitu, menampilkan animasi atau apapun itu yang menunjukkan bahwa halaman sedang loading, penting ditambahkan agar user melihat dengan jelas apa yang dilakukan oleh halaman, dan apa yang masih dipersiapkan.
async
dan defer
memiliki sifatnya masing masing, jika diilustrasikan dengan bagan, akan tampak seperti ini
Tentu masih ada cara lain untuk menambahkan kode JavaScript di HTML, yang mungkin akan dibahas pada artikel lain.