<script> etiketi sayfaya çalıştırılabilir kod ya da veri gömer; çoğunlukla JavaScript yüklemek için kullanılır. Kodu iki yolla verebilirsiniz: src özniteliğiyle harici bir dosya göstererek ya da kodu etiketin içine yazarak. Varsayılan davranışta tarayıcı <script> etiketine geldiğinde HTML ayrıştırmayı durdurur, betiği indirir ve çalıştırır, sonra devam eder. defer betiği ayrıştırma bitene kadar erteler ve sırayı korur, async betiği paralel indirip hazır olur olmaz sırasız çalıştırır, type="module" ise kodu JavaScript modülü olarak yükler ve varsayılan olarak erteler.
Bu yazıda <script> öğesinin özniteliklerini MDN belgelerine göre tek tek inceliyoruz: async ve defer farkı, type değerleri (klasik betik, module, importmap, speculationrules ve veri blokları), nomodule ile eski tarayıcılara yedek betik, blocking="render", betiğin MIME türü ve JavaScript kapalıyken devreye giren <noscript> öğesi. JavaScript dilinin kendisini öğrenmek için JavaScript öğrenme rehberine bakabilirsiniz.
Harici ve satır içi betik
<!-- Harici dosya -->
<script src="js/uygulama.js"></script>
<!-- Satır içi kod -->
<script>
console.log("Sayfa yüklendi");
</script>Teknik özete göre <script> öğesinin açılış ve kapanış etiketleri zorunludur. src kullandığınızda etiketin içi boş kalsa bile </script> yazılmalıdır; <script src="uygulama.js" /> biçiminde kendini kapatan bir yazım bu kurala uymaz, dolayısıyla geçerli değildir. Öğe metadata, akış ve ifade içeriği kategorisindedir; bu yüzden hem <head> içinde hem de gövdede ifade içeriği kabul eden her yerde kullanılabilir. Örtük ARIA rolü yoktur ve role verilemez.
MDN, kodu çoğunlukla ayrı .js dosyalarında tutmanızı önerir: odaklanmak, kendi kendine yeterli HTML yazmak ve yapılandırılmış uygulamalar kurmak kolaylaşır. Satır içi betikler küçük işler için pratiktir. MDN'in hızlı sayfa rehberi ayrıca satır içi betiklerin sayfa yüklemesini pahalılaştırabileceğini söyler: ayrıştırıcı, satır içi bir betiğin ayrıştırma sürerken sayfa yapısını değiştirebileceğini varsaymak zorundadır. Özellikle document.write() ile içerik üretmek yerine DOM API'lerini kullanmanız önerilir.
Varsayılan davranış: ayrıştırmayı engelleyen betik
async, defer veya type="module" taşımayan betikler, ayrıca type="module" olmayan tüm satır içi betikler, tarayıcı sayfayı ayrıştırmaya devam etmeden önce hemen indirilir ve çalıştırılır. Bunun iki sonucu vardır. Birincisi, büyük bir betik indirilirken sayfanın geri kalanı ayrıştırılmaz. İkincisi, betik kendisinden sonra gelen HTML öğelerine erişemez, çünkü o öğeler henüz oluşmamıştır. MDN bu durumda iki çözüm önerir: betiği gövdenin sonuna, </body> etiketinin hemen önüne taşımak ya da harici betiklerde defer kullanmak.
<body>
<button id="sepet-dugmesi" type="button">Sepete ekle</button>
<!-- Betik düğmeden sonra geldiği için düğmeye erişebilir -->
<script src="js/sepet.js"></script>
</body>async ve defer

Her iki öznitelik de mantıksal (boolean) özniteliktir: yazılması doğru, yazılmaması yanlış anlamına gelir, değer vermeniz gerekmez. İkisi de harici betiğin sayfa ayrıştırılırken paralel indirilmesini sağlar ve ayrıştırmayı engelleyen JavaScript'i ortadan kaldırır. Farkları çalışma zamanı ve sırasındadır.
async: Klasik betik paralel indirilir ve hazır olur olmaz çalıştırılır. Modül betiklerinde ise betik ve tüm bağımlılıkları paralel indirilir ve hazır olduklarında çalıştırılır. MDN, indirme tamamlandığında betiğin çalışacağını ve çalışırken sayfanın işlenmesini durduracağını, betiklerin hangi sırayla çalışacağının garanti edilmediğini vurgular. Bu yüzden async, sayfadaki başka hiçbir betiğe bağlı olmayan, bağımsız betikler için uygundur.
defer: Betik, belge ayrıştırıldıktan sonra ama DOMContentLoaded olayı tetiklenmeden önce çalıştırılır. defer taşıyan betikler indirilip çalışmalarını bitirene kadar DOMContentLoaded tetiklenmez. En önemli özelliği sırayı korumasıdır: defer betikleri belgedeki sıralarıyla çalışır. Bu yüzden DOM'a ve birbirine bağlı betikler için uygundur.
<!-- Sıra garanti değil: kutuphane.js sonra çalışırsa eklenti.js hata verebilir -->
<script async src="js/kutuphane.js"></script>
<script async src="js/eklenti.js"></script>
<!-- Sıra garanti: önce kutuphane.js, sonra eklenti.js, ikisi de ayrıştırmadan sonra -->
<script defer src="js/kutuphane.js"></script>
<script defer src="js/eklenti.js"></script>| Yazım | Ayrıştırma sırasında | Ne zaman çalışır | Sıra |
|---|---|---|---|
| Özniteliksiz klasik betik | Ayrıştırma durur, betik indirilir | Hemen | Belgedeki sıra |
| async | Paralel indirilir | İner inmez; çalışırken sayfayı durdurur | Garanti yok |
| defer | Paralel indirilir | Ayrıştırma bitince, DOMContentLoaded öncesi | Belgedeki sıra |
| type=module | Paralel indirilir | Varsayılan olarak ertelenir | async eklenirse hazır olunca çalışır |
asyncvedeferbirlikte yazılırsa öğe yalnızcaasyncvarmış gibi davranır.- Klasik betiklerde
asyncvedefer,srcyoksa (satır içi betikte) kullanılmamalıdır; etkileri olmaz. defermodül betiklerinde etkisizdir, çünkü modüller zaten varsayılan olarak ertelenir.
type özniteliği

type özniteliği, öğenin temsil ettiği betiğin türünü söyler. Değerine göre öğe bambaşka işler yapar:
| type değeri | Anlamı |
|---|---|
| Yazılmamış, boş veya JavaScript MIME türü | Klasik betik; JavaScript için MDN type yazmamayı önerir |
| module | Kod JavaScript modülü olarak işlenir; işlenmesi ertelenir |
| importmap | İçerik, modül adlarının çözümlenmesini denetleyen bir import map JSON nesnesidir |
| speculationrules | İçerik, önceden indirme ve önceden çizme kurallarını tanımlayan JSON dur |
| Başka bir değer | Veri bloğu; tarayıcı işlemez, src dahil diğer öznitelikler yok sayılır |
type="module" için MDN şunları belirtir: kodun işlenmesi ertelenir, charset ve defer özniteliklerinin etkisi yoktur ve klasik betiklerden farklı olarak başka kökenden alınan modüller için CORS protokolü zorunludur. importmap değeri importmap yazısında, speculationrules ise preload ve prefetch yazısında anlatılıyor.
Modüllerin klasik betiklerden farkları
MDN'in JavaScript modülleri rehberi, HTML'de modül kullanırken bilmeniz gereken farkları sıralar:
importveexportyalnızca modüllerde kullanılabilir.type="module"olmayan bir betikteimportyazarsanız sözdizimi hatası alırsınız.- Sayfayı
file://adresiyle yerelde açarsanız modüllerin güvenlik gereksinimleri nedeniyle CORS hataları alırsınız; test için bir sunucu kullanmanız gerekir. - Modüller otomatik olarak katı modda (strict mode) çalışır, bu yüzden bazı kodlar klasik betikten farklı davranabilir.
- Bir modül birden fazla
<script>etiketinde geçse bile yalnızca bir kez çalıştırılır. - Modülde tanımlanan değişkenler modülün kapsamında kalır, global kapsama çıkmaz; buna karşılık global değişkenler modülün içinden okunabilir.
- Satır içi bir modül başka modülleri içe aktarabilir, ama dışa aktardığı şeylere başka modüller erişemez, çünkü bir URL'si yoktur. MDN modülleri ayrı dosyalarda tanımlamanızı önerir.
- Sunucu modül dosyalarını JavaScript MIME türüyle (önerilen
text/javascript) göndermelidir; aksi hâlde katı MIME türü denetimi hatası alınır.
<script type="module" src="js/ana.js"></script>
<script type="module">
import { selamla } from "/js/selam.js";
selamla("Deniz");
</script>nomodule ile yedek betik
nomodule mantıksal özniteliği, betiğin ES modüllerini destekleyen tarayıcılarda çalıştırılmamasını söyler. Böylece modern tarayıcılara modül, modülleri desteklemeyen eski tarayıcılara yedek bir klasik betik sunabilirsiniz: modülü destekleyen tarayıcı nomodule betiğini yok sayar, desteklemeyen tarayıcı ise type="module" betiğini tanımadığı için atlar.
<script type="module" src="js/ana.js"></script>
<script nomodule src="js/yedek.js"></script>Veri blokları: JSON gömmek
type değeri JavaScript olmayan geçerli bir MIME türüyse içerik bir veri bloğudur ve tarayıcı onu çalıştırmaz. MDN, sunucu tarafında üretilen verileri sayfaya bu yolla gömmeyi örnek verir. Veri application/json türüyle yazılır, sonra normal bir betik içinden metin olarak okunup ayrıştırılır. Bu durumda src dahil diğer öznitelikler yok sayılır; yani veri bloğunu harici dosyadan yükleyemezsiniz. Arama motorları için yapılandırılmış veri olan JSON-LD de bu kalıbı kullanır; ayrıntısı Schema.org ve JSON-LD rehberinde.
<!-- Sunucunun ürettiği veri -->
<script id="kullanici-verisi" type="application/json">
{ "kullaniciId": 1234, "ad": "Elif Kaya", "uyelik": "2019-03-01" }
</script>
<script>
const kullanici = JSON.parse(document.getElementById("kullanici-verisi").text);
console.log(kullanici.ad);
</script>Diğer öznitelikler
| Öznitelik | Kısa açıklama |
|---|---|
| src | Harici betiğin adresi |
| integrity | Betiğin beklenen kriptografik özeti; src yoksa yazılmamalı |
| crossorigin | CORS ayarı; başka kökenden betiklerde window.onerror hata ayrıntıları için |
| nonce | CSP script-src politikasında betiğe izin veren, her yanıtta yeniden üretilen tek kullanımlık değer |
| referrerpolicy | Betik ve betiğin yaptığı isteklerde gönderilecek Referer bilgisi |
| fetchpriority | Diğer harici betiklere göre öncelik ipucu: high, low, auto |
| blocking | render belirteciyle betik çalışana kadar çizimi bekletir |
| attributionsrc | Attribution Reporting API için başlık gönderilmesini ister |
crossorigin olmadan başka kökenden yüklenen ve standart CORS denetimini geçmeyen betikler, window.onerror işleyicisine çok az bilgi aktarır. Statik dosyalarınızı ayrı bir alan adından sunuyorsanız hata kaydı için bu özniteliği kullanırsınız. integrity ve crossorigin'in birlikte nasıl çalıştığı integrity ve crossorigin yazısında anlatılıyor. nonce için MDN, sunucunun politikayı her gönderişinde benzersiz ve tahmin edilemez bir değer üretmesi gerektiğini, aksi hâlde politikanın kolayca aşılabileceğini vurgular; CSP'nin kendisi XSS ve CSP yazısında.
referrerpolicy belirtilmezse belge veya alan adı düzeyindeki politika devralınır; böyle bir politika da yoksa boş değer strict-origin-when-cross-origin gibi işlenir. Kullanımdan kalkan iki öznitelik vardır: charset (belgeler UTF-8 olmak zorunda olduğu ve betik kodlamayı belgeden devraldığı için gereksizdir) ve değerleri hiç standartlaşmamış language (yerine type kullanılır).
blocking="render"
Betikler varsayılan olarak çizimi engellemez. type="module", async veya defer taşımayan bir betik ayrıştırmayı engeller, çizimi değil. blocking="render" ise betik indirilip çalıştırılana kadar sayfanın ekrana çizilmesini bekletir. MDN'in örneğinde async bir betiğe eklenir: betik ayrıştırmayı engellemez ama çizim başlamadan önce çalışması garanti edilir. Yalnızca head içindeki betikler çizimi engelleyebilir; betikle sonradan eklenen bir betik için de blocking = "render" ayarlanması gerekir.
<head>
<script blocking="render" async src="js/tema-secimi.js"></script>
</head>MIME türü ve load, error olayları
Harici betik text/javascript MIME türüyle sunulmalıdır. MDN'e göre tarayıcılar bu konuda hoşgörülüdür ve betiği yalnızca görsel (image/*), video (video/*), ses (audio/*) türüyle ya da text/csv olarak sunulmuşsa engeller. Betik engellenirse öğeye error, aksi hâlde load olayı gönderilir. Bu hoşgörü modüller için geçerli değildir: modüller JavaScript MIME türü ister. Barındırma ortamınızda .js ve .mjs dosyalarının doğru türle sunulduğunu kontrol edin.
noscript: JavaScript kapalıyken
<noscript>, sayfadaki betik türü desteklenmiyorsa ya da tarayıcıda betik çalıştırma kapalıysa gösterilecek HTML bölümünü tanımlar. Betik kapalıyken çocuklarını normal HTML içeriği olarak temsil eder; betik açıkken ise çocuklarını düz metin olarak temsil eder, yani içeriği sayfada görünmez. Açılış ve kapanış etiketleri zorunludur.
İzin verilen içerik konuma göre değişir. Head içinde ve betik kapalıyken yalnızca <link>, <style> ve <meta> öğeleri (herhangi bir sırayla) bulunabilir. Head dışında ise saydam içerik modelini izler, yani üst öğesinin kabul ettiği içeriği alır; ancak içinde başka bir <noscript> bulunamaz. Head içinde kullanımı yalnızca HTML belgelerinde geçerlidir.
<head>
<noscript>
<link rel="stylesheet" href="css/betiksiz.css" />
</noscript>
</head>
<body>
<noscript>
<p>Bu hesap makinesi JavaScript gerektirir. <a href="tarife-listesi">Tarife listesini</a> düz sayfa olarak görebilirsiniz.</p>
</noscript>
</body>MDN'in betik erişilebilirliği rehberi bir yedek plan kurmanızı ister: kullanıcılar hız veya güvenlik için JavaScript'i kapatmış olabilir, ağ sorunları betiklerin yüklenmesini engelleyebilir, üçüncü taraf betikler ve eklentiler sizin betiklerinizi bozabilir. En azından <noscript> ile kısa bir mesaj bırakın; ideal olanı işlevi HTML ve sunucu tarafında da sağlamaktır. MDN aynı yerde, neredeyse herkesin JavaScript'i açık olduğu için <noscript>'in erişilemez betikler yazmak için bir bahane olmadığını da vurgular: içerik metin olarak erişilebilir olmalı, tüm işlevler klavyeyle kullanılabilmelidir. Klavye erişimi tabindex ve klavye erişimi yazısında anlatılıyor.
Uygulama: bir sayfada betikleri yerleştirmek
Aşağıdaki sayfa, yukarıdaki kuralların bir arada kullanımını gösteriyor. Tema seçimi çizimden önce gerektiği için head içinde blocking="render" ile; DOM'a bağlı uygulama kodu defer ile; birbirinden bağımsız analiz betiği async ile; modern kod modül olarak, eski tarayıcılar için yedek nomodule ile yükleniyor.
<!doctype html>
<html lang="tr">
<head>
<meta charset="utf-8" />
<title>Sipariş Takibi</title>
<script blocking="render" async src="js/tema-secimi.js"></script>
<script defer src="js/kutuphane.js"></script>
<script defer src="js/takip.js"></script>
<script async src="js/analiz.js"></script>
<script type="module" src="js/harita.js"></script>
<script nomodule src="js/harita-yedek.js"></script>
</head>
<body>
<h1>Siparişiniz nerede?</h1>
<noscript>
<p>Canlı takip için JavaScript gerekir. Kargo numaranızı müşteri hizmetlerine iletebilirsiniz.</p>
</noscript>
</body>
</html>Sık yapılan hatalar

- Kendini kapatan script:
<script src="x.js" />geçersizdir;</script>her zaman yazılır. - Satır içi betiğe defer veya async eklemek: klasik satır içi betiklerde etkisizdir.
- async betiklerin sırasına güvenmek: sıra garanti değildir; bağımlı betikler için
deferkullanın. - Head içindeki betikten gövdedeki öğeye erişmek: özniteliksiz betik çalışırken öğeler henüz yoktur;
deferekleyin ya da betiği sona taşıyın. - Klasik betikte import yazmak:
type="module"olmadan sözdizimi hatası alınır. - Modülleri file:// ile denemek: CORS hataları alırsınız; yerel bir sunucu kullanın.
- type="text/javascript" yazmayı zorunlu sanmak: JavaScript için
typeyazmamanız önerilir. - Veri bloğunu src ile yüklemeye çalışmak: veri bloklarında
srcyok sayılır.
Sık Sorulan Sorular
Betiği head içine mi koymalıyım, body sonuna mı?
İkisi de çalışır. Head içine koyacaksanız harici betiklere defer ekleyin; böylece indirme erken başlar ama çalışma ayrıştırma bitene kadar bekler. Özniteliksiz bir betik kullanıyorsanız onu </body> öncesine koymak, sayfanın betik yüzünden beklememesini ve betiğin öğelere erişebilmesini sağlar.
async mi defer mi?
Betik bağımsızsa ve ne zaman çalıştığı önemli değilse async; DOM'a ya da başka bir betiğe bağlıysa defer. Emin değilseniz defer sıra garantisi verdiği için daha az sürpriz yaratır.
type="module" betiğine defer eklemem gerekir mi?
Hayır. Modüller varsayılan olarak ertelenir ve defer onlarda etkisizdir. Modülün hazır olur olmaz çalışmasını istiyorsanız async ekleyebilirsiniz.
noscript arama motorları için gerekli mi?
MDN <noscript>'i betik kapalı kullanıcılar için bir yedek olarak tanımlar ve asıl önerisi içeriğin HTML'de metin olarak bulunmasıdır. Önemli içeriği yalnızca JavaScript ile üretmek yerine HTML'de vermek her durumda daha sağlam bir yoldur.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: HTML öğrenme rehberi · importmap · preload ve modulepreload · integrity ve crossorigin · meta etiketleri · JavaScript öğrenme rehberi · XSS ve CSP