<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

html
<!-- 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.

html
<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

async ve defer: async ve defer birlikte yazılırsa öğe yalnızca async varmış gibi davranır., Klasik betiklerde async ve defer, src yoksa (satır içi betikte)..., defer modül...

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.

html
<!-- 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ımAyrıştırma sırasındaNe zaman çalışırSıra
Özniteliksiz klasik betikAyrıştırma durur, betik indirilirHemenBelgedeki sıra
asyncParalel indirilirİner inmez; çalışırken sayfayı durdururGaranti yok
deferParalel indirilirAyrıştırma bitince, DOMContentLoaded öncesiBelgedeki sıra
type=moduleParalel indirilirVarsayılan olarak ertelenirasync eklenirse hazır olunca çalışır
  • async ve defer birlikte yazılırsa öğe yalnızca async varmış gibi davranır.
  • Klasik betiklerde async ve defer, src yoksa (satır içi betikte) kullanılmamalıdır; etkileri olmaz.
  • defer modül betiklerinde etkisizdir, çünkü modüller zaten varsayılan olarak ertelenir.

type özniteliği

type özniteliği: import ve export yalnızca modüllerde kullanılabilir. type="module" olmayan bir..., Sayfayı file:// adresiyle yerelde açarsanız modüllerin güvenlik...

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ğeriAnlamı
Yazılmamış, boş veya JavaScript MIME türüKlasik betik; JavaScript için MDN type yazmamayı önerir
moduleKod 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ğerVeri 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:

  • import ve export yalnızca modüllerde kullanılabilir. type="module" olmayan bir betikte import yazarsanı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.
html
<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.

html
<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.

html
<!-- 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

ÖznitelikKısa açıklama
srcHarici betiğin adresi
integrityBetiğin beklenen kriptografik özeti; src yoksa yazılmamalı
crossoriginCORS ayarı; başka kökenden betiklerde window.onerror hata ayrıntıları için
nonceCSP script-src politikasında betiğe izin veren, her yanıtta yeniden üretilen tek kullanımlık değer
referrerpolicyBetik ve betiğin yaptığı isteklerde gönderilecek Referer bilgisi
fetchpriorityDiğer harici betiklere göre öncelik ipucu: high, low, auto
blockingrender belirteciyle betik çalışana kadar çizimi bekletir
attributionsrcAttribution 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.

html
<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.

html
<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.

html
<!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

Sık yapılan hatalar: Kendini kapatan script, Satır içi betiğe defer veya async eklemek, async betiklerin sırasına güvenmek, Head içindeki betikten gövdedeki öğeye erişmek
  • 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 defer kullanın.
  • Head içindeki betikten gövdedeki öğeye erişmek: özniteliksiz betik çalışırken öğeler henüz yoktur; defer ekleyin 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 type yazmamanız önerilir.
  • Veri bloğunu src ile yüklemeye çalışmak: veri bloklarında src yok 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