---
title: "Web'in Temelleri ve HTML"
source: 'https://academia.sh/tr/kurslar/web-temelleri-ve-html'
language: tr
updated: '2026-08-17T04:41:05+00:00'
license: 'CC BY-SA 4.0'
lessons: 25
outcomes:
    - 'Bir sayfanın istekten çizime uzanan yaşam döngüsünü anlatır'
    - 'Belgeyi anlamsal etiketlerle yapılandırır'
    - 'Erişilebilir form kurar ve doğrulama davranışını yapılandırır'
---

# Web'in Temelleri ve HTML

Bir belgenin baytlardan düğüm ağacına dönüşümü, anlamsal işaretlemeyle yapılandırılması ve erişilebilir bir formla kullanıcıdan veri alması.

## Web Nasıl Çalışır

İşaretleme dilinin ne olduğu, tarayıcının bayttan çizime uzanan aşamaları, düğüm ağacının kaynağı ve kaynak yüklemenin ayrıştırmaya etkisi.

### İşaretleme Dili Kavramı

Source: https://academia.sh/tr/kurslar/web-temelleri-ve-html/isaretleme-dili-kavrami

İşaretlemenin metne eklenen yapı bildirimi olarak tanımı; yapı, sunum ve davranış ayrımı ve bildirimsel bir dilin sınırları.

İnternet Nasıl Çalışır kursu, adres satırına yazılan bir adın nasıl bir sunucuya ulaştığını
ve sunucunun bir yanıt gövdesi döndürdüğünü kurdu. O gövdenin nasıl taşındığı, hangi
başlıkla nitelendiği ve bağlantının nasıl kurulduğu orada anlatıldı. Bir soru açıkta kaldı:
gövdenin **kendisi** nedir?

Bu kurs o soruyu yanıtlar. Yanıt gövdesi çoğu zaman bir HTML belgesidir ve HTML bir
**işaretleme dilidir (markup language)**. Bu ders işaretlemenin ne olduğunu, neyi
tanımlayıp neyi tanımlamadığını ve neden ayrı bir dil olarak var olduğunu tanımlar.

#### İşaretleme Metne Eklenen Bildirimdir

İşaretleme, düz metnin içine serpiştirilen ve o metnin parçalarının **ne olduğunu** söyleyen
ek bilgidir. Terim, basılı yayıncılıktaki düzeltmen işaretlerinden gelir: dizgiciye
gönderilen müsveddede, bir sözcüğün altı çizilerek "bu vurgulanacak", kenara not düşülerek
"bu bir başlıktır" denirdi. İşaretin kendisi basılmazdı; basılacak olanı nitelerdi.

HTML aynı işi yapar. Aşağıdaki metinde iki tür bayt vardır: okunacak olanlar ve okunacak
olanı niteleyenler.

```js
// isaretleme.mjs — işaretlemeyi metinden ayırır
const belge = `<h1>Kuzey Yamac Istasyonu</h1>
<p>Istasyon <abbr title="deniz seviyesinden yukseklik">DSY</abbr> 1840 metrede
kuruludur ve <strong>dort</strong> buyuklugu olcer.</p>`;

let icerik = "";
let yapi = "";
let i = 0;
while (i < belge.length) {
  const k = belge.indexOf("<", i);
  if (k === -1) { icerik += belge.slice(i); break; }
  icerik += belge.slice(i, k);
  const son = belge.indexOf(">", k);
  yapi += belge.slice(k, son + 1);
  i = son + 1;
}

console.log("--- yalnizca icerik ---");
console.log(icerik.trim());
console.log("--- yalnizca yapi ---");
console.log(yapi);
console.log("--- bayt sayimi ---");
console.log("toplam :", Buffer.byteLength(belge));
console.log("icerik :", Buffer.byteLength(icerik));
console.log("yapi   :", Buffer.byteLength(yapi));
```

```
--- yalnizca icerik ---
Kuzey Yamac Istasyonu
Istasyon DSY 1840 metrede
kuruludur ve dort buyuklugu olcer.
--- yalnizca yapi ---
<h1></h1><p><abbr title="deniz seviyesinden yukseklik"></abbr><strong></strong></p>
--- bayt sayimi ---
toplam : 165
icerik : 82
yapi   : 83
```

İki bölüm de tek başına eksiktir. İçerik bölümü okunabilir ama yapısızdır: hangi satırın
başlık olduğu, `DSY` kısaltmasının neyin karşılığı olduğu kaybolmuştur. Yapı bölümü ise
içeriksizdir. Belge ikisinin birleşimidir ve bu örnekte yapı bildirimi, içerikten daha çok
bayt tutar. Bu oran her belgede farklıdır; bilgi yoğunluğu arttıkça yapının payı düşer.

Dikkat edilecek nokta, yapı bildiriminin **kaybolmamasıdır**. `abbr` ögesinin `title`
özniteliğinde taşınan açıklama ekranda görünmese de belgede durur; kısaltmayı okuyamayan
bir program veya kişi ona başvurabilir. İşaretleme, görünmeyen ama sorgulanabilen bir
katmandır.

#### Üç Ayrı Sorumluluk

Bir web sayfası üç ayrı soruya yanıt verir ve bu üç soru üç ayrı dille yanıtlanır.

| Soru | Katman | Dil |
|---|---|---|
| Bu içerik **ne**? | Yapı | HTML |
| Nasıl **görünecek**? | Sunum | CSS |
| Kullanıcı etkileşince ne **olacak**? | Davranış | JavaScript |

Bu ayrım bir üslup tercihi değil, bakım maliyetiyle ilgili bir karardır. Aynı belge
farklı sunumlarla gösterilebilir: dar bir ekranda, yazıcıda, ekran okuyucuda, arama
motorunun dizinleyicisinde. Bunların hepsi aynı yapıyı okur; yalnızca sunum katmanı
değişir. Yapı ile sunum aynı yerde tanımlanmış olsaydı, her yeni ortam için belgenin
kendisinin yeniden yazılması gerekirdi.

Ayrımın ihlali somut bir örnekle görülür. Bir metni büyük ve kalın göstermek için
`h1` ögesi kullanmak, sunum amacıyla yapı bildirimi yapmaktır: belgeye "burası birinci
düzey başlıktır" denmiş olur ve bu bildirimi okuyan her program yanıltılır. Tersi de
geçerlidir: bir başlığı `p` ögesiyle yazıp yalnızca biçemle büyütmek, yapı bilgisini yok
eder.

Bu kurs yapı katmanını kurar. Sunum katmanı sonraki kursta, davranış katmanı JavaScript
kurslarında ele alınır; bu kurs boyunca biçem ve betik yalnızca yapıyla ilişkileri
ölçüsünde geçer.

#### Bildirimsel Bir Dil

Programlama Temelleri kursunda deyim, ifade ve denetim akışı tanımlanmıştı. HTML bu
anlamda bir programlama dili değildir: koşul kuramaz, döngü çeviremez, değişken tutamaz,
hesap yapamaz. Yazılan şey bir komut dizisi değil, bir **bildirimdir**.

Bildirimsel olmanın iki sonucu vardır.

Birincisi, belgenin işlenme sırası yazarın denetiminde değildir. Bir belge yazıldığında
"önce şunu yap, sonra bunu yap" denmez; "bu bir başlık, bu bir liste, bu bir bağlantı"
denir. Bu bildirimlerden ne yapılacağını okuyan program kararlaştırır. Aynı belge, bir
tarayıcıda çizilir, bir dizinleyicide çözümlenir, bir dönüştürücüde başka biçime
çevrilir.

İkincisi, hata karşısındaki davranış farklıdır. Programlama Temelleri kursundaki sözdizim
hatası kavramı burada aynı biçimde işlemez: kusurlu bir HTML belgesi işlenmeyi
reddetmez. Ayrıştırıcı, tanımlı kurtarma kurallarını uygulayarak bir sonuç üretir. Bu
kuralları ilerleyen derslerde ayrıntısıyla ele alacağız; şimdilik önemli olan, "geçersiz
belge" diye bir çıktının olmadığıdır. Her belge bir sonuca çevrilir — sorun, çevrilen
sonucun yazarın niyetiyle uyuşup uyuşmadığıdır.

Üçüncü bir sonuç, dilin genişlemesiyle ilgilidir. Ayrıştırıcı, tanımadığı bir öge adıyla
karşılaştığında belgeyi reddetmez; o adı taşıyan bir öge kurar ve içeriğini olağan biçimde
işler. Tanımadığı bir özniteliği de saklar. Bu davranış, dile yeni bir öge eklendiğinde
onu tanımayan gerçekleştirimlerin belgeyi büsbütün yitirmesini önler: yeni ögenin
sağladığı ek anlam kaybolur, içeriği kalır. Aynı ilkeyi İnternet Nasıl Çalışır kursunda
HTTP başlıkları için de görmüştük; tanınmayan başlık yok sayılır, istek geçersiz sayılmaz.
Genişletilebilirlik, her iki tasarımda da hataya karşı hoşgörüyle satın alınmıştır.

#### Öge, Etiket ve Belge

Üç sözcük sık karıştırılır; kurs boyunca ayrımları korunacaktır.

**Etiket (tag)**, kaynak metinde köşeli parantezler arasındaki yazımdır: `<p>` bir açılış
etiketi, `</p>` bir kapanış etiketidir. Etiket, kaynak metnin bir parçasıdır.

**Öge (element)**, açılış etiketi, kapanış etiketi ve aralarındaki içeriğin oluşturduğu
bütündür. Öge, ayrıştırma sonrasında var olan kavramsal birimdir; kaynak metinde değil,
ondan üretilen yapıda bulunur.

**Belge (document)**, ögelerin oluşturduğu bütündür.

Bu ayrım gereksiz bir titizlik gibi görünebilir, ancak bir sonraki derste ayrıştırma
aşamalarını incelerken belirleyici olacaktır: bazı ögelerin kaynak metinde hiçbir etiketi
yoktur ve buna karşın belgede bulunurlar.

#### Kurs Boyunca İzlenecek Belge

Bu kurs tek bir örnek belge üzerinde ilerler: bir **ölçüm istasyonu sayfası**. Kuzey Yamaç
adlı bir istasyon, deniz seviyesinden 1840 metre yükseklikte kuruludur ve sıcaklık, bağıl
nem, rüzgâr hızı ve yağış ölçer. Sayfa istasyonun künyesini, ölçüm çizelgesini, konum
görselini ve son olarak gözlemcilerin veri düzeltmesi bildirebileceği bir formu
taşıyacaktır.

Belge kurs boyunca büyür. Bu derste yalnızca bir cümlelik bir parçası vardır; kurs
sonunda çok bölümlü, medya taşıyan ve doğrulanabilir bir form içeren bütün bir sayfa
olacaktır. Her ders bu belgeye bir katman ekler ve eklenen katmanın neden o biçimde
olduğunu gerekçelendirir.

#### Özet

- İşaretleme, metnin parçalarının ne olduğunu söyleyen ve metne karışan bir bildirim
  katmanıdır; işaretleme baytları içerik baytlarıyla aynı belgede taşınır ancak okunacak
  metnin parçası değildir.
- Bir sayfa üç ayrı soruya yanıt verir: içerik ne (yapı), nasıl görünecek (sunum), ne
  olacak (davranış). Bu kurs yalnızca yapı katmanını kurar.
- HTML bildirimsel bir dildir: denetim akışı, değişken ve hesap içermez; ne yapılacağını
  bildirimi okuyan program kararlaştırır.
- Kusurlu bir belge işlenmeyi reddetmez; ayrıştırıcı tanımlı kurtarma kurallarıyla her
  zaman bir sonuç üretir.
- Etiket kaynak metnin parçasıdır, öge ayrıştırma sonrasında oluşan birimdir; ikisi
  birebir örtüşmez.

#### Sonraki Adım

Bu ders işaretlemenin ne olduğunu tanımladı ama tarayıcının onunla ne yaptığını
anlatmadı. İnternet Nasıl Çalışır kursundaki Tarayıcı Ne Yapar dersi aşamaları başlıklar
hâlinde saymıştı: ayrıştırma, yerleşim, çizim. Sonraki ders bu aşamaları belgenin
kendisi açısından ele alır ve gelen baytların hangi dönüşümlerden geçerek ekranda bir
şeye dönüştüğünü, her aşamanın çıktısını göstererek izler.

### Tarayıcının Görevleri

Source: https://academia.sh/tr/kurslar/web-temelleri-ve-html/tarayicinin-gorevleri

Gelen baytların karakterlere, karakterlerin sözcük birimlerine, birimlerin ağaca ve ağacın ekrandaki kutulara dönüşmesi; her aşamanın girdisi ve çıktısı.

Önceki ders işaretlemenin bir bildirim katmanı olduğunu kurdu ve belgenin okunacak metinle
onu niteleyen yapıdan oluştuğunu gösterdi. Geriye tarayıcının bu bildirimle ne yaptığı
sorusu kaldı.

İnternet Nasıl Çalışır kursundaki Tarayıcı Ne Yapar dersi, adresin çözümlenmesinden
bağlantı kurmaya ve alt kaynakların istenmesine kadar olan zinciri anlatmıştı. Bu ders o
zincirin bir halkasını açar: yanıt gövdesi geldikten sonra ne olduğunu. Aşamalar birbirine
girdi–çıktı ilişkisiyle bağlıdır ve her birinin çıktısı, bir sonrakinin dilinde yazılmıştır.

#### Aşamaların Zinciri

Belge geldiğinde altı dönüşüm sırayla uygulanır.

| Aşama | Girdi | Çıktı |
|---|---|---|
| Kod çözme | bayt dizisi | karakter dizisi |
| Sözcüksel çözümleme | karakter dizisi | sözcük birimi akışı |
| Ağaç kurma | sözcük birimi akışı | düğüm ağacı |
| Biçem çözümleme | düğüm ağacı + biçem kuralları | biçemli ağaç |
| Yerleşim | biçemli ağaç | kutuların konum ve boyutları |
| Boyama | kutular | piksel |

Zincirin her halkası ayrı bir sorumluluk taşır. Sözcüksel çözümleyici bir ögenin nereye
ait olduğunu bilmez; ağaç kurucu karakter kodlamasını bilmez; yerleşim, sözcük
birimlerinin ne olduğunu bilmez. Bu ayrım, aşamalardan birinin bağımsız olarak
değiştirilmesine izin verir.

#### Kod Çözme: Bayttan Karaktere

Ağdan gelen şey bayttır. Bilgisayarlar Nasıl Çalışır kursundaki Karakter Kodlamaları
dersinde kurulduğu üzere, bayt dizisinin karakter dizisine çevrilmesi bir **kural**
gerektirir; baytların kendisi hangi kuralla yazıldığını söylemez.

```js
// birimler.mjs — bayt dizisinden birim akışına
const baytlar = Buffer.from('<p class="olcum">Sıcaklık: <b>-4</b> derece</p>', "utf8");
console.log("bayt sayisi     :", baytlar.length);

const metin = baytlar.toString("utf8"); // charset bildirimi bu adimi belirler
console.log("karakter sayisi :", metin.length);
console.log("--- birimler ---");

let i = 0;
while (i < metin.length) {
  const k = metin.indexOf("<", i);
  if (k === -1) { console.log("METIN   ", JSON.stringify(metin.slice(i))); break; }
  if (k > i) console.log("METIN   ", JSON.stringify(metin.slice(i, k)));
  const son = metin.indexOf(">", k);
  const ic = metin.slice(k + 1, son);
  if (ic.startsWith("/")) {
    console.log("KAPANIS  " + ic.slice(1));
  } else {
    const bosluk = ic.search(/[\s/]/);
    const ad = bosluk === -1 ? ic : ic.slice(0, bosluk);
    const kalan = bosluk === -1 ? "" : ic.slice(bosluk).trim();
    console.log("ACILIS   " + ad + (kalan ? "  [" + kalan + "]" : ""));
  }
  i = son + 1;
}
```

```
bayt sayisi     : 49
karakter sayisi : 47
--- birimler ---
ACILIS   p  [class="olcum"]
METIN    "Sıcaklık: "
ACILIS   b
METIN    "-4"
KAPANIS  b
METIN    " derece"
KAPANIS  p
```

İlk iki satır, kod çözme aşamasının neden ayrı bir aşama olduğunu gösterir: 49 bayt, 47 karaktere
karşılık gelir. Fark, dizedeki iki `ı` karakterinin bu kodlamada bir değil iki bayt tutmasından
gelir. Bayt sayısıyla karakter sayısı arasındaki bu ayrım, belgenin uzunluğu üzerine kurulan her
hesapta önem taşır.

Kod çözmenin hangi kurala göre yapılacağı çoğunlukla belgenin kendi içinde bildirilir; taşıma
katmanı bir kodlama bildiriyorsa öncelik onundur. Bu bildirimin biçimini ve nasıl bir sıra
izlendiğini Meta Etiketleri dersinde ele alacağız.

#### Sözcüksel Çözümleme: Karakterden Sözcük Birimine

Yukarıdaki çıktının ikinci bölümü bu aşamanın ürünüdür. Aşamanın ürettiği birimin adı
**sözcük birimidir (token)**; kavram, Bilgisayarlar Nasıl Çalışır kursundaki Derleme
Aşamaları dersinde aynı adla tanımlanmıştı. Çözümleyici karakter dizisini soldan sağa
okur ve üç tür birim üretir: açılış etiketi, kapanış etiketi ve metin. Yorum ve belge
türü bildirimi için de ayrı birim türleri vardır.

Çözümleyicinin yapmadığı şey dikkate değerdir. `b` ögesinin `p` ögesinin içinde olduğunu
söylemez; yalnızca birimlerin sırasını verir. İç içelik bilgisi, sıradan üretilecek olan
bir sonuçtur; birimin kendisinde yoktur. Aynı biçimde çözümleyici `p` ögesinin kapanmamış
olmasını da sorun saymaz: kapanış birimi gelmezse üretmez, o kadar.

Çözümleyici bir **durum makinesidir (state machine)**: hangi karakteri nasıl
yorumlayacağı o an bulunduğu duruma bağlıdır. Metin durumundayken `<` karakteri etiket
başlangıcı sayılır; etiket adı durumundayken boşluk, adın bittiğini bildirir; öznitelik
değeri durumundayken tırnak içinde `<` karakteri sıradan bir karakterdir. Bu nedenle
"HTML'i düzenli ifadeyle ayrıştırmak" genel durumda doğru sonuç vermez: düzenli ifadeler
durumdan duruma geçen bu davranışı kuramaz. Yukarıdaki betik ancak örnek kadar dar bir
girdide doğru çalışır.

#### Ağaç Kurma

Sözcük birimi akışı sıralı ve düzdür; belge ise iç içedir. Ağaç kurucu, açılış birimlerini
bir yığıta koyarak ve kapanış birimlerinde yığıttan çıkararak bu iç içeliği kurar.
Ayrıntısı sonraki dersin konusudur.

#### Biçem Çözümleme ve Kutu Üretimi

Ağaç kurulduğunda hangi ögenin ne olduğu bilinir ama nasıl görüneceği bilinmez. Biçem
çözümleme aşaması her ögeye uygulanacak biçem değerlerini hesaplar. Bu değerlerin bir
bölümü belgeye dışarıdan bağlanan kurallardan, bir bölümü tarayıcının kendi varsayılan
kurallarından gelir.

Varsayılan biçemlerin varlığı önemlidir: hiçbir biçem dosyası bağlanmamış bir belge de
yapılı görünür — başlıklar büyük ve kalın, listeler girintili, bağlantılar ayırt edilebilir
olur. Bu görünüm ögelerin doğasından değil, tarayıcının her öge için taşıdığı varsayılan
kural kümesinden gelir. Ögeleri "kalın olan" veya "girintili olan" diye anmak bu yüzden
yanıltıcıdır; bunlar sunum katmanının varsayılanlarıdır ve değiştirilebilir.

Biçem hesabının çıktısı, her görünür öge için bir **kutu**dur. Kutunun türü ögenin biçem değerine
bağlıdır: kimi kutular satır boyunca yayılır, kimi kutular metnin içinde akar. Bazı ögeler hiç
kutu üretmez; `head` bölümündeki içerik ve `display: none` verilmiş ögeler bu durumdadır.

#### Yerleşim ve Boyama

**Yerleşim (layout)**, kutuların birbirine göre yerini ve ölçüsünü hesaplar. Bu hesap
kullanılabilir genişlikten başlar ve ağaçta aşağı doğru ilerler: her kutu, kendisine
ayrılan alanı bilir ve çocuklarına dağıtır. Çıktı, her kutu için bir konum ve bir
boyuttur.

**Boyama (paint)**, hesaplanmış kutuları renklere çevirir. Arka planlar, kenarlıklar,
metin ve görseller sırayla çizilir. Çizim sırası, ögelerin belgede görünme sırasından
ayrıdır: bir ögenin başka bir ögenin üstünde görünmesi biçem katmanının kararıdır.

İki aşamanın ayrılması, yeniden çalışma maliyetini de ayırır. Bir ögenin rengi
değiştiğinde yalnızca boyama yinelenir; genişliği değiştiğinde yerleşim de yinelenir ve
yerleşim, değişen kutunun komşularını ve çocuklarını da etkilediği için daha pahalıdır.
Bu ayrım, sonraki kurslarda başarım konusunun dayanağıdır.

#### Kutular Ögelerle Bire Bir Örtüşmez

Belgedeki öge sayısıyla ekrandaki kutu sayısı eşit değildir ve bu, yerleşim aşamasının
ayrı bir aşama olmasının gerekçesidir. Üç sapma vardır.

Bir öge **hiç kutu üretmeyebilir**. `head` bölümünün içeriği, yorum düğümleri ve `display: none`
verilmiş ögeler yerleşime girmez; `visibility: hidden` ile gizlenen bir öge kutusunu üretmeyi
sürdürür, yalnızca boyanmaz. Bu ögeler ağaçta durur, sorgulanabilir ve betiklerce
değiştirilebilir; yalnızca çizilmezler.

Bir öge **birden çok kutu üretebilir**. İki satıra sığan bir paragrafın metni iki satır
kutusuna bölünür; bir bağlantının metni satır sonuna denk gelirse iki parçaya ayrılır ve
her parçanın kendi konumu ve ölçüsü olur. Ögenin kendisi tektir, kutusu değildir.

Bir kutu **hiçbir ögeye karşılık gelmeyebilir**. Sunum katmanı, belgede bulunmayan içerik
üretebilir; liste imleri ve üretilmiş içerik bu türdendir. Bu kutular ağaçta bir düğüme
karşılık gelmez.

Bu üç sapma, "belgeyi değiştirmek" ile "görünümü değiştirmek" arasındaki farkı somutlaştırır.
Ağaç, belgenin anlamını taşır; kutular, o anlamın belirli bir genişlikte, belirli bir
biçemle aldığı görünümdür.

#### Aşamalar Örtüşür

Zincir, birinci aşama tamamen bitmeden ikincisi başlamayacak biçimde işlemez. Belgenin
tamamı gelmeden ayrıştırma başlar; ağacın bir bölümü kurulduğunda ilk yerleşim hesabı
yapılabilir ve ilk boyama gerçekleşebilir. Kullanıcının uzun bir belgede ilk ekranı
belgenin tamamı inmeden görmesi bu örtüşmenin sonucudur.

Örtüşmenin bozulduğu yerler vardır: bazı kaynaklar ayrıştırmayı durdurur, bazıları
boyamayı bekletir. Bu, konunun dördüncü dersinin konusudur.

#### Özet

- Belge, art arda altı dönüşümden geçer: kod çözme, sözcüksel çözümleme, ağaç kurma, biçem
  çözümleme, yerleşim, boyama. Her aşamanın çıktısı bir sonrakinin girdisidir.
- Kod çözme aşaması bayt dizisini karakter dizisine çevirir; bayt sayısıyla karakter sayısı
  eşit değildir ve çevirinin kuralı belgede bildirilir.
- Sözcüksel çözümleyici bir durum makinesidir ve yalnızca sıra üretir; iç içelik bilgisini
  taşımaz. Bu nedenle işaretleme genel durumda düzenli ifadeyle ayrıştırılamaz.
- Ögelerin varsayılan görünümü doğalarından değil, tarayıcının varsayılan biçem
  kurallarından gelir.
- Yerleşim kutuların yerini ve ölçüsünü, boyama görünümünü hesaplar; bir değişikliğin
  hangi aşamayı yeniden çalıştırdığı maliyeti belirler.
- Aşamalar örtüşür: belgenin tamamı gelmeden ayrıştırma, ağacın tamamı kurulmadan çizim
  başlayabilir.

#### Sonraki Adım

Bu ders ağaç kurma aşamasını bir cümleyle geçti: sözcük birimi akışı bir ağaca çevrilir.
Oysa o ağaç, sonraki her şeyin dayanağıdır — biçem ona uygulanır, yerleşim onun üzerinden
hesaplanır, betikler onu değiştirir. Sonraki ders ağacın nasıl kurulduğunu adım adım
gösterir: yığıt nasıl işler, kaynak metinde bulunmayan ögeler nasıl ortaya çıkar ve iki
farklı yazımın neden aynı ağacı ürettiği.

### Belge Nesne Modeli Kavramı

Source: https://academia.sh/tr/kurslar/web-temelleri-ve-html/belge-nesne-modeli-kavrami

Sözcük birimi akışının düğüm ağacına çevrilmesi, yığıtın işleyişi, düğüm türleri ve kaynak metinle ağaç arasındaki kopukluk.

Önceki ders ağaç kurma aşamasını bir cümleyle geçmişti: sözcük birimi akışı bir ağaca
çevrilir. Bu
ders o cümleyi açar. Ağaç, belgenin işlenmesinde bir ara ürün değil, **asıl üründür**:
biçem ona uygulanır, yerleşim onun üzerinden hesaplanır, betikler onu okur ve değiştirir.
Kaynak metin, ağaç kurulduktan sonra bir daha kullanılmaz.

Bu ağacın adı **Belge Nesne Modeli (Document Object Model)** — kısaca DOM'dur. Ad iki şeyi
birden söyler: belgenin bir **modeli**dir (kaynak metnin kendisi değil, ondan üretilmiş
yapılı bir gösterim) ve **nesnelerden** oluşur (her düğüm sorgulanabilir ve
değiştirilebilir bir birimdir).

#### Neden Ağaç

Veri Yapıları kursundaki Ağaç Terminolojisi dersinde ağaç, her düğümün tek bir ebeveyni ve
sıralı çocukları olan yapı olarak tanımlanmıştı. İşaretlemenin ürettiği yapı tam olarak
budur, çünkü işaretlemenin kendisi iç içedir: bir öge ya bütünüyle başka bir ögenin içinde
yer alır ya da bütünüyle dışındadır. Kısmi örtüşme —bir ögenin yarısının içeride, yarısının
dışarıda kalması— tanımlı değildir.

Bu kısıt, iç içeliği yığıtla izlemeyi olanaklı kılar. Veri Yapıları kursundaki Yığıtlar
dersinde son giren ilk çıkar davranışı tanımlanmıştı; ağaç kurucu tam olarak bunu kullanır:
açılan öge yığıta konur, kapanan öge yığıttan alınır, yeni düğüm her zaman yığıtın
tepesindeki ögenin çocuğu olur.

#### Kurucu Algoritma

Aşağıdaki betik, sözcük birimi akışından ağaç kuran algoritmanın çekirdeğini
gerçekleştirir. Önceki dersteki çözümleyiciye iki kural eklenmiştir: boş ögeler yığıta
girmez, bazı ögeler başka bir öge açıldığında kendiliğinden kapanır.

```js
// agac.mjs — birim akisindan dugum agaci kurar
const BOS_ETIKET = new Set(["br", "hr", "img", "input", "link", "meta"]);
const KAPATAN = { li: ["li"], p: ["p"], h1: ["p"], div: ["p"], ul: ["p"] };

function birimleriAyir(metin) {
  const birimler = [];
  let i = 0;
  while (i < metin.length) {
    const k = metin.indexOf("<", i);
    if (k === -1) { birimler.push({ tur: "metin", deger: metin.slice(i) }); break; }
    if (k > i) birimler.push({ tur: "metin", deger: metin.slice(i, k) });
    const son = metin.indexOf(">", k);
    const ic = metin.slice(k + 1, son);
    if (ic.startsWith("!")) birimler.push({ tur: "belge-turu", deger: ic.slice(1) });
    else if (ic.startsWith("/")) birimler.push({ tur: "kapanis", ad: ic.slice(1).toLowerCase() });
    else {
      const bosluk = ic.search(/[\s/]/);
      birimler.push({ tur: "acilis", ad: (bosluk === -1 ? ic : ic.slice(0, bosluk)).toLowerCase() });
    }
    i = son + 1;
  }
  return birimler;
}

function agacKur(metin) {
  const kok = { tur: "belge", ad: "#belge", cocuklar: [] };
  const yigit = [kok];
  const tepe = () => yigit[yigit.length - 1];
  for (const birim of birimleriAyir(metin)) {
    if (birim.tur === "metin") {
      if (birim.deger.trim() === "") continue;
      tepe().cocuklar.push({ tur: "metin", deger: birim.deger.trim(), cocuklar: [] });
    } else if (birim.tur === "acilis") {
      while (yigit.length > 1 && (KAPATAN[birim.ad] ?? []).includes(tepe().ad)) yigit.pop();
      const dugum = { tur: "eleman", ad: birim.ad, cocuklar: [] };
      tepe().cocuklar.push(dugum);
      if (!BOS_ETIKET.has(birim.ad)) yigit.push(dugum);
    } else if (birim.tur === "kapanis") {
      const yer = yigit.map((d) => d.ad).lastIndexOf(birim.ad);
      if (yer > 0) yigit.length = yer;
    }
  }
  return kok;
}

function yaz(dugum, derinlik = 0) {
  const girinti = "  ".repeat(derinlik);
  if (dugum.tur === "metin") console.log(`${girinti}"${dugum.deger}"`);
  else console.log(`${girinti}${dugum.ad}`);
  for (const cocuk of dugum.cocuklar) yaz(cocuk, derinlik + 1);
}

function say(dugum, sayac = { eleman: 0, metin: 0, derinlik: 0 }, derinlik = 0) {
  if (dugum.tur === "eleman") sayac.eleman += 1;
  if (dugum.tur === "metin") sayac.metin += 1;
  sayac.derinlik = Math.max(sayac.derinlik, derinlik);
  for (const cocuk of dugum.cocuklar) say(cocuk, sayac, derinlik + 1);
  return sayac;
}

const belge = `<!doctype html>
<html>
<head><title>Kuzey Yamac Istasyonu</title></head>
<body>
<h1>Kuzey Yamac Istasyonu</h1>
<p>Deniz seviyesinden 1840 metre yukseklikte.
<ul>
<li>Sicaklik
<li>Bagil nem
</ul>
</body>
</html>`;

const agac = agacKur(belge);
yaz(agac);
console.log("---");
console.log(say(agac));
```

```
#belge
  html
    head
      title
        "Kuzey Yamac Istasyonu"
    body
      h1
        "Kuzey Yamac Istasyonu"
      p
        "Deniz seviyesinden 1840 metre yukseklikte."
      ul
        li
          "Sicaklik"
        li
          "Bagil nem"
---
{ eleman: 9, metin: 5, derinlik: 5 }
```

Kaynak metinde `p` ögesinin kapanış etiketi yoktur ve iki `li` ögesinin de yoktur. Ağaçta buna
karşın üçü de kapanmış ve doğru yerlerde durmaktadır. Bunu sağlayan `KAPATAN` tablosudur: `ul`
açıldığında açık `p` kapanır; `li` açıldığında açık `li` kapanır. Bu kurallar isteğe bağlı bir
kolaylık değil, dilin tanımının parçasıdır; her ayrıştırıcı aynılarını uygular.

#### Düğüm Türleri

Çıktıda üç tür düğüm görünür — belge, öge ve metin. Dördüncü tür, yorum düğümü, bu örnekte hiç
geçmez; kaynak metinde yorum yoktur.

**Belge düğümü**, ağacın köküdür. Bir ögeye karşılık gelmez; ağacın tamamının tutamağıdır.

**Öge düğümleri**, açılış etiketlerinden üretilir. Adı, öznitelikleri ve sıralı çocukları
vardır.

**Metin düğümleri**, etiketler arasındaki karakterlerden üretilir. Çocukları yoktur;
ağacın yaprağıdır. Yukarıdaki sayımdaki beş metin düğümü, belgedeki beş metin parçasına
karşılık gelir.

**Yorum düğümleri** (`<!-- ... -->` yazımı), belgede durur ve çizilmez. Betikler bunları
okuyabilir; işaretlemeye gömülmüş bilgiyi gizli saymamak gerekir.

Sayımda `eleman: 9` değeri, kaynak metindeki dokuz açılış etiketine karşılık gelir.
`derinlik: 5`, kökten en uzak yaprağa kadar geçilen kenar sayısıdır: belge → `html` →
`body` → `ul` → `li` → metin düğümü.

#### Kaynak Metinde Bulunmayan Ögeler

Yukarıdaki örnekte `html`, `head` ve `body` ögeleri kaynak metinde yazılıydı. Yazılmasalardı
da ağaçta olurlardı. Bu, önceki derste kurulan etiket–öge ayrımının en somut sonucudur:
**ağaçtaki her ögenin kaynak metinde bir etiketi yoktur.**

Ters yönlü bir sonuç da vardır: kaynak metindeki her etiket ağaçta bir ögeye karşılık
gelmez. Eşleşen açılışı olmayan bir kapanış etiketi yok sayılır; yukarıdaki algoritmada
`lastIndexOf` çağrısı `-1` döndürdüğünde hiçbir şey yapılmaması bunun karşılığıdır.

Bu iki yönlü kopukluk, bir belgeyi doğrulamanın neden kaynak metne bakarak yapılamayacağını
açıklar. Sorulması gereken soru "ne yazdım" değil, "ne kuruldu"dur.

#### Boş Ögeler

Bazı ögelerin içeriği olamaz ve bu nedenle kapanış etiketleri yoktur: `br`, `hr`, `img`,
`input`, `link`, `meta` ve birkaçı daha. Bunlara **boş öge (void element)** denir.
Algoritmada bu ögeler yığıta hiç konmaz; ağaca çocuk olarak eklenir ve yığıtın tepesi
değişmez.

Boş ögeler için `<br/>` biçiminde kapatma yazımı ayrıştırıcı tarafından kabul edilir ancak
bir etkisi yoktur; öge zaten boştur. Boş olmayan bir öge için aynı yazım —örneğin
`<div/>`— beklenen etkiyi yapmaz: öge kapanmaz, içerik onun içine akmayı sürdürür.

#### Ağaç Yaşayan Bir Yapıdır

Ağaç kurulduktan sonra donmaz. Betikler düğüm ekleyebilir, çıkarabilir, özniteliklerini
değiştirebilir; her değişiklik biçem ve yerleşim aşamalarının ilgili bölümünü yeniden
çalıştırır. Bu nedenle bir sayfanın belirli bir andaki ağacı ile sunucudan gelen kaynak
metin birbirinden ayrılabilir: kaynak metin ilk durumu, ağaç o andaki durumu anlatır.

Bu kursta betik yazılmayacak; ancak yazılan işaretlemenin doğrudan bir veri yapısına
çevrildiğini bilmek, sonraki kursların dayanağıdır. İşaretleme yazmak, bir metin dizmek
değil, bir ağaç bildirmektir.

#### Özet

- Belge Nesne Modeli, sözcük birimi akışından kurulan düğüm ağacıdır; biçem, yerleşim ve
  betikler kaynak metne değil bu ağaca uygulanır.
- Ağaç kurucu yığıt kullanır: açılan öge yığıta girer, kapanan öge çıkar, yeni düğüm
  yığıtın tepesindeki ögenin çocuğu olur.
- Kapanış etiketi yazılmayan bazı ögeler, başka bir öge açıldığında kendiliğinden kapanır;
  bu kurallar dilin tanımının parçasıdır.
- Dört düğüm türü vardır: belge, öge, metin ve yorum. Yorum düğümleri çizilmez ama belgede
  durur.
- Kaynak metinle ağaç bire bir örtüşmez: yazılmayan ögeler ağaçta belirebilir, yazılan
  bazı etiketler ağaca hiç geçmeyebilir.
- Boş ögelerin içeriği olamaz ve yığıta girmezler; kapatma yazımı onlarda etkisiz, boş
  olmayan ögelerde yanıltıcıdır.

#### Sonraki Adım

Bu ders ağacın belge metninden nasıl kurulduğunu gösterdi ve ayrıştırmanın kesintisiz
ilerlediğini varsaydı. Gerçekte belge, kendisinden başka kaynaklara başvurur: biçem
dosyaları, betikler, görseller. Bu başvurular ayrıştırma sürerken ortaya çıkar ve bir
bölümü ayrıştırmayı durdurur. Sonraki ders, hangi kaynağın hangi aşamayı beklettiğini ve
bunun belgede nereye yazıldığıyla nasıl değiştiğini bir olay çizelgesi üzerinden inceler.

### Kaynak Yükleme Sırası

Source: https://academia.sh/tr/kurslar/web-temelleri-ve-html/kaynak-yukleme-sirasi

Betik ve biçem dosyalarının ayrıştırmayı ve çizimi nasıl beklettiği; erteleme ve eşzamansız yükleme kiplerinin bir olay çizelgesi üzerinden karşılaştırılması.

Önceki ders ağacın sözcük birimi akışından kesintisiz kurulduğunu varsaydı. Gerçek
belgeler bu varsayımı bozar: ayrıştırma sürerken belge kendisinden başka kaynaklara başvurur ve bu
başvuruların bir bölümü ayrıştırmayı durdurur.

İnternet Nasıl Çalışır kursundaki Tarayıcı Ne Yapar dersi bu alt kaynak isteklerinin
varlığını göstermişti: bir adres yazıldığında sunucu birden çok istek görüyordu. Bu ders,
o isteklerin **ne zaman** yapıldığını ve belgenin işlenmesini nasıl etkilediğini ele alır.
Konu bir başarım ayrıntısı değil, belgenin nasıl yazılacağını belirleyen bir kısıttır.

#### Başvuru Ayrıştırma Sırasında Doğar

Tarayıcı belgeyi baştan sona okur. `link` ögesini gördüğü anda biçem dosyasının isteğini
yollar, `img` ögesini gördüğü anda görselin isteğini yollar. Belgenin sonunu beklemez.
Bunun doğrudan sonucu şudur: bir kaynağın belgedeki **yeri**, istendiği **anı** belirler.
Belgenin sonuna yazılan bir biçem dosyası, başına yazılana göre daha geç istenir.

Kaynakların ayrıştırma üzerindeki etkisi aynı değildir. Üç davranış vardır.

| Kaynak | Ayrıştırmayı durdurur | Çizimi bekletir |
|---|---|---|
| Görsel | Hayır | Hayır |
| Biçem dosyası (`link rel="stylesheet"`) | Hayır | Evet |
| Betik (öznitelik yazılmamış) | Evet | Dolaylı olarak |
| Betik (`defer`) | Hayır | Hayır |
| Betik (`async`) | Yürütülürken | Dolaylı olarak |

#### Betik Ayrıştırmayı Neden Durdurur

Öznitelik yazılmamış bir betik ögesi görüldüğünde ayrıştırma durur, betik indirilir,
yürütülür ve ancak sonra ayrıştırmaya devam edilir. Bu davranış bir eksiklik değil, bir
zorunluluktur: betik, o ana kadar kurulmuş ağacı okuyabilir ve ona düğüm ekleyebilir.
Ayrıştırıcı aynı anda çalışsaydı, betiğin gördüğü ağaç ile ayrıştırıcının kurduğu ağaç
çakışırdı.

Bu zorunluluğun bedeli, betik indirilirken belgenin geri kalanının işlenmemesidir. Bedeli
ödememenin iki yolu vardır ve ikisi de betiğin ağaca ne zaman erişeceğine ilişkin bir söz
verir.

**Erteleme (`defer`)**: betik ayrıştırmayı durdurmadan indirilir ve ayrıştırma bittikten sonra
yürütülür. Birden çok ertelenmiş betik, belgedeki yazılış sırasına göre yürütülür.
Bu, birbirine bağımlı betikler için tek güvenli kiptir.

**Eşzamansız (`async`)**: betik ayrıştırmayı durdurmadan indirilir ve indiği anda
yürütülür; yürütme sırasında ayrıştırma durur. Yürütme sırası indirme sırasına bağlıdır,
belgedeki sıraya değil. Başka betiklere bağımlı olmayan, tek başına çalışan betikler
içindir.

Bu ayrım bir tercih değil, bir sözleşmedir: `async` yazmak "sıramın bir önemi yok"
demektir. Sırası önemli olan bir betiğe bu öznitelik yazılırsa, hata belirli koşullarda
ortaya çıkar ve yeniden üretilmesi güçtür.

#### Biçem Dosyası Neden Çizimi Bekletir

Biçem dosyası ayrıştırmayı durdurmaz — ağaç kurulmayı sürdürür. Ancak biçem hesabı
tamamlanmadan çizim yapılamaz; yapılsaydı sayfa önce biçemsiz görünür, sonra biçemli
görünüme atlardı. Tarayıcı bunun yerine bekler.

İkinci ve daha az bilinen bir kural daha vardır: **bekleyen bir biçem dosyası varken betik
yürütülmez.** Gerekçesi aynı türdendir. Betik, bir ögenin hesaplanmış biçem değerini
sorabilir; henüz uygulanmamış bir kural varsa alacağı yanıt yanlış olur. Bu nedenle betik,
kendisi inmiş olsa bile biçem dosyası inene kadar bekletilir.

#### Olay Çizelgesi

Aşağıdaki betik bu kuralları bir olay modeli olarak çalıştırır. Gerçek bir tarayıcı değil,
kuralların modelidir: her belge parçası bir birim ayrıştırma süresi tutar, her kaynağın
getirme süresi verilidir.

```js
// yukleme.mjs — ayristirma-yukleme kurallarinin olay modeli
const belge = [
  { tur: "isaretleme", ad: "head baslangici", sure: 1 },
  { tur: "kaynak", ad: "bicem.css", bicim: "bicem", getirme: 6 },
  { tur: "isaretleme", ad: "govde ilk yarisi", sure: 2 },
  { tur: "kaynak", ad: "olcum.js", bicim: "betik", kip: "engelleyen", getirme: 3, yurutme: 1 },
  { tur: "isaretleme", ad: "govde ikinci yarisi", sure: 2 },
  { tur: "kaynak", ad: "grafik.js", bicim: "betik", kip: "ertelenen", getirme: 5, yurutme: 1 },
  { tur: "isaretleme", ad: "govde sonu", sure: 1 },
];

const gunluk = [];
let saat = 0;
const ertelenenler = [];
let bicemHazir = Infinity;

for (const parca of belge) {
  if (parca.tur === "isaretleme") {
    gunluk.push([saat, saat + parca.sure, "ayristirma: " + parca.ad]);
    saat += parca.sure;
    continue;
  }
  if (parca.bicim === "bicem") {
    gunluk.push([saat, saat, "istek: " + parca.ad + " (arka planda)"]);
    bicemHazir = saat + parca.getirme;
    continue;
  }
  if (parca.kip === "engelleyen") {
    const getirmeBitis = saat + parca.getirme;
    const yurutmeBaslangic = Math.max(getirmeBitis, bicemHazir);
    gunluk.push([saat, getirmeBitis, "DURDU: " + parca.ad + " getiriliyor"]);
    if (yurutmeBaslangic > getirmeBitis) {
      gunluk.push([getirmeBitis, yurutmeBaslangic, "DURDU: bekleyen bicem"]);
    }
    gunluk.push([yurutmeBaslangic, yurutmeBaslangic + parca.yurutme, "yurutme: " + parca.ad]);
    saat = yurutmeBaslangic + parca.yurutme;
    continue;
  }
  if (parca.kip === "ertelenen") {
    gunluk.push([saat, saat, "istek: " + parca.ad + " (arka planda)"]);
    ertelenenler.push({ ...parca, hazir: saat + parca.getirme });
  }
}

const ayristirmaBitis = saat;
gunluk.push([saat, saat, "ayristirma tamamlandi"]);
for (const betik of ertelenenler) {
  const baslangic = Math.max(saat, betik.hazir);
  gunluk.push([baslangic, baslangic + betik.yurutme, "yurutme (ertelenen): " + betik.ad]);
  saat = baslangic + betik.yurutme;
}

for (const [bas, bit, ad] of gunluk) {
  console.log(String(bas).padStart(3) + " - " + String(bit).padStart(3) + "  " + ad);
}
console.log("ayristirma bitisi:", ayristirmaBitis, "| tum betikler bitti:", saat);
```

```
  0 -   1  ayristirma: head baslangici
  1 -   1  istek: bicem.css (arka planda)
  1 -   3  ayristirma: govde ilk yarisi
  3 -   6  DURDU: olcum.js getiriliyor
  6 -   7  DURDU: bekleyen bicem
  7 -   8  yurutme: olcum.js
  8 -  10  ayristirma: govde ikinci yarisi
 10 -  10  istek: grafik.js (arka planda)
 10 -  11  ayristirma: govde sonu
 11 -  11  ayristirma tamamlandi
 15 -  16  yurutme (ertelenen): grafik.js
ayristirma bitisi: 11 | tum betikler bitti: 16
```

Çizelge üç şeyi görünür kılar. Birincisi, `olcum.js` üç birim boyunca ayrıştırmayı
durdurmuştur (3–6 aralığı) ve indikten sonra bile bir birim daha beklemiştir; beklediği
şey `bicem.css` dosyasının hazır olmasıdır. İkincisi, `grafik.js` ertelenmiş olduğu için
ayrıştırmayı hiç durdurmamış, isteği 10. birimde çıkmış ve ayrıştırma bittikten sonra
yürütülmüştür. Üçüncüsü, ayrıştırmanın bitişi (11) ile betiklerin bitişi (16) ayrı
büyüklüklerdir; belge okunabilir duruma geldiğinde tüm davranış katmanı henüz hazır
olmayabilir.

Sayılar bu modelin varsayımlarıdır; gerçek süreler ağ koşullarına ve dosya boyutlarına
bağlıdır. Değişmeyen şey sıralamadır.

#### Yerleştirme Kararları

Kurallar birkaç doğrudan sonuca varır.

Biçem dosyası belgenin baş bölümüne yazılır. Geç istenirse çizim geç başlar; gövdenin
ortasına yazılırsa, o noktaya kadar çizilmiş içerik sonradan yeniden biçemlenir.

Ayrıştırmayı durdurmayan bir betik kipi, öznitelik yazılmamış betiğe yeğlenir. Belgenin
sonuna yazmak da ayrıştırmanın büyük bölümünü kurtarır ancak erteleme kadar erken indirme
başlatmaz: sona yazılan betiğin isteği ancak ayrıştırıcı oraya vardığında çıkar.

Ayrıştırıcı durduğunda tarayıcı boş oturmaz. Belgenin ileri bölümlerini yalnızca kaynak
başvurusu aramak için önden tarayan bir düzenek, bulduğu istekleri erkenden yollar. Bu
davranış, kaynakların belgede açıkça bildirilmiş olmasına dayanır: adresi bir betiğin
hesaplayarak ürettiği bir kaynak bu taramada görünmez.

#### Özet

- Kaynak istekleri ayrıştırma sırasında doğar; bir kaynağın belgedeki yeri, istendiği anı
  belirler.
- Öznitelik yazılmamış betik ayrıştırmayı durdurur, çünkü betik o ana kadar kurulmuş ağacı
  okuyabilir ve değiştirebilir.
- Erteleme, betiği ayrıştırma bittikten sonra ve belgedeki sırayla yürütür; eşzamansız kip
  indiği anda yürütür ve sıra vermez.
- Biçem dosyası ayrıştırmayı durdurmaz ama çizimi bekletir; ayrıca bekleyen biçem varken
  betik yürütülmez.
- Ayrıştırmanın bitişi ile betiklerin bitişi ayrı anlardır; belgenin okunabilir olması tüm
  davranışın hazır olması demek değildir.

#### Sonraki Adım

Bu konu, belgenin nasıl işlendiğini kurdu: baytların ağaca çevrilmesi, ağacın kutulara
dönüşmesi, kaynakların bu zincire etkisi. Sonraki konu belgenin kendisini yazmaya başlar.
İlk soru, her belgenin en dışındaki yapıdır: belge türü bildiriminin ne işe yaradığı, baş
ve gövde bölümlerinin ne ayırdığı ve bunların hiçbiri yazılmadığında ayrıştırıcının ne
kurduğu.

## Belge Yapısı

Belge türü ve kök yapıdan söz dizimi kurallarına, başlık düzeninden tablolara kadar bir HTML belgesinin iskeletini kuran etiketler.

### Belge Türü ve Kök Yapı

Source: https://academia.sh/tr/kurslar/web-temelleri-ve-html/belge-turu-ve-kok-yapi

Belge türü bildiriminin işlevi, kök ögenin dil bildirimi, baş ve gövde bölümlerinin ayrımı ve yazılmayan ögelerin ayrıştırıcı tarafından kurulması.

Önceki konu belgenin nasıl işlendiğini kurdu. Bu konu belgenin nasıl yazıldığını kurar ve
en dıştaki yapıdan başlar: her HTML belgesinin ilk satırlarında bulunan ve çoğu zaman
üzerinde düşünülmeden kopyalanan bildirimlerden.

Bu bildirimlerin her biri belirli bir soruna karşılık gelir. Hangi soruna karşılık
geldiklerini bilmek, hangilerinin zorunlu, hangilerinin koşullu olduğunu da belirler.

#### Belge Türü Bildirimi

Bir belgenin ilk satırı `<!doctype html>` yazımıdır. Bu bir öge değildir; ayrıştırıcıya
verilen bir bildirimdir ve tek bir işi vardır: ayrıştırma kipini seçmek.

Kipin varlığının nedeni geriye dönük uyumluluktur. Web'in erken belgelerinin bir bölümü, sonradan
standartlaşan kurallara aykırı yazılmıştı; o belgeleri standart kurallarla işlemek, görünümlerini
bozardı. Çözüm iki kip tanımlamak oldu: standart kurallara uyan belgeler için **standart kip**,
eski belgeler için **uyumluluk kipi (quirks mode)**. Zamanla ikisinin arasına dar kapsamlı bir
üçüncü kip daha girdi — sınırlı uyumluluk kipi — ama belirleyici ayrım hâlâ bu ikisi arasındadır.
Belge türü bildirimi, belgenin hangi kiple işleneceğini seçen anahtardır.

Bildirimin yokluğu bir kusur değil, farklı bir istektir: ayrıştırıcı belgeyi uyumluluk
kipinde işler ve o kipteki kurallar bugün beklenen kurallar değildir. Aradaki farklar
çoğunlukla sunum katmanındadır — kutu genişliğinin nasıl hesaplandığı gibi — ve bu kurs
kapsamında ele alınmaz. Kural nettir: bildirim yazılır.

Bildirimin kendisinde başka bilgi taşınmaz. Sürüm numarası, şema adresi ve dil kimliği
içermez; yalnızca kipi seçer.

#### Kök Öge ve Dil Bildirimi

Belge türü bildiriminden sonra tek bir kök öge gelir: `html`. Ağacın belge düğümünden
sonraki tek çocuğudur ve tüm belge onun içindedir.

Kök ögede yazılması gereken bir öznitelik vardır: `lang`. Belgenin doğal dilini bildirir
ve etkisi görsel değildir.

```html
<!doctype html>
<html lang="tr">
```

Dil bildirimi en az dört kararı etkiler. Ekran okuyucular metni hangi dilin ses kurallarıyla
seslendireceğini buradan öğrenir; yanlış bildirilmiş bir dil, metni okunamaz kılar. Yazım
denetimi ve hecelemeyle satır bölme kuralları dile bağlıdır. Arama dizinleyicileri belgeyi
dile göre sınıflandırır. Bazı tipografik kurallar —tırnak işaretlerinin biçimi gibi— dile
göre değişir.

Belgenin bir bölümü başka dildeyse, o bölümü taşıyan ögede yeniden bildirilir; öznitelik
kalıtılır ve en yakın bildirim geçerlidir.

```html
<p>İstasyon adının İngilizce karşılığı
  <span lang="en">North Slope Station</span> biçimindedir.</p>
```

#### Baş ve Gövde

Kök ögenin iki çocuğu vardır ve sırası değişmez: `head`, sonra `body`.

**Baş bölümü (`head`)**, belgeye ilişkin bilgiyi taşır: başlığı, karakter kodlaması
bildirimi, dış kaynak bağlantıları, üstveri. Bu içerik çizilmez. Tek görünür etkisi,
`title` ögesinin tarayıcı sekmesinde ve yer imlerinde kullanılmasıdır.

**Gövde bölümü (`body`)**, belgenin okunacak içeriğini taşır. Çizilen her şey buradadır.

Ayrım keskin görünse de sınırda duran bir öge vardır: `script`. Betik ögesi her iki bölümde
de bulunabilir ve nerede bulunduğu, önceki derste görülen yükleme davranışını belirler.

#### Yazılmayan Ögeler Kurulur

`html`, `head` ve `body` ögelerinin etiketleri yazılmasa da ayrıştırıcı bunları kurar.
Baş bölümüne hangi ögenin gireceği, gövdeye hangisinin gireceği tanımlı bir kurala göre
belirlenir: baş bölümüne ait ögelerden başkası görüldüğü anda baş bölümü kapanır ve gövde
başlar.

```js
// kok.mjs — yazilmayan html/head/body ogelerinin yerlestirilmesi
const BAS_ICERIGI = new Set(["title", "base", "link", "meta", "style", "script", "noscript"]);

function yerlestir(metin) {
  const birimler = [...metin.matchAll(/<\/?([a-z0-9!]+)[^>]*>/g)]
    .map((e) => ({ ad: e[1], kapanis: e[0][1] === "/" }));
  const bas = [];
  const govde = [];
  let belgeTuru = null;
  let kip = "bas";
  for (const birim of birimler) {
    if (birim.ad === "!doctype") { belgeTuru = "html"; continue; }
    if (birim.kapanis) continue;
    if (["html", "head", "body"].includes(birim.ad)) continue;
    if (kip === "bas" && BAS_ICERIGI.has(birim.ad)) bas.push(birim.ad);
    else { kip = "govde"; govde.push(birim.ad); }
  }
  return { belgeTuru, bas, govde };
}

const belgeler = {
  "tam yazilmis": `<!doctype html><html lang="tr"><head><meta charset="utf-8">
<title>Kuzey Yamaç</title></head><body><h1>Kuzey Yamaç</h1><p>1840 m</p></body></html>`,
  "kok ogeleri yazilmamis": `<!doctype html><meta charset="utf-8">
<title>Kuzey Yamaç</title><h1>Kuzey Yamaç</h1><p>1840 m</p>`,
  "belge turu yok": `<title>Kuzey Yamaç</title><h1>Kuzey Yamaç</h1>`,
};

for (const [ad, belge] of Object.entries(belgeler)) {
  const { belgeTuru, bas, govde } = yerlestir(belge);
  console.log("[" + ad + "]");
  console.log("  belge turu :", belgeTuru ?? "yok -> uyumluluk kipi");
  console.log("  head       :", bas.join(", "));
  console.log("  body       :", govde.join(", "));
}
```

```
[tam yazilmis]
  belge turu : html
  head       : meta, title
  body       : h1, p
[kok ogeleri yazilmamis]
  belge turu : html
  head       : meta, title
  body       : h1, p
[belge turu yok]
  belge turu : yok -> uyumluluk kipi
  head       : title
  body       : h1
```

İlk iki belge farklı yazılmış, aynı yapıyı üretmiştir. Üçüncüsü aynı kuralla
bölünmüştür ama farklı bir kiple işlenecektir.

Bu kuralın bir sonucu, baş bölümüne ait olmayan bir ögenin yanlışlıkla baş bölümüne
yazılamamasıdır: yazılsa bile gövdeye taşınır. Tersi de doğrudur — gövdenin ortasına
yazılmış bir `title` ögesi baş bölümüne geri gitmez, gövdede kalır ve beklenen işi
görmez. Ayrıştırıcının hoşgörüsü, yazım hatalarını sessizce düzeltmek değil, tanımlı bir
sonuç üretmektir.

Etiketlerin yazılmasının yine de bir gerekçesi vardır: okunabilirlik ve öznitelik yeri.
`lang` özniteliği `html` ögesine, sınıf veya kimlik `body` ögesine yazılabilmelidir; bunun
için etiketin kaynak metinde bulunması gerekir.

#### Tekillik ve Sıra Kısıtları

Kök yapıda dört öge tektir: bir belgede yalnızca bir `html`, bir `head`, bir `body` ve bir
`title` bulunur. İkincisinin yazılması yeni bir öge kurmaz; ayrıştırıcı ikinci `body`
etiketini gördüğünde yeni bir gövde açmaz, var olanın öznitelikleriyle birleştirir.
Belgenin iki gövdesi olamaz, çünkü ağacın tek bir kökü ve o kökün tek bir görünür dalı
vardır.

Baş bölümünün içeriğinde sıra genellikle serbesttir; iki durum bunun dışındadır. Karakter
kümesi bildirimi olabildiğince erken yazılır — gerekçesi sonraki dersin konusudur. Göreli
adreslerin tabanını değiştiren `base` ögesi de, kendisinden **sonra** gelen adresleri
etkilediği için erken yazılır ve belgede yalnızca bir kez bulunur.

`title` ögesi baş bölümünün zorunlu içeriğidir. Yazılmadığında belge işlenmeyi sürdürür
ancak bağlamı dışında adlandırılamaz: sekmede adres görünür, yer imi adsız kaydedilir,
arama sonucunda belgeyi tanıtan metin başka kaynaklardan üretilir. Zorunluluk, bir
ayrıştırma koşulu değil, belgenin dış dünyada tanınabilir olmasının koşuludur.

#### İstasyon Belgesinin İskeleti

Kurs boyunca geliştirilecek belgenin ilk hâli şudur:

```html
<!doctype html>
<html lang="tr">
<head>
  <meta charset="utf-8">
  <title>Kuzey Yamaç Ölçüm İstasyonu</title>
</head>
<body>
  <h1>Kuzey Yamaç Ölçüm İstasyonu</h1>
  <p>Deniz seviyesinden 1840 metre yükseklikte kurulu otomatik ölçüm istasyonu.</p>
</body>
</html>
```

Beş satır bildirim, iki satır içerik. Bu oran belgede içerik arttıkça düşecektir; bildirim
bölümü belgenin büyüklüğünden bağımsız olarak sabit kalır.

`title` ögesi baş bölümünde, `h1` ögesi gövdededir ve ikisinin metni burada aynıdır. İkisi
farklı işler görür: `title` belgeyi bağlamı dışında adlandırır — sekmede, yer iminde, arama
sonucunda —; `h1` belgenin içindeki en üst başlıktır. Bağlam dışında anlaşılır olması
gereken `title` ögesi çoğu belgede daha uzun yazılır ve site adını da taşır.

#### Özet

- Belge türü bildirimi bir öge değildir; ayrıştırma kipini seçer ve başka bilgi taşımaz.
  Yazılmadığında belge uyumluluk kipinde işlenir.
- Kök öge `html`, belgenin tek kök çocuğudur; `lang` özniteliği seslendirme, heceleme,
  dizinleme ve tipografi kararlarını etkiler ve bir bölüm başka dildeyse orada yeniden
  bildirilir.
- Baş bölümü belgeye ilişkin bilgiyi, gövde okunacak içeriği taşır; baş bölümü içeriği
  çizilmez.
- `html`, `head` ve `body` etiketleri yazılmasa da ayrıştırıcı bu ögeleri kurar; bölünme,
  baş bölümüne ait olmayan ilk ögede yapılır.
- `title` belgeyi bağlamı dışında adlandırır, `h1` belgenin içindeki en üst başlıktır;
  metinleri aynı olabilir ama işlevleri ayrıdır.

#### Sonraki Adım

Bu ders belgenin en dış kabuğunu kurdu ve etiketleri sözdizimsel ayrıntısına girmeden
kullandı. Sonraki ders o ayrıntıyı ele alır: bir etiketin nasıl yazıldığı, özniteliklerin
hangi yazım biçimlerini kabul ettiği, metinde geçen `<` ve `&` karakterlerinin nasıl
yazılacağı ve kurallara aykırı bir yazımda ayrıştırıcının hangi kurtarma kurallarını
uyguladığı.

### Etiketler, Öznitelikler ve Varlıklar

Source: https://academia.sh/tr/kurslar/web-temelleri-ve-html/etiketler-oznitelikler-ve-varliklar

Etiket ve öznitelik yazım kuralları, karakter başvurularının çözülmesi, metin ile öznitelik bağlamı arasındaki fark ve hatalı işaretlemede kurtarma davranışı.

Önceki ders belgenin en dış kabuğunu kurdu ve etiketleri sözdizimsel ayrıntısına girmeden
kullandı. Bu ders o ayrıntıyı tanımlar: bir etiketin nasıl yazıldığını, özniteliklerin
hangi biçimleri kabul ettiğini, metinde geçen `<` ve `&` karakterlerinin nasıl yazılacağını
ve kurala aykırı bir yazımda ayrıştırıcının ne yaptığını.

Bu kurallar ezberlenecek bir liste değildir. Her biri, ayrıştırıcının durum makinesindeki
belirli bir geçişe karşılık gelir ve o geçişi bilmek, beklenmedik sonuçların nedenini
açıklar.

#### Etiketin Yazımı

Bir açılış etiketi `<` karakteriyle başlar, öge adıyla sürer, isteğe bağlı özniteliklerle
devam eder ve `>` ile biter. Kapanış etiketi `</` ile başlar, yalnızca ad taşır.

Öge adları büyük–küçük harf ayrımı gözetmez: `<P>`, `<p>` ile aynı ögeyi açar.
Ayrıştırıcı adı küçültür ve ağaca küçük harfli kurar. Ayrım gözetilmemesi, iki farklı
yazımın kullanılabileceği anlamına gelmez; bir belgede tek yazım kullanılır ve küçük harf
yerleşik biçimdir.

Boş olmayan her ögenin kapanış etiketi vardır. Bir bölümünün yazılmaması önceki derste
görüldüğü gibi tanımlı bir sonuç verir, ancak yazılmayan her kapanış, okuyan kişinin
belgeyi zihninde ayrıştırmasını zorlaştırır.

#### Öznitelikler

Öznitelik, açılış etiketinin içinde yazılan ve ögeyi niteleyen ad–değer çiftidir.

```html
<img src="yamac.jpg" alt="Ölçüm direği" width="960" height="640">
```

Öznitelik adları da büyük–küçük harf ayrımı gözetmez ve küçültülür. Sıraları anlamsızdır;
aynı ögede aynı adın iki kez yazılması durumunda ilki geçerli olur, ikincisi yok sayılır.

Değer yazımının üç biçimi vardır ve üçü de geçerlidir:

```html
<td colspan="2">    <!-- çift tırnak -->
<td colspan='2'>    <!-- tek tırnak -->
<td colspan=2>      <!-- tırnaksız -->
```

Tırnaksız yazım boşluk, tırnak, `=`, `<`, `>` ve ters tırnak içeremez. Bu kısıt bir tuzak
üretir: `class=olcum tablosu` yazımında `tablosu` ayrı bir öznitelik sayılır, değerin
parçası olmaz. Bu nedenle değerler her zaman tırnak içine alınır; kısıtın nerede
başladığını takip etmek, kuralı hep uygulamaktan pahalıdır.

**Mantıksal öznitelikler (boolean attributes)** ayrı bir sınıftır. Bunlarda anlamı taşıyan
şey değer değil, özniteliğin **varlığıdır**.

```html
<input type="checkbox" checked>
<input type="checkbox" checked="">
<input type="checkbox" checked="checked">
```

Üç yazım da aynı anlama gelir: işaretli. Sezginin tersine, `checked="false"` de aynı
anlama gelir — öznitelik yazılmıştır, dolayısıyla işaretlidir. Kapatmanın tek yolu
özniteliği hiç yazmamaktır.

#### Karakter Başvuruları

Metin içinde `<` karakteri etiket başlangıcı, `&` karakteri başvuru başlangıcı sayılır. Bu
karakterleri sıradan metin olarak yazmanın yolu **karakter başvurusudur (character
reference)**. Üç biçimi vardır: adlı (`&lt;`), onluk sayısal (`&#60;`) ve onaltılık sayısal
(`&#x3C;`).

Aşağıdaki betik çözümleme kuralını uygular. Adlı başvurular gerçekte birkaç bine yakın
addan oluşan bir tablodan aranır; burada küçük bir alt küme kullanılmıştır.

```js
// varliklar.mjs — karakter basvurularinin cozulmesi
const ADLI = { amp: "&", lt: "<", gt: ">", quot: '"', apos: "'", reg: "®" };
const DESEN = /&(#x[0-9a-fA-F]+|#[0-9]+|[a-zA-Z][a-zA-Z0-9]*)(;?)/g;

function cozumle(metin, oznitelikMi) {
  return metin.replace(DESEN, (tam, govde, noktali, konum) => {
    if (govde.startsWith("#")) {
      const onaltilik = govde[1] === "x";
      return String.fromCodePoint(parseInt(govde.slice(onaltilik ? 2 : 1), onaltilik ? 16 : 10));
    }
    let ad = null;
    for (let n = govde.length; n > 0; n -= 1) {
      if (govde.slice(0, n) in ADLI) { ad = govde.slice(0, n); break; }
    }
    if (ad === null) return tam;
    const tamEslesme = ad.length === govde.length && noktali === ";";
    const kalan = govde.slice(ad.length) + noktali;
    if (tamEslesme) return ADLI[ad];
    const sonraki = kalan[0] ?? metin[konum + tam.length] ?? "";
    if (oznitelikMi && /[=a-zA-Z0-9]/.test(sonraki)) return tam;
    return ADLI[ad] + kalan;
  });
}

const ornekler = [
  "5 &lt; 8 &amp;&amp; 8 &gt; 5",
  "&#69;&#x11f;im &#x131;s&#x131;nd&#x131;",
  "kod: &lt;p&gt;",
  "&hava; tabloda yok",
];
for (const ornek of ornekler) {
  console.log(JSON.stringify(ornek), "->", JSON.stringify(cozumle(ornek, false)));
}

console.log("--- ayni dizgi, iki baglam ---");
const adres = "/olcum?tur=nem&regsayi=3";
console.log("metin icerigi olarak :", cozumle(adres, false));
console.log("oznitelik degeri     :", cozumle(adres, true));
console.log("kacirilmis yazim     :", cozumle("/olcum?tur=nem&amp;regsayi=3", true));
```

```
"5 &lt; 8 &amp;&amp; 8 &gt; 5" -> "5 < 8 && 8 > 5"
"&#69;&#x11f;im &#x131;s&#x131;nd&#x131;" -> "Eğim ısındı"
"kod: &lt;p&gt;" -> "kod: <p>"
"&hava; tabloda yok" -> "&hava; tabloda yok"
--- ayni dizgi, iki baglam ---
metin icerigi olarak : /olcum?tur=nem®sayi=3
oznitelik degeri     : /olcum?tur=nem&regsayi=3
kacirilmis yazim     : /olcum?tur=nem&regsayi=3
```

İlk satır üç adlı başvuruyu çözer. İkincisi sayısal başvuruların onluk ve onaltılık
biçimlerini karıştırır; ikisi de aynı kod noktası kümesine başvurur — Bilgisayarlar Nasıl
Çalışır kursundaki Karakter Kodlamaları dersinde tanımlanan kod noktaları. Dördüncü satır,
tabloda bulunmayan bir adın çözülmediğini ve olduğu gibi bırakıldığını gösterir.

#### Aynı Dizgi, İki Bağlam

Çıktının ikinci bölümü, kolay gözden kaçan bir kuralı görünür kılar. `/olcum?tur=nem&regsayi=3`
dizgisi metin içeriğinde `®` karakteri üreterek bozulur; öznitelik değerinde bozulmaz.

Farkın nedeni, noktalı virgülle bitmeyen adlı başvurular için tanımlı ayrı bir kuraldır:
öznitelik değerinde, başvurunun ardından `=` veya harf/rakam geliyorsa çözümleme yapılmaz.
Bu kural, tırnak içindeki adreslerin sessizce bozulmasını önlemek için vardır — ama
yalnızca öznitelik bağlamında geçerlidir.

Üçüncü satır doğru yazımı gösterir. Bir adres öznitelik değerine yazılırken parametre
ayıracı `&amp;` biçiminde kaçırılır. Öznitelik bağlamındaki kural bu kaçırmayı çoğu durumda
gereksiz kılsa da, kuralın "çoğu durumda" işlemesine güvenmek yerine kaçırmak tek biçimli
davranış verir.

Genel kural üç karakteri kapsar: metin içeriğinde `<` ve `&` kaçırılır; öznitelik değerinde
ek olarak değeri saran tırnak karakteri kaçırılır. Bunun bir güvenlik boyutu vardır ve
kullanıcıdan gelen metin belgeye yerleştirilirken bu kaçırmanın yapılmaması, en yaygın
web açıklarının kaynağıdır; konu ileri kurslarda ayrıntılandırılır.

#### Kurtarma Kuralları

İlk derste "geçersiz belge diye bir çıktı yoktur" denmişti. Aşağıdaki betik bu iddiayı
somutlaştırır: açık öge yığıtını her sözcük biriminde yazdırır ve kurala aykırı
yazımlarda ne yapıldığını gösterir.

```js
// kurtarma.mjs — hatali isaretlemede acik oge yigitinin izi
const BOS = new Set(["br", "img", "input", "meta", "link", "hr"]);
const KAPATAN = { li: ["li"], dt: ["dt", "dd"], dd: ["dt", "dd"], p: ["p"], ul: ["p"], h1: ["p"] };

function izle(metin) {
  const yigit = [];
  console.log("kaynak:", metin);
  for (const e of metin.matchAll(/<(\/?)([a-z0-9]+)[^>]*>/g)) {
    const kapanis = e[1] === "/";
    const ad = e[2];
    let not = "";
    if (!kapanis) {
      const kendiliginden = KAPATAN[ad] ?? [];
      while (yigit.length > 0 && kendiliginden.includes(yigit[yigit.length - 1])) {
        not = "(acik " + yigit.pop() + " kendiliginden kapandi)";
      }
      if (BOS.has(ad)) not = "(bos oge, yigita girmez)";
      else yigit.push(ad);
    } else if (BOS.has(ad)) {
      not = "(bos ogenin kapanisi yok sayilir)";
    } else {
      const yer = yigit.lastIndexOf(ad);
      if (yer === -1) not = "(eslesen acilis yok, yok sayilir)";
      else {
        if (yer !== yigit.length - 1) not = "(uzerindeki " + yigit.slice(yer + 1).join(", ") + " de kapanir)";
        yigit.length = yer;
      }
    }
    console.log(("  " + e[0].padEnd(10) + "yigit: [" + yigit.join(" > ") + "] " + not).trimEnd());
  }
  if (yigit.length > 0) console.log("  belge sonu  acik kalan: [" + yigit.join(" > ") + "] -> kapatilir");
  console.log();
}

izle("<p>Ölçüm <b>durdu</b></p>");
izle("<p>Ölçüm <b><i>durdu</b></i>");
izle("<ul><li>ilk<li>ikinci</ul>");
izle("<p>metin</span></p>");
```

```
kaynak: <p>Ölçüm <b>durdu</b></p>
  <p>       yigit: [p]
  <b>       yigit: [p > b]
  </b>      yigit: [p]
  </p>      yigit: []

kaynak: <p>Ölçüm <b><i>durdu</b></i>
  <p>       yigit: [p]
  <b>       yigit: [p > b]
  <i>       yigit: [p > b > i]
  </b>      yigit: [p] (uzerindeki i de kapanir)
  </i>      yigit: [p] (eslesen acilis yok, yok sayilir)
  belge sonu  acik kalan: [p] -> kapatilir

kaynak: <ul><li>ilk<li>ikinci</ul>
  <ul>      yigit: [ul]
  <li>      yigit: [ul > li]
  <li>      yigit: [ul > li] (acik li kendiliginden kapandi)
  </ul>     yigit: [] (uzerindeki li de kapanir)

kaynak: <p>metin</span></p>
  <p>       yigit: [p]
  </span>   yigit: [p] (eslesen acilis yok, yok sayilir)
  </p>      yigit: []
```

Dört kural görünür duruma gelir. Eşleşen açılışı olmayan kapanış etiketi **yok sayılır**.
Yanlış sırada kapatılan ögelerde, kapanan ögenin üzerindeki ögeler de kapanır — ikinci
örnekte `i` ögesi, `b` kapanınca kapanmıştır ve sonradan gelen `</i>` boşa düşmüştür.
Belge sonunda açık kalan ögeler kapatılır. Bazı ögeler, kardeşleri açıldığında
kendiliğinden kapanır.

Bu kuralların birlikte üretebildiği sonuç, yazarın niyetiyle uyuşmayabilir. İkinci
örnekte yazar `b` ve `i` ögelerinin aynı metni kapsamasını istemiş, ancak sonuçta `i`
ögesi `durdu` metnini hiç kapsamamıştır. Ayrıştırıcı hata bildirmez; belge kurulur.
Bu, işaretleme doğrulayan araçların neden gerekli olduğunun gerekçesidir: kusur,
ayrıştırma anında değil, sonucun beklenenden farklı olmasında görünür.

#### Yorumlar

Yorum, `<!--` ile başlayıp `-->` ile biten bölümdür ve ağaçta bir yorum düğümü üretir.
Çizilmez ama belgede durur ve okunabilir. İçine yazılan hiçbir şey gizli değildir; sunucu
adresleri, kaldırılmış özellik notları ve geçici açıklamalar belgeyle birlikte gönderilir.

Yorum metni `<!--`, `-->` ve `--!>` dizilerini içeremez; `>` ya da `->` ile başlayamaz. Yalın `--`
dizisi yasak değildir, ama bu sınırlara yakın durduğu için ayraç olarak yeğlenmez.

#### Özet

- Öge ve öznitelik adları büyük–küçük harf ayrımı gözetmez ve küçültülür; öznitelik sırası
  anlamsızdır ve yinelenen adda ilki geçerlidir.
- Öznitelik değerleri tırnaksız da yazılabilir ancak tırnaksız yazım boşlukta biter; bu
  nedenle değerler her zaman tırnak içine alınır.
- Mantıksal özniteliklerde anlamı taşıyan şey değer değil varlıktır; `checked="false"`
  yazımı da özniteliği açar, kapatmanın yolu yazmamaktır.
- Karakter başvurularının üç biçimi vardır; adlı başvurunun noktalı virgülle bitmemesi,
  metin içeriği ile öznitelik değerinde farklı sonuç verir.
- Ayrıştırıcı hatalı işaretlemede durmaz: eşleşmeyen kapanış yok sayılır, yanlış sırada
  kapatma üstteki ögeleri de kapatır, açık kalanlar belge sonunda kapanır.
- Yorumlar belgeyle birlikte gönderilir ve okunabilir; gizli bilgi taşımazlar.

#### Sonraki Adım

Bu ders karakter başvurularını çözerken bir varsayımda bulundu: baytların hangi kurala göre
karakterlere çevrildiği belliydi. Oysa bu kural belgenin kendisinde bildirilir ve
bildirilmediğinde tahmin edilir. Sonraki ders baş bölümündeki üstveri etiketlerini ele
alır ve bunların en önemlisiyle başlar: karakter kümesi bildiriminin baytları nasıl
yorumlattığı.

### Meta Etiketleri

Source: https://academia.sh/tr/kurslar/web-temelleri-ve-html/meta-etiketleri

Karakter kümesi bildiriminin baytları nasıl yorumlattığı, kodlamayı belirleyen kaynakların önceliği, görüntü alanı bildirimi ve adlandırılmış üstveri.

Önceki ders karakter başvurularını çözerken bir varsayımda bulundu: baytların hangi kurala
göre karakterlere çevrildiği belliydi. Bu ders o varsayımı kaldırır ve baş bölümündeki
üstveri etiketlerini, en belirleyici olanından başlayarak ele alır.

`meta` ögesi boş bir ögedir ve tek başına bir anlamı yoktur; anlamı taşıyan şey
öznitelikleridir. Dört farklı işlevde kullanılır ve bunlar birbirinden bağımsızdır.

#### Karakter Kümesi Bildirimi

Bilgisayarlar Nasıl Çalışır kursundaki Karakter Kodlamaları dersi tek bir noktayı kurmuştu:
bayt dizisi, hangi kurala göre yazıldığını kendi içinde söylemez. Aynı baytlar farklı
kurallarla farklı karakterlere çözülür.

```js
// charset.mjs — ayni baytlar, iki farkli kodlama varsayimi
const metin = "Sıcaklık: -4 °C";
const baytlar = Buffer.from(metin, "utf8");

const kodNoktalari = (d) =>
  [...d].map((k) => "U+" + k.codePointAt(0).toString(16).toUpperCase().padStart(4, "0")).join(" ");

console.log("baytlar         :", baytlar.toString("hex").match(/../g).join(" "));
console.log("bayt sayisi     :", baytlar.length);
console.log("karakter sayisi :", metin.length);
console.log("utf-8 okuma     :", baytlar.toString("utf8"));
console.log("latin1 okuma    :", baytlar.toString("latin1"));
console.log("utf-8 ilk uc    :", kodNoktalari(baytlar.toString("utf8").slice(0, 3)));
console.log("latin1 ilk bes  :", kodNoktalari(baytlar.toString("latin1").slice(0, 5)));

console.log("--- charset bildirimi belgenin ilk baytlarinda aranir ---");
const bas = '<!doctype html>\n<html lang="tr">\n<head>\n<meta charset="utf-8">\n';
console.log("bildirimin bittigi bayt :", Buffer.byteLength(bas));
console.log("1024 baytin altinda mi  :", Buffer.byteLength(bas) < 1024);
```

```
baytlar         : 53 c4 b1 63 61 6b 6c c4 b1 6b 3a 20 2d 34 20 c2 b0 43
bayt sayisi     : 18
karakter sayisi : 15
utf-8 okuma     : Sıcaklık: -4 °C
latin1 okuma    : SÄ±caklÄ±k: -4 Â°C
utf-8 ilk uc    : U+0053 U+0131 U+0063
latin1 ilk bes  : U+0053 U+00C4 U+00B1 U+0063 U+0061
--- charset bildirimi belgenin ilk baytlarinda aranir ---
bildirimin bittigi bayt : 63
1024 baytin altinda mi  : true
```

`c4 b1` bayt çifti, bir kuralda tek bir karaktere (U+0131, `ı`) çözülür; başka bir kuralda
iki ayrı karaktere (U+00C4 ve U+00B1). Baytlar aynıdır; farklı olan yalnızca yorumdur.
On sekiz bayt, birinci okumada on beş, ikinci okumada on sekiz karakter verir.

Belgenin bildirimi tek satırdır:

```html
<meta charset="utf-8">
```

Bu satırın etkisi görsel değil, yapısaldır. Bildirim yanlışsa yalnızca birkaç karakter
bozulmaz; belge farklı bir karakter dizisine çözülür ve etiket adları bile etkilenebilir.

#### Bildirimin Yeri Neden Önemli

Burada döngüsel bir sorun vardır. Bildirimin kendisi belgenin içindedir; onu okumak için
belgeyi çözmek, çözmek için de bildirimi okumak gerekir.

Çözüm bir ön taramadır. Ayrıştırıcı belgenin ilk bölümünü —tanımlı sınır 1024 bayttır—
tek baytlık bir varsayımla tarar ve yalnızca bir `charset` bildirimi arar. Bulursa
belgeyi o kuralla ayrıştırmaya başlar.

Bunun iki sonucu vardır. Birincisi, bildirim ilk 1024 bayt içinde olmalıdır; baş bölümünün
en başına yazılmasının nedeni budur. İkincisi, bildirim bu sınırın dışında kalır veya ön
tarama sırasında varsayılan kuralla bulunamazsa, ayrıştırıcı yanlış kuralla başlamış olur;
bildirimi sonradan gördüğünde ayrıştırmayı **baştan** yapması gerekir. Bu yeniden başlatma
ölçülebilir bir maliyettir ve bildirimin konumuyla önlenir.

#### Kodlamayı Belirleyen Kaynaklar

Belgedeki `meta` bildirimi tek kaynak değildir. Sıralı bir öncelik listesi vardır.

| Öncelik | Kaynak |
|---|---|
| 1 | Bayt sırası imi (BOM) |
| 2 | Kullanıcının açık seçimi |
| 3 | Taşıma katmanı: HTTP `Content-Type` başlığındaki `charset` |
| 4 | Belgedeki `meta charset` bildirimi |
| 5 | Sezme ve varsayılan |

Bayt sırası imi kavramı Bilgisayarlar Nasıl Çalışır kursundaki Bayt Sırası dersinde
tanımlanmıştı; belgenin başındaki birkaç bayt kodlamayı belirtir ve her şeyi bastırır.

Üçüncü sıra dikkat gerektirir: **HTTP başlığı belgedeki bildirimi bastırır.** İnternet
Nasıl Çalışır kursundaki HTTP İstek ve Yanıtı dersinde `Content-Type` başlığının bir
`charset` parametresi taşıdığı görülmüştü. O parametre, belgede başka bir bildirim olsa
bile geçerlidir. Sunucu yapılandırması ile belge birbiriyle çelişiyorsa kazanan sunucudur;
bu, bozuk görünen karakterlerin belgede aranıp bulunamadığı durumların sık nedenidir.

Beşinci sıra bir güvence değildir. Sezme, belgenin içeriğine bakarak tahmin yürütür ve
tahmin yanlış olabilir. Bildirim yazmamak, sonucun ortama bırakılması demektir.

#### Görüntü Alanı Bildirimi

İkinci `meta` kullanımı, belgenin dar ekranlarda nasıl ölçekleneceğini bildirir.

```html
<meta name="viewport" content="width=device-width, initial-scale=1">
```

Bu bildirimin gerekçesi tarihseldir. Dar ekranlı aygıtlar, dar ekran için yazılmamış
belgeleri okunabilir kılmak üzere geniş bir sanal görüntü alanı varsayar ve sayfayı
küçültür. Bildirim bu varsayımı kapatır: görüntü alanının genişliği aygıtın kendi genişliği
sayılır ve başlangıç ölçeği birdir.

Bildirimin kendisi bir sunum kararı değildir; sunum katmanının hangi genişlik üzerinde
hesap yapacağını belirler. Bu nedenle dar ekranı gözeten her belgede bulunur ve sonraki
kursta ele alınacak duyarlı düzenin ön koşuludur.

`user-scalable=no` gibi ölçeklemeyi kapatan değerler yazılabilir. Yazılmamalıdır:
büyütme, görme gücü sınırlı kullanıcılar için tek erişim yoludur ve kapatılması bir
erişilebilirlik ihlalidir.

#### Adlandırılmış Üstveri

Üçüncü kullanım, `name` ve `content` çiftiyle belgeye ilişkin bilgi bildirir. Bu bilgi
çizilmez; belgeyi işleyen programlar için taşınır.

```html
<meta name="description" content="Kuzey Yamaç otomatik ölçüm istasyonunun
      sıcaklık, bağıl nem, rüzgâr hızı ve yağış kayıtları.">
<meta name="robots" content="index, follow">
<meta name="author" content="Ölçüm Ağı Çalışma Grubu">
```

`description` değeri, belgenin arama sonuçlarında görünen özeti olarak kullanılabilir.
Kullanılacağı garanti değildir — dizinleyiciler belgenin içeriğinden daha uygun bir özet
üretmeyi seçebilir. Yazılması yine de gerekçelidir: yazılmadığında seçim bütünüyle
dizinleyiciye kalır.

`robots` değeri dizinleyicilere belgenin dizinlenip dizinlenmeyeceğini ve bağlantılarının
izlenip izlenmeyeceğini bildirir. Bu bir erişim denetimi değildir; bildirime uymayan bir
istemci belgeyi yine de okur. Gizlilik gerektiren içerik yetkilendirmeyle korunur,
üstveriyle değil.

Belgeyi başka ortamlarda önizleyen sistemler için ayrı üstveri sözleşmeleri vardır; bunlar
`meta` ögesinin aynı düzeneğini kullanır ve başlık, açıklama, görsel adresi gibi alanları
bildirir. Sözleşme adları platformdan platforma değişir; ortak nokta, önizlemenin belgenin
içeriğinden tahmin edilmesi yerine açıkça bildirilmesidir.

#### Eşdeğer Başlıklar

Dördüncü kullanım, `http-equiv` özniteliğiyle bir HTTP yanıt başlığının eşdeğerini bildirir.

```html
<meta http-equiv="content-security-policy" content="default-src 'self'">
```

Bu düzenek, sunucu başlıklarını değiştirme olanağı olmayan durumlar içindir. Olanak varsa
gerçek başlık yeğlenir: başlık belge ayrıştırılmadan önce geçerli olur, `http-equiv`
bildirimi ise ayrıştırıcı oraya vardığında. Aradaki fark, güvenlik bildirimlerinde
belirleyicidir.

#### İstasyon Belgesinin Baş Bölümü

Kurs boyunca geliştirilen belgenin baş bölümü şu hâle gelir:

```html
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Kuzey Yamaç Ölçüm İstasyonu — Ölçüm Ağı</title>
  <meta name="description" content="Kuzey Yamaç otomatik ölçüm istasyonunun sıcaklık,
        bağıl nem, rüzgâr hızı ve yağış kayıtları.">
</head>
```

Sıra rastlantısal değildir: kodlama bildirimi ilk sıradadır, çünkü kendisinden sonraki her
şeyin nasıl okunacağını belirler. `title` ögesi, önceki derste belirtildiği gibi belgeyi
bağlamı dışında adlandırır ve burada site adını da taşır.

#### Özet

- Karakter kümesi bildirimi baytların hangi kuralla karakterlere çevrileceğini söyler;
  yanlış bildirim belgenin tamamını farklı bir karakter dizisine çözer.
- Bildirim, ayrıştırıcının ön taraması nedeniyle ilk 1024 bayt içinde olmalıdır; geç
  bulunması ayrıştırmanın baştan yapılmasına yol açar.
- Kodlamayı belirleyen kaynakların önceliği vardır ve HTTP başlığı belgedeki bildirimi
  bastırır.
- Görüntü alanı bildirimi, sunum katmanının hangi genişlik üzerinde hesap yapacağını
  belirler; ölçeklemeyi kapatan değerler erişilebilirliği bozar.
- Adlandırılmış üstveri belgeyi işleyen programlar içindir ve bağlayıcı değildir;
  dizinleme bildirimleri bir erişim denetimi sağlamaz.
- `http-equiv` bildirimleri gerçek HTTP başlıklarının yerini tutar ancak daha geç geçerli
  olur; olanak varsa başlık yeğlenir.

#### Sonraki Adım

Baş bölümü tamamlandı; sıra gövdededir. Gövdenin ilk yapısal kararı başlıklardır: belgenin
hangi bölümlere ayrıldığını ve bu bölümlerin birbirine göre düzeyini bildiren etiketler.
Sonraki ders başlık düzeyinin neden bir yazı tipi ölçüsü değil bir yapı bildirimi olduğunu
ve düzeyi atlamanın belgenin ana hattını nasıl bozduğunu gösterir.

### Başlık Hiyerarşisi

Source: https://academia.sh/tr/kurslar/web-temelleri-ve-html/baslik-hiyerarsisi

Başlık düzeyinin yapı bildirimi olarak işlevi, belgenin ana hattının çıkarılması, düzey atlamanın sonucu ve başlıkların gezinme aracı olarak kullanımı.

Baş bölümü tamamlandı; gövdenin yazımı başlıyor. Gövdenin ilk yapısal kararı başlıklardır:
belgenin hangi bölümlere ayrıldığını ve bu bölümlerin birbirine göre düzeyini bildiren
etiketler.

Altı başlık ögesi vardır: `h1` ile `h6`. Sayı, başlığın **düzeyini** verir — birinci düzey
en üsttedir. Bu sayı bir yazı tipi ölçüsü değildir; iç içelik derinliğini bildirir.

#### Başlık Bir Yapı Bildirimidir

Bir başlık ögesi iki iş görür. Kendisinden sonra gelen içeriğe bir ad verir ve o içeriğin
belgedeki derinliğini bildirir. İkinci iş, düzey sayısıyla taşınır.

Bir kitabın içindekiler bölümünü düşünmek yeterlidir: bölüm başlıkları, alt bölüm
başlıkları ve alt alt bölüm başlıkları farklı girinti düzeylerinde yazılır ve girinti,
hangi alt bölümün hangi bölüme ait olduğunu gösterir. HTML'de girintinin karşılığı düzey
sayısıdır.

Bu bilgi kullanılır. Ekran okuyucular belge içinde başlıktan başlığa gezinme sunar;
kullanıcı belgeyi baştan dinlemek yerine ana hattı tarayıp ilgilendiği bölüme atlar. İçerik
çıkaran araçlar belgeyi başlıklara göre bölümler. Dizinleyiciler başlık metnine, gövde
metninden farklı bir ağırlık verir.

#### Ana Hattı Çıkarmak

Başlık düzeyleri bir ağaç tanımlar ve bu ağaç belgeden hesaplanabilir.

```js
// baslik.mjs — baslik duzeyi dizisinden ana hat cikarir
const belge = `
<h1>Kuzey Yamaç İstasyonu</h1>
<h2>Konum</h2>
<h2>Ölçülen Büyüklükler</h2>
<h3>Sıcaklık</h3>
<h3>Bağıl Nem</h3>
<h2>Veri Erişimi</h2>
<h4>Dosya Biçimi</h4>
`;

const basliklar = [...belge.matchAll(/<h([1-6])>([^<]*)<\/h\1>/g)].map((e) => ({
  duzey: Number(e[1]),
  metin: e[2],
}));

let onceki = 0;
const uyarilar = [];
const numara = [];
for (const baslik of basliklar) {
  if (onceki !== 0 && baslik.duzey > onceki + 1) {
    uyarilar.push(`h${onceki} sonrasi h${baslik.duzey}: h${onceki + 1} atlandi (${baslik.metin})`);
  }
  numara.length = baslik.duzey;
  numara[baslik.duzey - 1] = (numara[baslik.duzey - 1] ?? 0) + 1;
  const etiket = [...numara].map((s) => s ?? 0).join(".");
  console.log("  ".repeat(baslik.duzey - 1) + etiket + "  " + baslik.metin);
  onceki = baslik.duzey;
}

console.log("--- denetim ---");
console.log("baslik sayisi :", basliklar.length);
console.log("h1 sayisi     :", basliklar.filter((b) => b.duzey === 1).length);
for (const uyari of uyarilar) console.log("uyari         :", uyari);
```

```
1  Kuzey Yamaç İstasyonu
  1.1  Konum
  1.2  Ölçülen Büyüklükler
    1.2.1  Sıcaklık
    1.2.2  Bağıl Nem
  1.3  Veri Erişimi
      1.3.0.1  Dosya Biçimi
--- denetim ---
baslik sayisi : 7
h1 sayisi     : 1
uyari         : h2 sonrasi h4: h3 atlandi (Dosya Biçimi)
```

Son başlık kusuru görünür kılar. `Veri Erişimi` ikinci düzeydeyken `Dosya Biçimi` dördüncü düzeye
yazılmış; üçüncü düzey atlanmıştır. Numaralandırmada bu, boş bir basamak olarak `1.3.0.1`
biçiminde belirir: ana hatta karşılığı olmayan bir düzey vardır.

#### Düzey Atlamanın Sonucu

Atlanan düzey, ana hatta bir boşluk bırakır. Belgeyi başlıktan başlığa gezen bir kullanıcı,
`Veri Erişimi` ile `Dosya Biçimi` arasında var olmayan bir ara bölüm bekler ve bulamaz.
Sonuç bir görsel kusur değil, yanlış bir yapı bildirimidir.

Atlamanın nedeni çoğunlukla sunumla ilgilidir: yazar, dördüncü düzey başlığın varsayılan
görünümünü daha uygun bulmuştur. Bu, birinci derste tanımlanan katman ihlalidir. Doğru
çözüm düzeyi yapıya göre seçmek ve görünümü sunum katmanında düzeltmektir.

Ters yönde bir kural yoktur: düzey **azalırken** atlamak kusur değildir. Üçüncü düzey bir
bölümden sonra ikinci düzeye dönmek, alt bölümün bittiğini ve yeni bir üst bölüm
başladığını bildirir; bu olağan bir kapanıştır.

#### Belgede Kaç Birinci Düzey Başlık

Bu konuda iki savunulabilir yaklaşım vardır ve ikisi de kullanımdadır.

**Tek `h1` yaklaşımı**: belgede yalnızca bir birinci düzey başlık bulunur ve belgenin
konusunu adlandırır. Diğer her şey onun altındadır. Bu yaklaşım ana hattı tek köklü bir
ağaç yapar ve belgeyi bağlamı dışında özetlemeyi kolaylaştırır.

**Bölümlere göre `h1` yaklaşımı**: her bölümleme ögesi kendi başlık düzeyini yeniden
başlatır ve düzey, iç içelikten hesaplanır. Bu yaklaşımın dayandığı ana hat hesabı, ekran
okuyucu gerçekleştirimlerinde tutarlı biçimde desteklenmemiştir.

Bu kurs birinci yaklaşımı izler: belgede tek bir `h1`, altında sayı sırasına uyan başlıklar.
Gerekçe kuramsal üstünlük değil, gerçekleştirimlerin bu düzeni tutarlı biçimde
yorumlamasıdır. Bir yapı bildiriminin değeri, onu okuyan araçların ondan aynı sonucu
çıkarmasına bağlıdır.

#### Başlığın İçeriği

Başlık ögesi metin taşır ve metin içi işaretlemeye izin verir: bir başlıkta kısaltma,
kod parçası veya vurgu bulunabilir. Bulunmaması gereken şey, ayrı bir bölüme ait olan
içeriktir — başlık bir bölümü adlandırır, kendisi bölümün içeriği değildir.

Başlık metni bağlamı dışında da anlaşılır olmalıdır. Başlıktan başlığa gezinen bir
kullanıcı, gövde metnini görmeden yalnızca başlıkları duyar. "Ayrıntılar", "Devamı" veya
"Bölüm 3" gibi başlıklar bu kullanımda bilgi taşımaz.

Bir belgede başlık ögesiyle yazılmamış ama görsel olarak başlık gibi duran metin varsa,
ana hatta o bölüm yoktur. Tersi de geçerlidir: yalnızca büyük görünsün diye başlık ögesine
konmuş bir metin, ana hatta var olmayan bir bölüm ekler.

#### Alt Başlık Sorunu

Bir başlığın altına, onu tamamlayan kısa bir tanıtım satırı yazmak sık karşılaşılan bir
gereksinimdir. Bu satır ikinci düzey bir başlık ögesine konursa ana hatta var olmayan bir
bölüm eklenmiş olur: gezinme sırasında kullanıcı bir alt bölüm bekler, oysa yalnızca bir
tanıtım cümlesi vardır.

```html
<h1>Kuzey Yamaç Ölçüm İstasyonu</h1>
<h2>1840 metrede, dört büyüklük</h2>   <!-- ana hatta sahte bir bolum ekler -->
```

Doğru yazım, tanıtım satırını başlık ögesi dışında bırakır ve ikisini bir arada tutar:

```html
<header>
  <h1>Kuzey Yamaç Ölçüm İstasyonu</h1>
  <p>1840 metrede, dört büyüklük</p>
</header>
```

Burada ana hatta yalnızca bir giriş vardır; tanıtım satırı bir paragraftır ve başlıkla
görsel ilişkisi sunum katmanında kurulur. Kullanılan `header` ögesi, bir sonraki konunun
konusudur; buradaki işlevi, iki ögeyi tek bir tanıtım bloğu olarak gruplamaktır.

Aynı akıl yürütme, başlık metninin bir bölümünü küçük göstermek için ikinci bir başlık
ögesi açmaya da uygulanır. Ana hatta bir giriş eklemek isteniyorsa başlık ögesi kullanılır;
istenmiyorsa kullanılmaz. Görünüm bu kararın ölçütü değildir.

#### İstasyon Belgesinin Ana Hattı

Kurs boyunca geliştirilen belgenin gövdesi başlıklarla bölümlenir:

```html
<body>
  <h1>Kuzey Yamaç Ölçüm İstasyonu</h1>
  <p>Deniz seviyesinden 1840 metre yükseklikte kurulu otomatik ölçüm istasyonu.</p>

  <h2>Konum ve Kuruluş</h2>
  <p>İstasyon, kuzeye bakan yamacın orta kesiminde yer alır.</p>

  <h2>Ölçülen Büyüklükler</h2>
  <h3>Sıcaklık</h3>
  <p>Ölçüm aralığı on dakikadır.</p>
  <h3>Bağıl Nem</h3>
  <p>Ölçüm aralığı on dakikadır.</p>

  <h2>Veri Erişimi</h2>
  <p>Kayıtlar günlük dosyalar hâlinde yayımlanır.</p>
</body>
```

Bu iskelet, belgenin geri kalanının üzerine kurulacağı yapıdır. Sonraki derslerde her
bölümün içi doldurulacak; başlık düzeni değişmeyecektir.

#### Özet

- Başlık düzeyi bir yazı tipi ölçüsü değil, belgedeki derinliği bildiren yapı verisidir.
- Başlıklar bir ana hat tanımlar; bu ana hat gezinme, içerik çıkarma ve dizinleme
  araçlarınca kullanılır.
- Düzey artarken atlamak ana hatta karşılığı olmayan bir boşluk bırakır; düzey azalırken
  atlamak kusur değildir.
- Belgede tek bir birinci düzey başlık bulundurma yaklaşımı, gerçekleştirimlerin tutarlı
  yorumu nedeniyle bu kursta izlenir.
- Başlık metni bağlamı dışında da anlaşılır olmalıdır; başlıktan başlığa gezen kullanıcı
  gövdeyi görmez.

#### Sonraki Adım

Başlıklar bölümleri adlandırdı; içlerini paragraflar dolduracak. Paragraf metninin
kendisi de yapısızdır: bir vurgunun neden vurgu olduğu, bir kısaltmanın neyin karşılığı
olduğu, alıntının nereden geldiği bildirilmedikçe kaybolur. Sonraki ders metin içi
işaretlemeyi ele alır ve bu arada ayrıştırıcının metne uyguladığı bir dönüşümü gösterir:
kaynak metindeki boşluk ve satır sonlarının ne olduğunu.

### Metin Etiketleri

Source: https://academia.sh/tr/kurslar/web-temelleri-ve-html/metin-etiketleri

Vurgu, alıntı, kısaltma, tanım ve kod işaretlemesi; beyaz boşluğun daraltılması ve satır sonu davranışının kaynak metne bağımlılığı.

Başlıklar bölümleri adlandırdı; bu ders bölümlerin içini dolduran metni işaretler. Paragraf
metni ilk bakışta yapısız görünür, ancak içinde bildirilmeyi bekleyen bilgi vardır: bir
sözcüğün neden öne çıktığı, bir kısaltmanın neyin karşılığı olduğu, bir alıntının nereden
geldiği.

Bu ders ayrıca metne uygulanan bir dönüşümü gösterir: kaynak metindeki boşluklar ve satır sonları
ekrana olduğu gibi çıkmaz.

#### Paragraf

`p` ögesi bir paragraf bildirir. Başka bir paragrafı, listeyi veya başlığı içeremez;
Belge Nesne Modeli dersinde görüldüğü gibi, böyle bir öge açıldığında açık paragraf
kendiliğinden kapanır.

Paragraflar arasındaki boşluk sunum katmanının kararıdır. Metni ayırmak için boş
paragraflar yazmak, ana hatta anlamsız düğümler ekler ve ekran okuyucuda boş paragraf
olarak duyurulabilir.

#### Vurgu ve Önem

Dört öge sık karıştırılır çünkü varsayılan görünümleri ikişer ikişer aynıdır.

| Öge | Bildirdiği şey |
|---|---|
| `em` | Cümledeki vurgunun bu sözcükte olduğu |
| `strong` | İçeriğin önemli, ciddi veya ivedi olduğu |
| `i` | Metnin akıştan ayrı bir nitelik taşıdığı (yabancı sözcük, terim, düşünce) |
| `b` | Metnin dikkat çekmesi gerektiği, ek anlam yüklemeden |

`em` ile `i` arasındaki fark okunuşta belirir. `em`, cümlenin anlamını değiştiren
vurgudur:

```html
<p><em>Bu</em> istasyon 1840 metrededir.</p>
<p>Bu istasyon <em>1840</em> metrededir.</p>
```

İki cümle aynı sözcükleri taşır ve farklı şeyler söyler; birincisi hangi istasyonun söz
konusu olduğunu, ikincisi yüksekliğin değerini öne çıkarır. Ekran okuyucular bu ayrımı
seslendirebilir.

`strong` ise vurgu değil önem bildirir; okunuşu değiştirmez, içeriğin ağırlığını
bildirir. İç içe kullanıldığında önem derecesi artar.

`i` ve `b` ögeleri anlam yüklemez. Bir Latince tür adını `i` ile, bir ürün kodunu `b` ile
işaretlemek geçerlidir; bu ögeler "eğik yaz" ve "kalın yaz" komutları değil, "burada
farklı bir şey var" bildirimleridir. Yalnızca görünüm isteniyorsa doğru yer sunum
katmanıdır.

#### Alıntılar

İki öge vardır ve ayrım uzunlukta değil, akışta olup olmamasındadır.

`q`, cümle içinde geçen alıntıdır. `blockquote`, kendi başına duran alıntı bloğudur.
İkisi de `cite` özniteliğiyle kaynağın adresini taşıyabilir.

```html
<p>Kurulum raporunda <q cite="/rapor/2019">yamacın kuzey yüzü seçilmiştir</q>
   denmektedir.</p>

<blockquote cite="/rapor/2019">
  <p>Yamacın kuzey yüzü, gün boyunca doğrudan ışık almadığı için sıcaklık
     ölçümünde daha az sapma verir.</p>
  <footer>Kurulum Raporu, <cite>Ölçüm Ağı Teknik Notları</cite></footer>
</blockquote>
```

`cite` **özniteliği** kaynağın adresini taşır ve çoğu tarayıcıda görünmez. `cite`
**ögesi** ise bir eserin adını işaretler — kitap, rapor, makale. İkisi ayrı şeylerdir ve
`cite` ögesi bir kişinin adını işaretlemek için kullanılmaz; eserin adını işaretler.

#### Kısaltma ve Tanım

`abbr` bir kısaltmayı işaretler ve `title` özniteliğiyle açılımını taşır. `dfn` bir terimin
**tanımlandığı** yeri işaretler — terimin her geçtiği yeri değil.

```html
<p><dfn><abbr title="deniz seviyesinden yükseklik">DSY</abbr></dfn>, bir noktanın
   ortalama deniz düzeyine göre dikey uzaklığıdır. Kuzey Yamaç istasyonunun
   <abbr title="deniz seviyesinden yükseklik">DSY</abbr> değeri 1840 metredir.</p>
```

`title` özniteliğinin içeriğine yalnızca fare imleci üzerine geldiğinde erişilebilir;
klavyeyle veya dokunmatik ekranla erişimi güvenilir değildir. Bu nedenle açılım, belgede
en az bir kez açık metin olarak da yazılır — yukarıdaki tanım cümlesi bu işi görür.

#### Kod, Girdi ve Değişken

Dört öge teknik metin içindir ve varsayılan olarak eşit aralıklı yazı tipiyle gösterilir:
`code` bir kod parçasını, `kbd` kullanıcının bastığı tuşları, `samp` bir programın
çıktısını, `var` bir değişken adını işaretler.

```html
<p><code>indir.sh</code> betiği <kbd>Ctrl</kbd> + <kbd>C</kbd> ile durdurulduğunda
   <samp>kesildi: kismi dosya silindi</samp> iletisini yazar.</p>
```

Ayrımın değeri arama ve dönüştürmededir: bir belgedeki tüm komut örneklerini çıkarmak,
`code` ögeleri işaretlenmişse tek bir sorguyla yapılır.

#### Beyaz Boşluk Daraltılır

Kaynak metindeki boşluk dizileri belgeye olduğu gibi **girer**; daraltma ayrıştırmada değil, sunum
katmanının varsayılan `white-space` kuralıyla çizim sırasında olur. Boşluk, sekme ve satır sonu
dizileri tek bir boşluğa iner; bölümün başındaki ve sonundaki boşluk atılır.

```js
// bosluk.mjs — beyaz bosluk daraltma kuralinin modeli
function daralt(metin) {
  return metin.replace(/[ \t\n\r]+/g, " ").replace(/^ | $/g, "");
}

const kaynak = "Ölçüm       aralığı\n        on dakikadır.\n\n    Kayıt saati UTC'dir.";
console.log("kaynak (karakter):", kaynak.length);
console.log(JSON.stringify(kaynak));
console.log("--- daraltildiktan sonra ---");
const sonuc = daralt(kaynak);
console.log("sonuc (karakter) :", sonuc.length);
console.log(JSON.stringify(sonuc));

console.log("--- bolunmez bosluk daralmaz ---");
const sirali = "18" + "   " + "derece";                   // uc sirali bosluk
const bolunmez = "18" + "\u00a0\u00a0\u00a0" + "derece"; // uc kez &nbsp;
console.log("sirali  :", daralt(sirali).length, "karakter");
console.log("bolunmez:", daralt(bolunmez).length, "karakter");

console.log("--- ard arda satirlarda etiket araligi ---");
const metinIcerigi = (d) => daralt(d.replace(/<[^>]+>/g, ""));
console.log("satir sonlu:", JSON.stringify(metinIcerigi("<em>güney</em>\n<em>kuzey</em>")));
console.log("bitisik    :", JSON.stringify(metinIcerigi("<em>güney</em><em>kuzey</em>")));
```

```
kaynak (karakter): 67
"Ölçüm       aralığı\n        on dakikadır.\n\n    Kayıt saati UTC'dir."
--- daraltildiktan sonra ---
sonuc (karakter) : 48
"Ölçüm aralığı on dakikadır. Kayıt saati UTC'dir."
--- bolunmez bosluk daralmaz ---
sirali  : 9 karakter
bolunmez: 11 karakter
--- ard arda satirlarda etiket araligi ---
satir sonlu: "güney kuzey"
bitisik    : "güneykuzey"
```

Kuralın üç sonucu vardır. Birincisi, kaynak metin okunabilirlik için serbestçe girintilenip
satırlara bölünebilir; belgeye geçen metin değişmez. İkincisi, `&nbsp;` karakter
başvurusuyla yazılan **bölünmez boşluk (non-breaking space)** bu daraltmaya girmez; iki
sözcüğün ayrılmasını engellemek gerektiğinde kullanılır. Sıradan boşluk yerine
kullanılması, metnin arama sonuçlarında bulunamamasına yol açar. Üçüncüsü, son iki
satırın gösterdiği gibi, iki ögeyi ayrı satırlara yazmak aralarına bir boşluk koyar; yan
yana yazmak koymaz. Kaynak metnin
biçimlendirilmesi, görünen sonucu değiştirebilir.

#### Satır Sonu ve Önbiçimli Metin

Daraltma kuralının iki istisnası vardır.

`br` boş ögesi zorunlu bir satır sonu koyar. Yalnızca satır sonunun içeriğin parçası
olduğu yerlerde kullanılır — adres, şiir, ayrık kısa satırlar. Paragrafları ayırmak için
kullanılmaz.

`pre` ögesi içindeki metinde boşluk ve satır sonları korunur. Kod listeleri ve hizalı
çıktılar bu ögeye konur; teknik belgelerde `pre` ile `code` birlikte kullanılır.

```html
<pre><code>saat,sicaklik,nem
07:00,-4.2,72
07:10,-4.1,71</code></pre>
```

Açılış etiketinden hemen sonraki satır sonu ayrıştırıcı tarafından atılır; kapanış
etiketinden önceki atılmaz. Bu yüzden yukarıdaki örnekte kapanış etiketi son satıra
bitişik yazılmıştır.

#### Özet

- `em` cümledeki vurguyu, `strong` içeriğin önemini bildirir; `i` ve `b` anlam yüklemeden
  metnin ayrı bir nitelik taşıdığını gösterir.
- `q` akış içindeki, `blockquote` kendi başına duran alıntıdır; `cite` özniteliği kaynağın
  adresini, `cite` ögesi bir eserin adını taşır.
- `abbr` kısaltmanın açılımını `title` ile taşır; bu içeriğe erişim güvenilir olmadığı için
  açılım belgede en az bir kez açık metin olarak da yazılır.
- Beyaz boşluk dizileri tek boşluğa iner ve kenarlardaki boşluk atılır; bölünmez boşluk bu
  daraltmaya girmez.
- İki ögenin ayrı satırlara yazılması aralarına bir boşluk koyar; kaynak metnin
  biçimlendirilmesi görünen sonucu etkileyebilir.
- `br` yalnızca satır sonunun içeriğin parçası olduğu yerlerde kullanılır; `pre` içinde
  boşluk ve satır sonu korunur.

#### Sonraki Adım

Metin içi işaretleme paragrafın içini yapılandırdı. Bazı içerikler ise paragraf değildir:
sıralı adımlar, birbirine denk maddeler, terim–açıklama çiftleri. Bunları paragraf olarak
yazmak yapı bilgisini yok eder. Sonraki ders liste ögelerini ele alır ve sıralı listelerin
numaralandırma kuralını, numaranın nereden başladığını belirleyen özniteliklerle birlikte
gösterir.

### Listeler

Source: https://academia.sh/tr/kurslar/web-temelleri-ve-html/listeler

Sırasız, sıralı ve tanım listelerinin ayrımı; sıralı liste numaralandırmasının kuralı, iç içe yazım ve liste seçiminin yapısal gerekçesi.

Metin içi işaretleme paragrafın içini yapılandırdı. Bazı içerikler ise paragraf değildir:
sıralı adımlar, birbirine denk maddeler, terim–açıklama çiftleri. Bunları paragraf olarak
yazmak yapı bilgisini yok eder — okuyucu maddelerin sayısını, sırasının anlamlı olup
olmadığını ve nerede bittiğini kestiremez.

Üç liste türü vardır ve seçim, içeriğin doğasına göre yapılır.

#### Üç Tür

**Sırasız liste (`ul`)**, maddelerin sırasının anlam taşımadığı listedir. Maddeler yer
değiştirse belgenin anlamı değişmez.

**Sıralı liste (`ol`)**, sıranın anlam taşıdığı listedir: adımlar, sıralama, öncelik. Yer
değiştirme anlamı bozar.

**Tanım listesi (`dl`)**, ad–değer çiftlerinden oluşur: terim ve açıklaması, alan ve
içeriği, soru ve yanıtı.

Seçim ölçütü görünüm değildir. Bir sırasız listenin madde imlerini kaldırmak ya da bir
sıralı listenin numaralarını gizlemek sunum katmanının işidir; hangi ögenin seçileceğini
sıranın anlamlı olup olmadığı belirler.

#### Madde Ögesi ve Kapsayıcısı

`li` ögesi bir liste maddesidir ve yalnızca bir liste ögesinin doğrudan çocuğu olabilir.
Doğrudan gövdeye yazılmış bir `li` ögesi geçersizdir.

`li` ögesinin kapanış etiketi yazılmayabilir; Belge Nesne Modeli dersinde görüldüğü gibi
yeni bir `li` açıldığında öncekisi kendiliğinden kapanır. Yazılması yine de yeğlenir.

Liste ögesinin doğrudan çocuğu olarak metin yazmak geçersizdir: `ul` ve `ol` ögelerinin
çocukları yalnızca `li` olabilir. Aradaki boşluk ve satır sonları yok sayılır, ancak
gerçek metin ayrıştırıcı tarafından listenin dışına taşınır.

```html
<ul>
  <li>Sıcaklık</li>
  <li>Bağıl nem</li>
  <li>Rüzgâr hızı</li>
  <li>Yağış</li>
</ul>
```

#### Numaralandırma Kuralı

Sıralı listenin numaraları varsayılan olarak birden başlar ve birer artar. Üç öznitelik bu
davranışı değiştirir: `start` başlangıç değerini, `reversed` azalan sıralamayı, `value` tek
bir maddenin değerini belirler. `value` yazıldığında sonraki maddeler oradan devam eder.

```js
// listeler.mjs — sirali liste numaralandirma kurali
function numarala(liste) {
  const n = liste.ogeler.length;
  const baslangic = liste.start ?? (liste.reversed ? n : 1);
  const sonuc = [];
  let sayac = baslangic;
  for (const oge of liste.ogeler) {
    const deger = oge.value ?? sayac;
    sonuc.push([deger, oge.metin]);
    sayac = deger + (liste.reversed ? -1 : 1);
  }
  return sonuc;
}

const listeler = [
  { ad: "varsayilan", ogeler: [{ metin: "gövde açılır" }, { metin: "sensör okunur" }, { metin: "kayıt yazılır" }] },
  { ad: "start=5", start: 5, ogeler: [{ metin: "gövde açılır" }, { metin: "sensör okunur" }] },
  { ad: "reversed", reversed: true, ogeler: [{ metin: "gövde açılır" }, { metin: "sensör okunur" }, { metin: "kayıt yazılır" }] },
  { ad: "value ile atlama", ogeler: [{ metin: "gövde açılır" }, { metin: "sensör okunur", value: 7 }, { metin: "kayıt yazılır" }] },
];

for (const liste of listeler) {
  console.log("[" + liste.ad + "]");
  for (const [numara, metin] of numarala(liste)) console.log("  " + numara + ". " + metin);
}
```

```
[varsayilan]
  1. gövde açılır
  2. sensör okunur
  3. kayıt yazılır
[start=5]
  5. gövde açılır
  6. sensör okunur
[reversed]
  3. gövde açılır
  2. sensör okunur
  1. kayıt yazılır
[value ile atlama]
  1. gövde açılır
  7. sensör okunur
  8. kayıt yazılır
```

`reversed` durumunda başlangıç değerinin madde sayısından geldiğine dikkat edilmelidir:
liste üç maddeliyse üçten başlar. `start` yazılırsa o değer geçerli olur.

Bu değerler yalnızca görünüm değildir; belgenin taşıdığı veridir. Uzun bir yordamın ikinci
sayfasında `start="8"` yazmak, numaraları sunum katmanında elle yazmaktan farklıdır:
sekizinci adım olduğu belgede bildirilmiş olur.

Numaraların biçimi —rakam, harf, roma sayısı— çoğunlukla bir sunum kararıdır ve sunum katmanında
belirlenir; biçimin kendisine metinde atıf yapılıyorsa `type` özniteliği içeriğe yazılır.

#### Tanım Listeleri

`dl` ögesi ad–değer çiftlerini taşır. `dt` adı, `dd` değeri işaretler. İkisi arasındaki
ilişki komşuluktan çıkar: bir `dt` ögesini izleyen `dd` ögeleri ona aittir.

```html
<dl>
  <dt>Kuruluş yüksekliği</dt>
  <dd>1840 m</dd>

  <dt>Ölçüm aralığı</dt>
  <dd>10 dakika</dd>

  <dt>Sıcaklık</dt>
  <dt>Bağıl nem</dt>
  <dd>Aynı sensör gövdesinde ölçülür.</dd>
</dl>
```

Son grup, birden çok adın tek bir açıklamayı paylaşabildiğini gösterir. Tersi de
geçerlidir: bir ada birden çok `dd` ögesi verilebilir.

Bir çifti tek bir birim olarak gruplamak gerekirse `dt` ve `dd` ögeleri bir `div` içine
alınabilir; bu, `dl` ögesinin doğrudan çocuğu olarak izin verilen tek ek ögedir.

Tanım listesi, adı düşündürdüğünün ötesinde kullanılır: bir künye, bir üstveri çizelgesi,
bir soru–yanıt kümesi de ad–değer yapısındadır. Ölçüt sözlük olmak değil, her maddenin bir
adı ve ona bağlı bir değeri olmasıdır.

#### İç İçe Listeler

Bir alt liste, üst listenin maddesinin **içine** yazılır — maddeler arasına değil.

```html
<ul>
  <li>Sıcaklık
    <ul>
      <li>Gövde sıcaklığı</li>
      <li>Toprak sıcaklığı</li>
    </ul>
  </li>
  <li>Bağıl nem</li>
</ul>
```

Alt listeyi `li` ögeleri arasına, doğrudan `ul` ögesinin çocuğu olarak yazmak yapıyı
bozar: alt liste hangi maddeye ait olduğunu bildirmez. Görünüm çoğu durumda benzer
kalacağı için kusur gözden kaçar; ekran okuyucuda ise iç içelik yanlış duyurulur.

İç içe yazımda kapanış etiketlerinin yazılması özellikle önemlidir. Dış maddenin kapanışı
yazılmazsa, kendiliğinden kapanma kuralı alt listenin bitiminde değil, sonraki `li`
açılışında işler; sonuç genellikle beklenendir ama kaynak metin yanıltıcı hâle gelir.

#### Listenin Duyurduğu Bilgi

Liste ögesinin değeri madde imlerinde değil, taşıdığı sayımdadır. Ekran okuyucular bir
listeye girildiğinde madde sayısını ve o an kaçıncı maddede olunduğunu duyurur. Aynı
içeriği ard arda paragraflar olarak yazmak bu bilgiyi yok eder: kullanıcı kaç maddenin
olduğunu ancak sonuna vardığında öğrenir.

Bu ölçüt, listenin ne zaman kullanılmayacağını da belirler. Tek maddelik bir liste bir
sayım bilgisi taşımaz ve yalnızca girinti için yazılmışsa gereksizdir. Her satırı ayrı bir
liste olan bir yapı da aynı biçimde yanıltıcıdır: sayım her seferinde bire iner.

İki boyutlu veri de liste değildir. Her maddenin birden çok alanı varsa —her ölçümün
saati, değeri ve birimi gibi— yapı bir çizelgedir ve konunun son dersinde ele alınacak
tablo ögeleriyle yazılır. Listeyi tabloya çeviren ölçüt, maddelerin karşılaştırılabilir
alanlara bölünüp bölünmediğidir.

#### İstasyon Belgesinde

Ölçüm istasyonu sayfasının iki listesi vardır ve türleri farklıdır:

```html
<h2>Ölçülen Büyüklükler</h2>
<ul>
  <li>Sıcaklık</li>
  <li>Bağıl nem</li>
  <li>Rüzgâr hızı</li>
  <li>Yağış</li>
</ul>

<h2>Kalibrasyon Yordamı</h2>
<ol>
  <li>Gövde kapağı açılır.</li>
  <li>Sensör bağlantıları sökülmeden okuma alınır.</li>
  <li>Referans değerle karşılaştırılır.</li>
  <li>Sapma kaydedilir ve gövde kapatılır.</li>
</ol>
```

Ölçülen büyüklüklerin sırası anlam taşımaz; kalibrasyon adımlarınınki taşır. Ayrım, iki
ögenin görünümüyle değil, içeriğin doğasıyla verilmiştir.

#### Özet

- Sırasız liste sıranın anlamsız, sıralı liste anlamlı olduğu durumdadır; tanım listesi
  ad–değer çiftlerini taşır.
- `li` ögesi yalnızca bir liste ögesinin doğrudan çocuğu olabilir; liste ögesinin doğrudan
  çocuğu olarak metin yazılamaz.
- Sıralı listede `start`, `reversed` ve `value` numaralandırmayı belirler; `value`
  yazıldığında sonraki maddeler oradan devam eder.
- Tanım listesinde bir ada birden çok değer, bir değere birden çok ad bağlanabilir; ilişki
  komşuluktan çıkar.
- Alt liste, ait olduğu maddenin içine yazılır; maddeler arasına yazmak iç içelik bilgisini
  bozar.

#### Sonraki Adım

Listeler içeriği gruplandırdı ama belge hâlâ kendi içine kapalıdır: başka bir belgeye
geçmenin yolu yoktur. Sonraki ders bağlantıları ele alır ve göreli bir adresin nasıl
çözüldüğünü —hangi tabana göre, hangi kuralla— gerçek çözümleme çıktıları üzerinden
gösterir.

### Bağlantılar

Source: https://academia.sh/tr/kurslar/web-temelleri-ve-html/baglantilar

Göreli ve mutlak adreslerin çözülmesi, taban adresin belirlenmesi, bağlantı metninin taşıdığı bilgi ve bağlantı ilişkisi bildirimleri.

Listeler içeriği gruplandırdı ama belge hâlâ kendi içine kapalıdır. Bağlantı, bir belgeyi
başka bir kaynağa bağlayan ögedir ve web'i bir belge yığını olmaktan çıkarıp bir ağ yapan
şeydir.

Bağlantı ögesi `a`, hedefini `href` özniteliğiyle bildirir. Bu ders özniteliğin değerinin
nasıl yorumlandığını, metnin ne taşıması gerektiğini ve bağlantının hedefiyle ilişkisini
bildiren özniteliklerini ele alır.

#### Adresin Çözülmesi

İnternet Nasıl Çalışır kursundaki Tarayıcı Ne Yapar dersi bir adresin bölümlerini —şema,
konak, bağlantı noktası, yol, sorgu, parça— ayırmıştı. Belgede yazılan adres çoğu zaman bu
bölümlerin tamamını taşımaz; eksik bölümler **taban adresten** tamamlanır.

Taban adres, varsayılan olarak belgenin kendi adresidir. Çözümleme kuralı tanımlıdır ve
her istemcide aynı sonucu verir.

```js
// baglantilar.mjs — goreli adreslerin taban adrese gore cozulmesi
const taban = "https://ornek.test/istasyon/kuzey-yamac/olcumler.html";

const adaylar = [
  "gunluk.html",
  "./gunluk.html",
  "../ozet.html",
  "../../dizin.html",
  "/hakkinda.html",
  "//baska.test/veri.json",
  "https://baska.test/veri.json",
  "?tur=nem",
  "#sicaklik",
  "veri/2024.csv",
];

console.log("taban:", taban);
for (const aday of adaylar) {
  console.log(aday.padEnd(28), "->", new URL(aday, taban).href);
}

console.log("--- base ogesi tabani degistirir ---");
const yeniTaban = new URL("/arsiv/", taban).href;
console.log("yeni taban:", yeniTaban);
console.log("gunluk.html".padEnd(28), "->", new URL("gunluk.html", yeniTaban).href);
```

```
taban: https://ornek.test/istasyon/kuzey-yamac/olcumler.html
gunluk.html                  -> https://ornek.test/istasyon/kuzey-yamac/gunluk.html
./gunluk.html                -> https://ornek.test/istasyon/kuzey-yamac/gunluk.html
../ozet.html                 -> https://ornek.test/istasyon/ozet.html
../../dizin.html             -> https://ornek.test/dizin.html
/hakkinda.html               -> https://ornek.test/hakkinda.html
//baska.test/veri.json       -> https://baska.test/veri.json
https://baska.test/veri.json -> https://baska.test/veri.json
?tur=nem                     -> https://ornek.test/istasyon/kuzey-yamac/olcumler.html?tur=nem
#sicaklik                    -> https://ornek.test/istasyon/kuzey-yamac/olcumler.html#sicaklik
veri/2024.csv                -> https://ornek.test/istasyon/kuzey-yamac/veri/2024.csv
--- base ogesi tabani degistirir ---
yeni taban: https://ornek.test/arsiv/
gunluk.html                  -> https://ornek.test/arsiv/gunluk.html
```

Çıktı beş ayrı biçimi ayırt eder.

**Aynı dizin göreli**: `gunluk.html` ve `./gunluk.html` aynı sonucu verir; belgenin
bulunduğu dizin tabandır. Belgenin adının çözümlemede atıldığına dikkat edilmelidir —
`olcumler.html` sonucun parçası değildir.

**Üst dizin göreli**: `../` her yinelemede bir dizin yukarı çıkar.

**Kök göreli**: `/` ile başlayan adres, yolu kökten yazar; konak ve şema tabandan gelir.
Belge dizin ağacında taşındığında bu adresler değişmez, göreli olanlar değişir.

**Şema göreli**: `//` ile başlayan adres yalnızca şemayı tabandan alır. Şemayı belgeye
bağlamak, güvenli olmayan bir bağlamdan güvenli olmayan bir hedefe geçilmesine izin verdiği
için yeğlenmez; şema açıkça yazılır.

**Aynı belge göreli**: yalnızca sorgu veya parça taşıyan adresler belgenin kendi yolunu
korur. `#sicaklik` biçimindeki parça adresleri ağa hiç çıkmaz; hedef, belgede o kimliği
taşıyan ögedir.

#### Taban Adresin Değiştirilmesi

Baş bölümüne yazılan `base` ögesi, belgedeki tüm göreli adreslerin tabanını değiştirir.

```html
<head>
  <base href="/arsiv/">
</head>
```

Çıktının son bölümü etkiyi gösterir: aynı `gunluk.html` değeri başka bir adrese
çözülür. Bu öge belgede en çok bir kez bulunur ve kendisinden sonra yazılan adresleri
etkiler; önce yazılmasının nedeni budur.

Etkisi belge geneline yayıldığı için bu öge dikkatle kullanılır. Bir bölümü etkileyen
değil, her göreli adresi etkileyen bir karardır — belgedeki parça adresleri de dahil.

#### Adresteki Karakterler

Adres, sınırlı bir karakter kümesiyle yazılır. Bu kümenin dışındaki karakterler —Türkçe
harfler, boşluk— **yüzde kodlamasıyla** baytlara çevrilir.

```js
// kodlama.mjs — adreste ASCII disi karakterler ve bosluklar
const taban = "https://ornek.test/istasyon/";
for (const aday of ["ölçüm raporu.pdf", "veri/2024 mart.csv", "arşiv/kuzey-yamaç"]) {
  console.log(JSON.stringify(aday), "->", new URL(aday, taban).pathname);
}
console.log("--- konak adi ayri bir kuralla kodlanir ---");
console.log(new URL("https://ölçüm.test/veri").href);
```

```
"ölçüm raporu.pdf" -> /istasyon/%C3%B6l%C3%A7%C3%BCm%20raporu.pdf
"veri/2024 mart.csv" -> /istasyon/veri/2024%20mart.csv
"arşiv/kuzey-yamaç" -> /istasyon/ar%C5%9Fiv/kuzey-yama%C3%A7
--- konak adi ayri bir kuralla kodlanir ---
https://xn--lm-4ia2d6a.test/veri
```

Her `%XX` çifti bir bayttır ve baytlar Karakter Kodlamaları dersinde tanımlanan kuralla
üretilmiştir; `ö` harfi iki bayt tuttuğu için iki çifte çevrilir. Boşluk `%20` olur.

Son satır konak adının ayrı bir kuralla ele alındığını gösterir: yol bölümü yüzde
kodlamasıyla, konak adı ise Alan Adı Nedir dersinde belirtilen ASCII gösterimiyle
yazılır. Aynı adresin iki bölümü iki farklı kodlamaya girer.

Dosya adlarını ASCII karakterlerle yazmak bu dönüşümü gereksiz kılar ve adresi okunabilir
tutar; zorunlu değildir ancak kaynak metinde adresin kısa kalmasını sağlar.

#### Bağlantı Metni

Bağlantının metni, hedefin ne olduğunu bildirmelidir. Bunun ölçütü çevresindeki cümle
değildir: ekran okuyucular belge içindeki bağlantıları bir liste hâlinde sunar ve kullanıcı
bu listeyi metinlerine bakarak tarar.

```html
<!-- baglamdan koparildiginda bilgi tasimaz -->
<p>Günlük ölçüm dosyaları için <a href="/veri/gunluk">buraya tıklayın</a>.</p>

<!-- kendi basina anlasilir -->
<p>Ölçümler <a href="/veri/gunluk">günlük dosyalar hâlinde</a> yayımlanır.</p>
```

İkinci yazımda bağlantı metni hedefi tanımlar. Aynı belgede aynı metni taşıyan iki
bağlantının farklı hedeflere gitmesi de kaçınılması gereken bir durumdur.

Bağlantı, belge dışına çıkmayan bir eyleme —bir kaydı silmek, bir formu göndermek— değil,
bir kaynağa gitmek içindir. Eylemler için düğme ögesi kullanılır; ayrım, konunun form
derslerinde ele alınacaktır.

#### Hedefin Türü ve İlişkisi

`download` özniteliği, hedefin görüntülenmek yerine indirilmesini ister ve isteğe bağlı olarak
dosya adını verir; yalnız aynı kökendeki adreslerde ve `blob:`/`data:` şemalarında geçerlidir,
çapraz kökende yok sayılır. `type` özniteliği hedefin içerik türünü bildirir; bir ipucudur,
bağlayıcı değildir.

`rel` özniteliği bağlantının hedefiyle **ilişkisini** bildirir. Değerleri boşlukla ayrılmış
anahtar sözcüklerdir ve her biri tanımlıdır.

```html
<p>Ham veri: <a href="/veri/2024-03.csv" download type="text/csv">Mart ayı kayıtları</a></p>

<p>Yöntem, <a href="https://baska.test/standart" rel="noopener external">ölçüm
   standardında</a> tanımlanmıştır.</p>
```

`target="_blank"` özniteliği hedefi yeni bir bağlamda açar. Güncel tarayıcılar bu öznitelik için
`noopener` davranışını örtük olarak uygular: açılan belge, kendisini açan belgeye program yoluyla
erişemez. `rel="noopener"` yine de yazılır — hem eski gerçekleştirimler için hem de niyeti belgede
görünür kılmak için. Yeni bağlamda açmanın kendisi de gözden geçirilmesi gereken bir karardır;
kullanıcının geri dönüş beklentisini bozar ve bu davranışı bekleyip beklemediği belgeden
anlaşılmaz.

`rel` özniteliği `a` ögesine özgü değildir. Baş bölümündeki `link` ögesi aynı özniteliği
kullanır ve belgeyle ilişkili kaynakları bildirir — biçem dosyası, simge, aynı belgenin
başka dildeki sürümü, sayfalanmış bir dizinin önceki ve sonraki sayfaları. İki ögede de
`rel`, "bu adres benimle şu ilişkidedir" bildirimidir.

#### İstasyon Belgesinde

```html
<h2>Veri Erişimi</h2>
<p>Kayıtlar <a href="veri/">günlük dosyalar hâlinde</a> yayımlanır. Ölçüm yöntemi
   <a href="../yontem.html">ağ genelinde ortaktır</a>. Ham veri dosyaları
   <a href="veri/2024-03.csv" download type="text/csv">Mart ayı kayıtları</a>
   biçiminde indirilebilir.</p>
```

Üç bağlantının üçü de göreli yazılmıştır ve belgenin dizin ağacındaki yerine bağlıdır.
Belge taşınırsa `../yontem.html` bağlantısı kırılır; kök göreli yazım bu kırılmayı önler
ama belgeyi başka bir kökte yayımlamayı zorlaştırır. İki yazım arasındaki seçim, belgenin
nerede yayımlanacağına ilişkin bir karardır.

#### Özet

- Göreli adresler taban adrese göre çözülür; taban, varsayılan olarak belgenin kendi
  adresidir ve belgenin dosya adı çözümlemede atılır.
- Beş göreli biçim vardır: aynı dizin, üst dizin, kök göreli, şema göreli ve aynı belge;
  parça adresleri ağa hiç çıkmaz.
- `base` ögesi belgedeki tüm göreli adreslerin tabanını değiştirir ve belgede en çok bir
  kez bulunur.
- Bağlantı metni bağlamından koparıldığında da hedefi tanımlamalıdır; ekran okuyucular
  bağlantıları liste hâlinde sunar.
- `rel` özniteliği bağlantının hedefiyle ilişkisini bildirir; yeni bağlamda açan
  bağlantılarda `noopener` ilişkisi de bildirilir.

#### Sonraki Adım

Belge bu noktada başka kaynaklara bağlanabiliyor. Geriye bu konunun son yapısı kalıyor: iki
boyutlu veri. Ölçüm kayıtları bir liste değil bir çizelgedir — her satırın birden çok
alanı, her alanın bir başlığı vardır. Sonraki ders tablo ögelerini ele alır ve bir veri
hücresinin hangi başlıklara bağlı olduğunun nasıl hesaplandığını gösterir.

### Tablolar

Source: https://academia.sh/tr/kurslar/web-temelleri-ve-html/tablolar

Tablo ögelerinin yapısı, başlık hücrelerinin kapsamı, ızgaranın colspan ve rowspan ile kurulması ve bir veri hücresinin başlıklarının hesaplanması.

Önceki ders belgeyi başka kaynaklara bağladı. Bu ders konunun son yapısını ele alır: iki
boyutlu veri.

Ölçüm kayıtları bir liste değildir. Her satırın birden çok alanı, her alanın bir başlığı
vardır ve bir hücrenin anlamı, hem satırının hem sütununun başlığından çıkar. Tablo ögesi
bu iki boyutlu ilişkiyi bildirir.

#### Tablo Ne Zaman Kullanılır

Ölçüt tektir: veri iki boyutlu mu? Her hücrenin anlamı bir satır başlığı ve bir sütun
başlığının kesişiminden çıkıyorsa yapı bir tablodur.

Tablo ögelerinin sayfa düzeni kurmak için kullanılması ayrı bir konudur ve yapılmaz:
düzen sunum katmanının işidir ve tablo ögesi kullanıldığında ekran okuyucu içeriği satır
ve sütun ilişkisi olarak duyurur — var olmayan bir veri ilişkisi bildirilmiş olur.

#### Temel Yapı

```html
<table>
  <caption>Kuzey Yamaç istasyonu haftalık ölçüm özeti</caption>
  <thead>
    <tr><th scope="col">Gün</th><th scope="col">En düşük (°C)</th></tr>
  </thead>
  <tbody>
    <tr><th scope="row">Pazartesi</th><td>-4</td></tr>
    <tr><th scope="row">Salı</th><td>-6</td></tr>
  </tbody>
  <tfoot>
    <tr><th scope="row">Ortalama</th><td>-5</td></tr>
  </tfoot>
</table>
```

`caption` tablonun adıdır ve tablonun ilk çocuğu olarak yazılır. Tablonun ne gösterdiğini
bildirir; bir ekran okuyucu tabloya girildiğinde bu metni duyurur, dolayısıyla tabloyu
bağlamı dışında adlandırır. Tablonun üstüne ayrı bir paragraf olarak yazılmış bir başlık
bu işi görmez: tabloyla arasında bildirilmiş bir bağ yoktur.

`thead`, `tbody` ve `tfoot` tabloyu üç bölüme ayırır. Yazılmasalar da `tbody` ayrıştırıcı
tarafından kurulur. Yazılmalarının gerekçesi, uzun tablolarda başlık satırının veriden
ayrılmasıdır: bu ayrım hem sunum katmanına hem de tabloyu bölerek işleyen araçlara bilgi
verir.

`tr` bir satır, `th` bir başlık hücresi, `td` bir veri hücresidir. `th` ile `td` arasındaki
fark görünüm değildir: başlık hücresi, kendisine bağlı veri hücrelerini adlandırır.

#### Başlığın Kapsamı

`scope` özniteliği bir başlık hücresinin hangi yöndeki hücreleri adlandırdığını bildirir:
`col` sütununu, `row` satırını. Basit tablolarda bu bildirim, hücre–başlık eşleşmesini
tahmine bırakmaz.

Sütun başlıkları `thead` içinde, satır başlıkları her satırın ilk hücresinde bulunur.
İkisi birden bulunabilir — yukarıdaki örnekte `Pazartesi` bir satır başlığı, `En düşük`
bir sütun başlığıdır ve `-4` hücresi her ikisine bağlıdır.

#### Izgara ve Yayılma

Bir tablo, kaynak metinde satır satır yazılır ama gerçekte bir **ızgaradır (grid)**. `colspan` ve
`rowspan` öznitelikleri bir hücrenin birden çok ızgara gözünü kaplamasını sağlar ve bu
durumda kaynak metindeki hücre sayısı ile ızgaradaki göz sayısı ayrışır.

Aşağıdaki betik ızgarayı kurar ve her veri hücresinin başlıklarını hesaplar.

```js
// tablolar.mjs — izgarayi colspan/rowspan ile kurar, hucre basliklarini bulur
const satirlar = [
  [{ b: 1, m: "Gün", rowspan: 2 }, { b: 1, m: "Sıcaklık (°C)", colspan: 2 }, { b: 1, m: "Nem (%)", rowspan: 2 }],
  [{ b: 1, m: "En düşük" }, { b: 1, m: "En yüksek" }],
  [{ b: 1, m: "Pazartesi" }, { m: "-4" }, { m: "3" }, { m: "72" }],
  [{ b: 1, m: "Salı" }, { m: "-6" }, { m: "1" }, { m: "68" }],
];

const izgara = [];
const yerlestir = (s, k, hucre) => {
  izgara[s] ??= [];
  izgara[s][k] = hucre;
};

let satirNo = 0;
for (const satir of satirlar) {
  let sutunNo = 0;
  for (const hucre of satir) {
    while (izgara[satirNo]?.[sutunNo]) sutunNo += 1; // dolu yeri atla
    const genislik = hucre.colspan ?? 1;
    const yukseklik = hucre.rowspan ?? 1;
    for (let s = 0; s < yukseklik; s += 1) {
      for (let k = 0; k < genislik; k += 1) yerlestir(satirNo + s, sutunNo + k, hucre);
    }
    sutunNo += genislik;
  }
  satirNo += 1;
}

console.log("--- izgara ---");
for (const satir of izgara) {
  console.log(satir.map((h) => (h.b ? "[" + h.m + "]" : h.m).padEnd(15)).join("").trimEnd());
}

function basliklar(s, k) {
  const satirBasliklari = [];
  for (let i = k - 1; i >= 0; i -= 1) {
    const h = izgara[s][i];
    if (h?.b && !satirBasliklari.includes(h.m)) satirBasliklari.unshift(h.m);
  }
  const sutunBasliklari = [];
  for (let i = s - 1; i >= 0; i -= 1) {
    const h = izgara[i][k];
    if (h?.b && !sutunBasliklari.includes(h.m)) sutunBasliklari.unshift(h.m);
  }
  return [...satirBasliklari, ...sutunBasliklari];
}

console.log("--- veri hucrelerinin basliklari ---");
for (let s = 2; s < izgara.length; s += 1) {
  for (let k = 0; k < izgara[s].length; k += 1) {
    const hucre = izgara[s][k];
    if (hucre.b) continue;
    console.log(hucre.m.padStart(3), "<-", basliklar(s, k).join(" / "));
  }
}
```

```
--- izgara ---
[Gün]          [Sıcaklık (°C)][Sıcaklık (°C)][Nem (%)]
[Gün]          [En düşük]     [En yüksek]    [Nem (%)]
[Pazartesi]    -4             3              72
[Salı]         -6             1              68
--- veri hucrelerinin basliklari ---
 -4 <- Pazartesi / Sıcaklık (°C) / En düşük
  3 <- Pazartesi / Sıcaklık (°C) / En yüksek
 72 <- Pazartesi / Nem (%)
 -6 <- Salı / Sıcaklık (°C) / En düşük
  1 <- Salı / Sıcaklık (°C) / En yüksek
 68 <- Salı / Nem (%)
```

Kaynak yapıda ilk satır üç hücre, ikinci satır iki hücre taşır; ızgaradaysa her satır dört
gözdür. `Gün` ve `Nem (%)` başlıkları iki satıra, `Sıcaklık (°C)` başlığı iki sütuna
yayılmıştır ve ızgarada aynı hücre birden çok gözde görünür.

Çıktının ikinci bölümü, işaretlemenin taşıdığı asıl bilgiyi gösterir. `-4` değeri tek
başına anlamsızdır; bağlı olduğu üç başlıkla birlikte "Pazartesi günü sıcaklığın en düşük
değeri" anlamına gelir. Ekran okuyucular bir hücreye girildiğinde bu başlıkları duyurur;
kullanıcı satırın ve sütunun neresinde olduğunu böyle izler.

İşaretlemenin karşılığı şudur:

```html
<tr>
  <th scope="col" rowspan="2">Gün</th>
  <th scope="colgroup" colspan="2">Sıcaklık (°C)</th>
  <th scope="col" rowspan="2">Nem (%)</th>
</tr>
<tr>
  <th scope="col">En düşük</th>
  <th scope="col">En yüksek</th>
</tr>
```

`colgroup` kapsam değeri, başlığın tek bir sütunu değil bir sütun grubunu adlandırdığını
bildirir; satır yönünde karşılığı `rowgroup` değeridir.

#### Karmaşık Tablolarda Açık Bağ

Yukarıdaki hesap komşuluğa dayanır ve başlıklar düzenli yerleştirildiğinde doğru sonuç verir.
Düzensiz tablolarda —başlıklar tablonun ortasında ya da birden çok düzeyde— bu tahmin yeterli
olmaz. Bu durumda bağ açıkça bildirilir: her başlık hücresine bir `id` verilir ve her veri hücresi
`headers` özniteliğinde bağlı olduğu başlıkların kimliklerini sayar.

```html
<table>
  <tr>
    <th id="gun" rowspan="2">Gün</th>
    <th id="sic" colspan="2">Sıcaklık (°C)</th>
  </tr>
  <tr>
    <th id="sic-dus">En düşük</th>
    <th id="sic-yuk">En yüksek</th>
  </tr>
  <tr>
    <th id="gun-pzt" headers="gun">Pazartesi</th>
    <td headers="gun-pzt sic sic-dus">-4</td>
    <td headers="gun-pzt sic sic-yuk">3</td>
  </tr>
</table>
```

Açık bağ bakımı pahalıdır: her hücre kendi başlıklarını sayar ve kimliklerin belge içinde
tek olması gerekir. Bu maliyet ancak yapı komşulukla çözülemediğinde ödenir. Daha iyi bir
çözüm çoğu durumda tabloyu bölmektir — karmaşık bir tablo, iki basit tabloya ayrıldığında
hem yazımı hem okunması kolaylaşır.

#### Sütun Grupları

`colgroup` ve `col` ögeleri sütunlara toplu olarak öznitelik vermek içindir ve tablonun
başında, `caption` ögesinden sonra yazılır. İçerik taşımazlar; ızgaradaki sütunları
adlandırırlar. Kullanımları sunum katmanıyla ilişkilidir ve veri bağı kurmazlar; hücre
başlıkları yerine geçmezler.

#### İstasyon Belgesinde

```html
<h2>Haftalık Özet</h2>
<table>
  <caption>Kuzey Yamaç istasyonu, son yedi günün günlük uç değerleri</caption>
  <thead>
    <tr>
      <th scope="col" rowspan="2">Gün</th>
      <th scope="colgroup" colspan="2">Sıcaklık (°C)</th>
      <th scope="col" rowspan="2">Nem (%)</th>
    </tr>
    <tr>
      <th scope="col">En düşük</th>
      <th scope="col">En yüksek</th>
    </tr>
  </thead>
  <tbody>
    <tr><th scope="row">Pazartesi</th><td>-4</td><td>3</td><td>72</td></tr>
    <tr><th scope="row">Salı</th><td>-6</td><td>1</td><td>68</td></tr>
  </tbody>
</table>
```

#### Özet

- Tablo, hücrenin anlamının satır ve sütun başlıklarının kesişiminden çıktığı iki boyutlu
  veri içindir; sayfa düzeni için kullanılmaz.
- `caption` tabloyu adlandırır ve tabloya bağlıdır; üstüne yazılmış ayrı bir paragraf bu
  bağı kurmaz.
- `th` ile `td` arasındaki fark görünüm değil işlevdir; `scope` başlığın hangi yöndeki
  hücreleri adlandırdığını bildirir.
- `colspan` ve `rowspan` ile kaynak metindeki hücre sayısı ızgaradaki göz sayısından ayrılır;
  aynı hücre birden çok gözde görünür.
- Komşulukla çözülemeyen yapılarda `headers` ve `id` ile açık bağ kurulur; bakım maliyeti
  yüksek olduğu için önce tabloyu bölmek değerlendirilir.

#### Sonraki Adım

Bu konu belgenin iskeletini kurdu: kök yapı, söz dizimi, başlıklar, metin, listeler,
bağlantılar ve tablolar. Belgede yazılanların her biri bir yapı bildirimidir, ancak bu
bildirimler henüz belgenin **bölümlerini** adlandırmıyor: gezinme nerede bitiyor, ana
içerik nerede başlıyor, alt bilgi hangisi? Sonraki konu bu soruyu yanıtlayan bölüm
etiketlerini ele alır ve belgenin gezinme yapısını kurar.

## Anlamsal İşaretleme ve Medya

Bölüm etiketleriyle belgeye yer imi kazandırmak; görsel, ses, video ve gömülü içeriği erişilebilir ve yalıtılmış biçimde yerleştirmek.

### Anlamsal Bölüm Etiketleri

Source: https://academia.sh/tr/kurslar/web-temelleri-ve-html/anlamsal-bolum-etiketleri

Başlık, gezinme, ana içerik ve alt bilgi ögelerinin yer imi rollerine karşılığı, bölümlere ad verme ve anlamsız kapsayıcının yeri.

Önceki konu belgenin iskeletini kurdu: başlıklar, metin, listeler, bağlantılar, tablolar.
Bu bildirimlerin hiçbiri belgenin **bölümlerini** adlandırmıyor. Gezinme nerede bitiyor,
ana içerik nerede başlıyor, alt bilgi hangisi?

Bu ders o soruyu yanıtlayan ögeleri ele alır. Ögelerin görünüme etkisi yok denecek kadar
azdır; kazandırdıkları şey, belgeye bir üst düzey harita eklemektir.

#### Bölüm Ögesi Ne Bildirir

Bir belgenin okuyucusu ekranı görüyorsa bölümleri düzenden çıkarır: üstteki şerit başlık,
yandaki sütun gezinme, ortadaki alan içeriktir. Bu çıkarım görsel ipuçlarına dayanır ve
belgenin kendisinde bildirilmemiştir.

Anlamsal bölüm ögeleri bu çıkarımı bildirime çevirir. Belgeye "burası gezinme" denildiğinde
o bilgi görsel olmayan araçlara da açılır: ekran okuyucu kullanıcıları bölümden bölüme
atlayabilir, okuma kipleri ana içeriği ayırabilir, dizinleyiciler gezinme bağlantılarını
içerik bağlantılarından ayırabilir.

Bu bilginin taşındığı yapıya **erişilebilirlik ağacı (accessibility tree)** denir.
Belge Nesne Modeli dersindeki ağaçtan türetilir ve her ögeyi bir **rol**, bir **ad** ve
bir **durum** üçlüsüyle karşılar. Bölüm ögeleri bu ağaçta **yer imi (landmark)** rolleri üretir.

#### Yer İmi Rolleri

Karşılıklar tanımlıdır ve bir bölümü koşullar. Aşağıdaki betik kuralları uygular.

```js
// alanlar.mjs — bolum ogelerinin yer imi rollerine karsiligi
const ROL = {
  header: "banner",       // yalnizca belgenin en ust duzeyinde
  nav: "navigation",
  main: "main",
  aside: "complementary",
  footer: "contentinfo",  // yalnizca belgenin en ust duzeyinde
  form: "form",           // yalnizca erisilebilir adi varsa
  section: "region",      // yalnizca erisilebilir adi varsa
  article: "article",
  div: null,
};

const belge = [
  { ad: "header", derinlik: 1 },
  { ad: "nav", derinlik: 2, etiket: "Ana gezinme" },
  { ad: "main", derinlik: 1 },
  { ad: "section", derinlik: 2, etiket: "Günlük ölçümler" },
  { ad: "section", derinlik: 2 },
  { ad: "aside", derinlik: 2, etiket: "İstasyon künyesi" },
  { ad: "div", derinlik: 2 },
  { ad: "footer", derinlik: 1 },
  { ad: "footer", derinlik: 3 },
];

for (const oge of belge) {
  let rol = ROL[oge.ad];
  if ((oge.ad === "header" || oge.ad === "footer") && oge.derinlik > 1) rol = null;
  if ((oge.ad === "section" || oge.ad === "form") && !oge.etiket) rol = null;
  const ad = oge.etiket ? ' ("' + oge.etiket + '")' : "";
  console.log(("  ".repeat(oge.derinlik - 1) + oge.ad + ad).padEnd(34), rol ?? "— yer imi degil");
}

console.log("--- yer imi listesi ---");
const yerImleri = belge
  .map((oge) => {
    let rol = ROL[oge.ad];
    if ((oge.ad === "header" || oge.ad === "footer") && oge.derinlik > 1) rol = null;
    if ((oge.ad === "section" || oge.ad === "form") && !oge.etiket) rol = null;
    return rol ? rol + (oge.etiket ? ": " + oge.etiket : "") : null;
  })
  .filter(Boolean);
console.log(yerImleri.join("\n"));
```

```
header                             banner
  nav ("Ana gezinme")              navigation
main                               main
  section ("Günlük ölçümler")      region
  section                          — yer imi degil
  aside ("İstasyon künyesi")       complementary
  div                              — yer imi degil
footer                             contentinfo
    footer                         — yer imi degil
--- yer imi listesi ---
banner
navigation: Ana gezinme
main
region: Günlük ölçümler
complementary: İstasyon künyesi
contentinfo
```

Çıktının son bölümü, ekran okuyucu kullanıcısının gördüğü listedir. Belgenin haritasıdır ve
kullanıcı buradan doğrudan bir bölüme atlayabilir.

Üç koşul dikkat gerektirir. `header` ve `footer` ögeleri yalnızca **belgenin en üst
düzeyinde** yer imi üretir; bir makalenin içindeki alt bilgi bir yer imi değildir, o
makalenin alt bilgisidir. `section` ögesi ancak bir **adı** olduğunda yer imi üretir;
adsız bir bölüm listede görünmez. `div` hiçbir koşulda yer imi üretmez.

Betikteki `aside` karşılığı koşulsuzdur: öge bir makalenin ya da bölümün içine yuvalansa da yer
imi üretmeyi sürdürür. Bu, `header`/`footer`'ın üst düzey koşulundan ve `section`'ın ad koşulundan
ayrılır. Yine de adsız bir `aside` listede ayırt edilemez; ad vermek bir eşleme koşulu değil,
kullanışlılık gereğidir.

#### Ögeler

`header` bir tanıtım bloğudur: başlık, alt başlık, künye. En üst düzeyde belgenin
tanıtımıdır; bir makalenin içinde o makalenin tanıtımıdır.

`nav` gezinme bağlantılarını taşır. Belgedeki her bağlantı grubunun `nav` olması gerekmez —
öge, belgenin **birincil** gezinme bloklarını işaretler. Bir paragrafın içindeki
bağlantılar gezinme değildir.

`main` belgenin ana içeriğidir ve belgede görünür durumda bir kez bulunur; `hidden` işaretli ek
`main` ögelerine izin verilir. İçinde gezinme, alt bilgi veya site geneline yayılan tekrar eden
içerik bulunmaz; yalnızca o belgeye özgü içerik bulunur.

`aside` ana akışla dolaylı ilişkili içeriktir: yan not, künye kutusu, ilgili bağlantılar.
Ölçüt, çıkarıldığında ana içeriğin anlamını yitirmemesidir.

`footer` bir bölümün kapanış bilgisidir: yazar, telif, ilgili belgeler. En üst düzeyde belgenin
alt bilgisidir; bir makalenin içinde o makalenin alt bilgisidir.

#### Bölüme Ad Vermek

Aynı türden birden çok yer imi varsa —iki gezinme bloğu gibi— hangisinin hangisi olduğu
adla ayrılır. İki yol vardır.

```html
<nav aria-label="Ana gezinme">
  <ul><li><a href="/">Ağ dizini</a></li></ul>
</nav>

<nav aria-labelledby="istasyon-gezinme">
  <h2 id="istasyon-gezinme">İstasyon bölümleri</h2>
  <ul><li><a href="#olcumler">Ölçümler</a></li></ul>
</nav>
```

`aria-label` adı doğrudan yazar; belgede görünmez. `aria-labelledby` adı belgede zaten
bulunan bir ögenin metninden alır ve bu yeğlenir: ad ile görünen başlık ayrışmaz.

Ad yazarken rol adı tekrarlanmaz. `aria-label="Ana gezinme menüsü"` yazımı ekran okuyucuda
"ana gezinme menüsü gezinme" biçiminde duyurulur; rol zaten ögeden gelir.

#### Atlama Bağlantısı

Klavyeyle gezen bir kullanıcı, her sayfada gezinme bağlantılarının tamamını geçerek ana
içeriğe varır. Bunu kısaltan yapı, belgenin ilk odaklanabilir ögesi olarak yazılan bir
bağlantıdır.

```html
<body>
  <a href="#ana-icerik" class="atlama">Ana içeriğe atla</a>
  <header>
    <nav aria-label="Ağ genelinde gezinme">
      <ul><li><a href="/istasyonlar/">İstasyonlar</a></li></ul>
    </nav>
  </header>
  <main id="ana-icerik">
    <h1>Kuzey Yamaç Ölçüm İstasyonu</h1>
  </main>
</body>
```

Bağlantı çoğunlukla sunum katmanında gizlenir ve yalnızca odaklandığında görünür duruma
gelir. Hedefin kimliği, `main` ögesinin kendisinde bulunur.

#### Anlamsız Kapsayıcı Ne Zaman

`div` ve `span` ögeleri hiçbir anlam bildirmez. Varlıklarının gerekçesi, her gruplamanın
anlamlı olmamasıdır: bir kutuyu ortalamak, bir arka plan vermek, bir sunum grubu kurmak
için ögeye ihtiyaç vardır ve bu ögenin belgeye yanlış bir bildirim eklememesi gerekir.

Seçim ölçütü şudur: bu grup, belgenin yapısı hakkında bir şey söylüyor mu? Söylüyorsa
anlamsal öge, söylemiyorsa `div` kullanılır. Yalnızca sunum için `section` yazmak, yer imi
listesine anlamsız bir giriş ekler — ya da adsız olduğu için hiçbir şey eklemez ve öge
gereksiz kalır.

`div` blok düzeyinde, `span` metin akışı içinde gruplar. İkisi de sunum katmanının
tutamağıdır.

#### İstasyon Belgesinde

```html
<body>
  <a href="#ana-icerik">Ana içeriğe atla</a>

  <header>
    <p>Ölçüm Ağı</p>
    <nav aria-label="Ağ genelinde gezinme">
      <ul>
        <li><a href="/istasyonlar/">İstasyonlar</a></li>
        <li><a href="/yontem.html">Ölçüm yöntemi</a></li>
      </ul>
    </nav>
  </header>

  <main id="ana-icerik">
    <h1>Kuzey Yamaç Ölçüm İstasyonu</h1>
    <p>Deniz seviyesinden 1840 metre yükseklikte kurulu otomatik ölçüm istasyonu.</p>

    <section aria-labelledby="olculen">
      <h2 id="olculen">Ölçülen Büyüklükler</h2>
      <ul>
        <li>Sıcaklık</li>
        <li>Bağıl nem</li>
      </ul>
    </section>

    <aside aria-labelledby="kunye">
      <h2 id="kunye">İstasyon Künyesi</h2>
      <dl>
        <dt>Kuruluş yüksekliği</dt><dd>1840 m</dd>
        <dt>Ölçüm aralığı</dt><dd>10 dakika</dd>
      </dl>
    </aside>
  </main>

  <footer>
    <p>Ölçüm Ağı Çalışma Grubu</p>
  </footer>
</body>
```

Belgenin yer imi listesi bu noktada altı girişlidir ve bu liste, önceki konuda kurulan başlık ana
hattından ayrı bir gezinme düzlemidir. İkisi birbirini tamamlar: yer imleri belgenin kaba
bölümlerini, başlıklar bölümlerin içindeki hiyerarşiyi verir.

#### Özet

- Anlamsal bölüm ögeleri, görsel düzenden çıkarılan bölüm bilgisini belgede bildirime
  çevirir ve erişilebilirlik ağacında yer imi rolleri üretir.
- `header` ve `footer` yalnızca belgenin en üst düzeyinde yer imi üretir; `section` ancak
  adı varsa üretir; `div` hiçbir koşulda üretmez.
- `main` belgede bir kez bulunur ve yalnızca o belgeye özgü içeriği taşır.
- Aynı türden birden çok yer imi adla ayrılır; adın belgede görünen bir başlıktan alınması
  yeğlenir ve ada rol adı yazılmaz.
- Anlam bildirmeyen gruplamalar için `div` ve `span` kullanılır; sunum amacıyla anlamsal
  öge yazmak belgeye yanlış bildirim ekler.

#### Sonraki Adım

Bu ders `section` ögesini adlandırılmış bir bölüm olarak kullandı ve `article` ögesini yer
imi tablosunda gösterdi ama ayrımı açmadı. İkisi arasındaki seçim en sık yanlış yapılan
anlamsal karardır. Sonraki ders bu ayrımı tanımlar ve bir bölümün kendi başına anlamlı olup
olmadığını sınayan ölçütü verir.

### Makale ve Bölüm Ayrımı

Source: https://academia.sh/tr/kurslar/web-temelleri-ve-html/makale-ve-bolum-ayrimi

Bağımsız dağıtılabilirlik ölçütüyle makale ve bölüm seçimi, başlıksız bölümün sorunu ve iç içe makalelerin yapısı.

Önceki ders `section` ögesini adlandırılmış bir bölüm olarak kullandı, `article` ögesini
yer imi tablosunda gösterdi ama ayrımı açmadı. İkisi arasındaki seçim, anlamsal
işaretlemede en sık yanlış yapılan karardır — çünkü görünür bir sonucu yoktur ve yanlış
seçim hiçbir hata üretmez.

Ayrımın tek bir ölçütü vardır ve bu ders o ölçütü tanımlar.

#### Bağımsızlık Ölçütü

`article` ögesi, **kendi başına dağıtılabilir** bir içerik birimini işaretler. Ölçüt
şudur: bu içerik bulunduğu belgeden koparılıp başka bir yerde —bir akışta, bir listede,
bir okuma uygulamasında— tek başına yayımlansa anlamını korur mu?

Korursa `article`, korumazsa `section`.

Ölçüt, içeriğin türüyle ilgili değildir. Bir gazete yazısı `article` olabilir; bir ürün
kartı, bir yorum, bir günlük kaydı, bir arama sonucu da olabilir. Tersine, uzun bir
belgenin "Yöntem" bölümü `article` değildir: koparıldığında neyin yöntemi olduğu kaybolur.

`section` ögesi ise **tematik bir gruplamadır**. Belgenin bir bölümünü ayırır; anlamı,
belgenin bütünüyle ilişkisinden gelir. İçindekiler tablosuna bir giriş oluşturacak her
bölüm bu ögeye adaydır.

#### Bölümün Başlığı Olmalıdır

`section` ögesinin bir başlığı yoksa, ne ayırdığı bildirilmemiştir. Bu, önceki dersteki
kuralın nedenidir: adı olmayan bir `section` yer imi listesinde görünmez ve öge işlevsiz
kalır.

Aşağıdaki betik belgedeki bölümleme ögelerini tarar ve başlık taşıyıp taşımadıklarını
bildirir.

```js
// bolum.mjs — bolumleme ogelerinin baslik denetimi
const belge = `
<main>
  <section id="ozet">
    <h2>İstasyon Özeti</h2>
    <p>Kuzey Yamaç istasyonu 1840 metrede kuruludur.</p>
  </section>
  <section id="uyari">
    <p>Bakım nedeniyle veri akışı durabilir.</p>
  </section>
  <article id="kayit-2">
    <h2>Kalibrasyon Kaydı</h2>
    <p>Nem sensörü yeniden ayarlandı.</p>
  </article>
</main>`;

const BOLUMLEME = new Set(["section", "article", "nav", "aside"]);
const desen = /<(\/?)([a-z0-9]+)([^>]*)>/g;
const yigit = [];
let esleme;
while ((esleme = desen.exec(belge)) !== null) {
  const [, kapanis, ad, oznitelik] = esleme;
  if (kapanis) {
    if (BOLUMLEME.has(ad)) {
      const bolum = yigit.pop();
      console.log(
        (bolum.ad + (bolum.kimlik ? "#" + bolum.kimlik : "")).padEnd(18),
        bolum.baslik ? "baslik var" : "BASLIK YOK",
      );
    }
    continue;
  }
  if (BOLUMLEME.has(ad)) {
    const kimlik = oznitelik.match(/id="([^"]*)"/)?.[1] ?? "";
    yigit.push({ ad, kimlik, baslik: false });
  } else if (/^h[1-6]$/.test(ad) && yigit.length > 0) {
    yigit[yigit.length - 1].baslik = true;
  }
}
```

```
section#ozet       baslik var
section#uyari      BASLIK YOK
article#kayit-2    baslik var
```

İkinci bölüm bir kusur bildirir. İçeriği bir uyarıdır ve tematik bir bölüm değildir; doğru
öge bir uyarı bloğu için `div` ya da —uyarının yapısal bir bölüm olması isteniyorsa— bir
başlık eklenmiş `section` olurdu.

Bu denetim bir üslup tercihi değildir. Başlıksız bir bölüm, önceki dersteki yer imi
listesinde görünmez; belgeyi bölümlere ayırdığı sanılırken hiçbir şey yapmamış olur.

#### İç İçe Makaleler

Bir makale başka bir makaleyi içerebilir ve bu geçerlidir: içteki makale, dıştakiyle
ilişkilidir ama kendi başına da anlamlıdır. Bir yazının altındaki yorumlar bu yapının
tipik örneğidir.

```html
<article>
  <h2>Nem Sensörü Kalibrasyonu</h2>
  <p>Sensör, referans değere göre yüzde iki sapma gösterdi.</p>

  <section>
    <h3>Gözlemci Notları</h3>
    <article>
      <h4>Sabah ölçümü</h4>
      <p>Gövde içi yoğuşma gözlendi.</p>
    </article>
    <article>
      <h4>Akşam ölçümü</h4>
      <p>Sapma yinelenmedi.</p>
    </article>
  </section>
</article>
```

Yapı iki düzey bildirir. Dıştaki makale bir kalibrasyon kaydıdır; içtekiler o kayda
düşülmüş notlardır ve her biri tek başına okunabilir. İkisini birbirine bağlayan `section`
ögesi bağımsız değildir — koparıldığında "hangi kaydın notları" bilgisi kaybolur.

Başlık düzeylerine dikkat edilmelidir: iç içelik başlık düzeyini kendiliğinden değiştirmez.
Başlık Hiyerarşisi dersinde belirtildiği gibi, bu kurs düzeyleri belgenin bütünü üzerinden
sayar; `article` ögesinin içinde `h1` ile yeniden başlanmaz.

#### Makalenin Üstverisi

Bağımsız dağıtılabilirlik, makalenin kendi künyesini taşımasını gerektirir: koparıldığında
ne zaman ve kim tarafından yazıldığı da onunla birlikte gitmelidir. İki öge bu bilgiyi
makinece okunabilir kılar.

`time` ögesi bir tarihi veya süreyi işaretler. Görünen metin serbest yazılabilir; `datetime`
özniteliği aynı değeri tanımlı bir biçimde taşır.

```html
<article>
  <h3>Nem sensörü yeniden ayarlandı</h3>
  <p>Referans değere göre yüzde iki sapma düzeltildi.</p>
  <footer>
    <p>Kayıt tarihi: <time datetime="2024-03-12T07:30">12 Mart sabahı</time></p>
    <address>Gözlemci: A. Yılmaz</address>
  </footer>
</article>
```

`datetime` değerinin biçimi tanımlıdır: tarih `YYYY-AA-GG`, saat `SS:DD` düzeninde yazılır
ve ikisi `T` ile birleşir. Bu biçim, "12 Mart sabahı" gibi serbest bir yazımdan sıralama
ve karşılaştırma yapılamayacağı için gereklidir.

`address` ögesi bir adres değil, **iletişim bilgisidir**: bulunduğu makalenin (ya da en üst
düzeyde belgenin) yazarına ulaşma yolunu işaretler. Bir posta adresini işaretlemek için
kullanılmaz.

#### Karar Ölçütleri

Sıralı bir denetim, çoğu durumu çözer.

| Soru | Yanıt evetse |
|---|---|
| İçerik koparıldığında tek başına anlamlı mı? | `article` |
| Belgenin bir konusunu ayıran, başlığı olan bir bölüm mü? | `section` |
| Ana içerikten koparılabilir, dolaylı ilişkili bir ek mi? | `aside` |
| Yalnızca sunum için gruplama mı? | `div` |

İlk üç soruya "hayır" veriliyorsa öge gereksizdir. Anlamsal öge eklemek her zaman bir
iyileştirme değildir: yanlış bildirim, hiç bildirim yapmamaktan daha zararlıdır çünkü
belgeyi okuyan araç ona güvenir.

Bir başka sık hata, her `div` ögesini `section` ile değiştirmektir. Bu, yer imi listesini
anlamsız girişlerle doldurur — ya da adsız oldukları için hiçbir şey eklemez ve yalnızca
kaynak metni büyütür.

#### İstasyon Belgesinde

Ölçüm istasyonu sayfasında iki yapı bir arada bulunur.

```html
<main id="ana-icerik">
  <h1>Kuzey Yamaç Ölçüm İstasyonu</h1>

  <section aria-labelledby="olculen">
    <h2 id="olculen">Ölçülen Büyüklükler</h2>
    <ul>
      <li>Sıcaklık</li>
      <li>Bağıl nem</li>
    </ul>
  </section>

  <section aria-labelledby="kayitlar">
    <h2 id="kayitlar">Kalibrasyon Kayıtları</h2>

    <article>
      <h3>Nem sensörü yeniden ayarlandı</h3>
      <p>Referans değere göre yüzde iki sapma düzeltildi.</p>
      <footer><p>Gözlemci: A. Yılmaz</p></footer>
    </article>

    <article>
      <h3>Rüzgâr ölçer yatağı değiştirildi</h3>
      <p>Düşük hızlarda takılma gözlenmişti.</p>
      <footer><p>Gözlemci: A. Yılmaz</p></footer>
    </article>
  </section>
</main>
```

"Ölçülen Büyüklükler" bir bölümdür: koparıldığında hangi istasyonun büyüklükleri olduğu
kaybolur. Her kalibrasyon kaydı ise bir makaledir: tarih, işlem ve gözlemci bilgisiyle
birlikte başka bir belgede —örneğin ağ genelindeki bir kayıt akışında— tek başına
yayımlanabilir.

İç `footer` ögeleri, önceki derste belirtildiği gibi belgenin alt bilgisi değildir;
bulundukları makalenin alt bilgisidir ve yer imi listesine girmezler.

#### Özet

- `article`, bulunduğu belgeden koparıldığında anlamını koruyan içerik birimidir; ölçüt
  içeriğin türü değil, bağımsız dağıtılabilirliğidir.
- `section`, belgenin bir konusunu ayıran tematik gruplamadır ve anlamını belgeyle
  ilişkisinden alır.
- Başlığı olmayan bir `section` yer imi listesinde görünmez ve neyi ayırdığını bildirmez.
- Makaleler iç içe olabilir; içteki makale dıştakiyle ilişkilidir ama kendi başına da
  anlamlıdır.
- Yanlış anlamsal öge, öge yazmamaktan daha zararlıdır; ilk üç ölçüte uymayan gruplamalar
  `div` ile yazılır.

#### Sonraki Adım

Belge bölümlendi ama hâlâ yalnızca metinden oluşuyor. Ölçüm istasyonu sayfasının konumu
bir görselle anlatılmalı ve bu görsel, göremeyen okuyucu için de bir karşılık taşımalıdır.
Sonraki ders görsel ögesini ele alır: alternatif metnin ne söylemesi gerektiğini, boyut
bildiriminin yerleşimi nasıl etkilediğini ve aynı görselin farklı ekranlar için farklı
boyutlarda nasıl sunulduğunu.

### Görseller

Source: https://academia.sh/tr/kurslar/web-temelleri-ve-html/gorseller

Alternatif metnin ne söylemesi gerektiği, boyut bildiriminin yerleşime etkisi ve aynı görselin farklı ekranlar için aday kümesiyle sunulması.

Belge bölümlendi ama hâlâ yalnızca metinden oluşuyor. Ölçüm istasyonu sayfasının konumu bir
görselle anlatılmalıdır ve bu görsel, göremeyen okuyucu için de bir karşılık taşımalıdır.

`img` boş bir ögedir; içeriği yoktur, kaynağı `src` özniteliğiyle bildirilir. İki
özniteliği zorunludur: `src` ve `alt`.

#### Alternatif Metin

`alt` özniteliği, görselin **yerine geçecek metni** taşır. Görselin betimlemesi değildir;
görsel gösterilemediğinde onun işini görecek metindir. Ölçüt şudur: bu görseli metne
çevirmem gerekseydi ne yazardım?

Yanıt görselin belgedeki işlevine bağlıdır ve üç durum vardır.

**Bilgi taşıyan görsel.** Alternatif metin o bilgiyi verir.

```html
<img src="konum.png" alt="İstasyon, kuzeye bakan yamacın orta kesiminde,
     vadi tabanından 400 metre yukarıda.">
```

Burada yazılan şey görselin nasıl göründüğü —renkler, çizim biçimi— değil, ne söylediğidir.

**İşlevsel görsel.** Bir bağlantının veya düğmenin içindeki görselde alternatif metin,
görselin değil **eylemin** karşılığıdır.

```html
<a href="/veri/2024-03.csv"><img src="indir.svg" alt="Mart ayı kayıtlarını indir"></a>
```

**Süsleyici görsel.** Belgeye bilgi katmayan görselde alternatif metin **boş** bırakılır.

```html
<img src="ayrac.svg" alt="">
```

Boş `alt` ile `alt` yazmamak aynı şey değildir. Boş değer "bu görselin metin karşılığı
yoktur, atlayın" bildirimidir ve ekran okuyucu görseli duyurmaz. Öznitelik hiç yazılmazsa
bildirim yapılmamış olur; bu durumda birçok ekran okuyucu dosya adını okur, ki bu
kullanıcıya hiçbir şey vermez.

Alternatif metinde "resim", "görsel", "fotoğraf" sözcükleri yazılmaz; ögenin türü zaten
rolünden duyurulur. Metnin uzunluğu içeriğe bağlıdır; birkaç cümleyi aşan betimlemeler
için ayrı bir uzun açıklama bağlantısı yeğlenir.

#### Boyut Bildirimi

`width` ve `height` öznitelikleri görselin **iç boyutlarını** piksel olarak bildirir. Bu
bildirim, sunum katmanı görseli başka bir boyutta gösterse bile anlamlıdır: tarayıcı
oranı buradan hesaplar ve görsel inmeden önce ona yer ayırır.

```js
// oran.mjs — en boy orani ve ayrilan yukseklik
const g = 1440, y = 960;
console.log("width/height =", g + "/" + y, "-> oran", (g / y).toFixed(4));
for (const genislik of [300, 480, 720]) {
  console.log(String(genislik).padStart(4), "piksel genislikte yukseklik:", (genislik * y / g).toFixed(1));
}
```

```
width/height = 1440/960 -> oran 1.5000
 300 piksel genislikte yukseklik: 200.0
 480 piksel genislikte yukseklik: 320.0
 720 piksel genislikte yukseklik: 480.0
```

Bildirim yazılmazsa tarayıcı, görsel inene kadar ona sıfır yükseklik ayırır; görsel indiği
anda altındaki içerik aşağı kayar. Kullanıcı o sırada okuyor ya da bir düğmeye basmak
üzereyse bu kayma somut bir sorundur. Boyut bildirimi bu kaymayı önler ve yerleşim
hesabının görselin inmesini beklemeden yapılmasını sağlar.

Öznitelikler birimsiz yazılır; `width="960px"` yazımı geçersizdir.

`loading="lazy"` özniteliği, görselin görüntü alanına yaklaşana kadar istenmemesini
bildirir. İlk ekranda görünen görsellere yazılmaz — o görseller zaten hemen gerekir ve
gecikmeleri açılışı yavaşlatır.

#### Aday Kümesi

Tek bir görsel dosyası her ekran için uygun değildir. Dar bir ekrana geniş bir dosya
göndermek bant genişliği harcar; geniş ve yoğun bir ekrana dar bir dosya göndermek
bulanık sonuç verir.

`srcset` özniteliği aynı görselin farklı genişlikteki adaylarını, `sizes` özniteliği ise
görselin **düzendeki** genişliğini bildirir. Seçimi tarayıcı yapar.

```html
<img src="yamac-960.jpg"
     srcset="yamac-480.jpg 480w, yamac-960.jpg 960w,
             yamac-1440.jpg 1440w, yamac-1920.jpg 1920w"
     sizes="(max-width: 600px) 100vw, (max-width: 1000px) 50vw, 640px"
     width="1440" height="960"
     alt="İstasyon, kuzeye bakan yamacın orta kesiminde.">
```

Seçim kuralı hesaplanabilir: `sizes` listesinden ilk uyan koşul kaynak boyutunu verir, her
adayın yoğunluğu `w` değerinin kaynak boyutuna oranıdır, aygıtın piksel oranını karşılayan
en küçük yoğunluklu aday seçilir.

```js
// adaylar.mjs — srcset/sizes aday secim kuralinin modeli
const adaylar = [
  { adres: "yamac-480.jpg", w: 480 },
  { adres: "yamac-960.jpg", w: 960 },
  { adres: "yamac-1440.jpg", w: 1440 },
  { adres: "yamac-1920.jpg", w: 1920 },
];

// "(max-width: 600px) 100vw, (max-width: 1000px) 50vw, 640px"
const boyutlar = [
  { enCok: 600, deger: (g) => g * 1.0 },
  { enCok: 1000, deger: (g) => g * 0.5 },
  { enCok: Infinity, deger: () => 640 },
];

function kaynakBoyutu(gorunumGenisligi) {
  return boyutlar.find((k) => gorunumGenisligi <= k.enCok).deger(gorunumGenisligi);
}

function sec(gorunumGenisligi, pikselOrani) {
  const boyut = kaynakBoyutu(gorunumGenisligi);
  const yogunluklar = adaylar.map((a) => ({ ...a, yogunluk: a.w / boyut }));
  const yeterli = yogunluklar.filter((a) => a.yogunluk >= pikselOrani);
  const secilen = yeterli.length > 0
    ? yeterli.reduce((en, a) => (a.yogunluk < en.yogunluk ? a : en))
    : yogunluklar.reduce((en, a) => (a.yogunluk > en.yogunluk ? a : en));
  return { boyut, secilen };
}

const durumlar = [[390, 1], [390, 3], [800, 1], [800, 2], [1440, 1], [1440, 2]];
console.log("gorunum  oran  kaynak boyutu  secilen aday   yogunluk");
for (const [genislik, oran] of durumlar) {
  const { boyut, secilen } = sec(genislik, oran);
  console.log(
    String(genislik).padStart(7),
    String(oran).padStart(5),
    String(boyut).padStart(14),
    "  " + secilen.adres.padEnd(15),
    secilen.yogunluk.toFixed(2),
  );
}
```

```
gorunum  oran  kaynak boyutu  secilen aday   yogunluk
    390     1            390   yamac-480.jpg   1.23
    390     3            390   yamac-1440.jpg  3.69
    800     1            400   yamac-480.jpg   1.20
    800     2            400   yamac-960.jpg   2.40
   1440     1            640   yamac-960.jpg   1.50
   1440     2            640   yamac-1440.jpg  2.25
```

Çıktının ilk iki satırı aynı ekran genişliğini iki farklı piksel yoğunluğuyla
karşılaştırır: aynı düzende, aynı boyutta gösterilen görsel için üç kat yoğun bir ekranda
üç kat büyük bir dosya seçilir. Üçüncü ve beşinci satırlar ise ekran genişledikçe kaynak
boyutunun `sizes` kuralına göre değiştiğini gösterir — 800 pikselde görselin yarısı, 1440
pikselde sabit 640 piksel.

`sizes` değerinin yanlış yazılması sessiz bir kusur üretir: tarayıcı bildirilen boyuta
göre seçim yapar, gerçek boyuta göre değil. Bildirim düzenle uyumlu tutulmalıdır.

Seçim tarayıcının kararıdır ve ağ koşullarını, önbellekteki dosyaları veya kullanıcı
ayarlarını da hesaba katabilir. Yukarıdaki hesap, kuralın kendisidir; sonucun garantisi
değildir.

#### Biçim Seçimi

`picture` ögesi, aday kümesini farklı **dosya biçimleri** arasında seçmek için de kullanılır.
İçindeki `source` ögeleri sırayla denenir, ilk desteklenen biçim seçilir; `img` ögesi hem
son çare hem de alternatif metnin taşıyıcısıdır.

```html
<picture>
  <source srcset="yamac.avif" type="image/avif">
  <source srcset="yamac.webp" type="image/webp">
  <img src="yamac.jpg" width="1440" height="960"
       alt="İstasyon, kuzeye bakan yamacın orta kesiminde.">
</picture>
```

Aynı düzenek, dar ekranda farklı bir kırpma göstermek için de kullanılır: `source`
ögelerine `media` koşulu yazıldığında seçim ekran genişliğine göre yapılır. `srcset` ile
`picture` arasındaki ayrım şudur — `srcset` aynı görselin farklı ölçeklerini, `picture`
farklı görselleri ya da farklı biçimleri seçer.

#### Özet

- `alt` görselin betimlemesi değil, yerine geçecek metindir; işlevsel görsellerde eylemin,
  süsleyici görsellerde boş dizginin karşılığıdır.
- Boş `alt` ile `alt` yazmamak farklıdır: birincisi "atla" bildirimidir, ikincisi hiçbir
  bildirim yapmaz.
- `width` ve `height` bildirimi en boy oranını verir ve görsel inmeden yer ayrılmasını
  sağlayarak içeriğin kaymasını önler.
- `srcset` adayları, `sizes` düzendeki genişliği bildirir; seçim, kaynak boyutu ve aygıtın
  piksel oranından hesaplanır.
- `picture` farklı biçimler veya farklı kırpmalar arasında seçim yapar; `srcset` aynı
  görselin ölçekleri arasında.

#### Sonraki Adım

Görsel belgeye girdi ama bağlamı hâlâ eksik: hangi ölçüm noktasını gösterdiği, ne zaman
çekildiği, hangi metne ait olduğu bildirilmemiş durumda. Sonraki ders görseli bir açıklama
metniyle ilişkilendiren ögeyi ele alır ve bu ilişkinin erişilebilir adı nasıl değiştirdiğini
gösterir.

### Şekil ve Açıklama

Source: https://academia.sh/tr/kurslar/web-temelleri-ve-html/sekil-ve-aciklama

Şekil ögesinin akıştan kopabilir içerik birimi olarak işlevi, açıklama ile alternatif metnin farkı ve erişilebilir ad hesabının sırası.

Görsel belgeye girdi ama bağlamı eksik: hangi ölçüm noktasını gösterdiği, ne zaman
çekildiği, hangi metne ait olduğu bildirilmemiş durumda. Alternatif metin bu bilgiyi
taşımaz — o, görseli göremeyen okuyucu içindir; bağlam bilgisi ise her okuyucu içindir.

`figure` ögesi bu ayrımı kurar: içeriği ve açıklamasını tek bir birim olarak bağlar.

#### Şekil Ögesi

`figure`, ana akıştan **kopabilen** ama akışta anılan bir içerik birimidir. Ölçüt şudur:
bu içerik metnin ortasından alınıp sayfanın kenarına, bir eke ya da bir sonraki sayfaya
taşınsa, metin okunmayı sürdürür mü?

Sürdürüyorsa şekildir. Metnin akışını kesen ve yerinden ayrılamayan içerik şekil değildir.

`figcaption` şeklin açıklamasıdır ve `figure` ögesinin ilk veya son çocuğu olarak yazılır.
Bir şekilde en çok bir açıklama bulunur.

```html
<figure>
  <img src="konum.png" width="1440" height="960"
       alt="İstasyon, kuzeye bakan yamacın orta kesiminde,
            vadi tabanından 400 metre yukarıda.">
  <figcaption>Şekil 1. İstasyonun yamaç üzerindeki konumu ve vadi tabanına
    göre yüksekliği.</figcaption>
</figure>
```

#### Açıklama ile Alternatif Metin Ayrımı

İki metin aynı görsele bağlıdır ve farklı işler görür.

`alt`, görselin **yerine geçer**. Görseli göremeyen okuyucuya, gören okuyucunun görselden
aldığı bilgiyi verir. Gören okuyucuya sunulmaz.

`figcaption`, görseli **çevreleyen bilgidir**: adı, numarası, kaynağı, tarihi, yorumu. Her
okuyucuya sunulur.

İkisinin aynı metni taşıması bir kusurdur: ekran okuyucu kullanıcısı aynı cümleyi iki kez
duyar. Aynı bilgiyi iki kez yazmak yerine, açıklamanın içerdiği bilgi alternatif metinden
çıkarılır.

Açıklama görselin bilgisini eksiksiz veriyorsa —bir çizelgenin değerlerini yazan bir
açıklama gibi— alternatif metin boş bırakılabilir. Bu, `alt=""` yazımının süsleyici olmayan
bir kullanımıdır: görselin bilgisi belgede zaten metin olarak vardır.

#### Erişilebilir Ad Hesabı

Önceki derste ögelerin erişilebilirlik ağacında bir **rol**, bir **ad** ve bir **durum**
ile karşılandığı belirtilmişti. Adın hangi kaynaktan alınacağı sıralı bir kuralla
belirlenir ve `figcaption` bu sıranın bir halkasıdır.

```js
// ad.mjs — erisilebilir ad hesabinin sirasi
const YEREL_KAYNAK = {
  img: "alt",
  figure: "figcaption",
  table: "caption",
  fieldset: "legend",
};

function erisilebilirAd(oge, belge) {
  if (oge["aria-labelledby"]) {
    const parcalar = oge["aria-labelledby"]
      .split(/\s+/)
      .map((kimlik) => belge[kimlik] ?? "")
      .filter(Boolean);
    if (parcalar.length > 0) return ["aria-labelledby", parcalar.join(" ")];
  }
  if (oge["aria-label"]) return ["aria-label", oge["aria-label"]];
  const alan = YEREL_KAYNAK[oge.ad];
  if (alan && oge[alan] !== undefined && oge[alan] !== "") return [alan, oge[alan]];
  if (oge.title) return ["title", oge.title];
  return ["yok", ""];
}

const belge = { "olcum-basligi": "Kuzey Yamaç", "olcum-tarihi": "12 Mart" };

const ogeler = [
  { ad: "img", alt: "Yamaçtaki ölçüm direği" },
  { ad: "img", alt: "" },
  { ad: "img", title: "Direk" },
  { ad: "figure", figcaption: "Şekil 1: Direğin konumu" },
  { ad: "figure", figcaption: "Şekil 1: Direğin konumu", "aria-label": "Direk konumu" },
  { ad: "figure", "aria-labelledby": "olcum-basligi olcum-tarihi", figcaption: "Şekil 1" },
  { ad: "table", caption: "Günlük ölçümler" },
];

for (const oge of ogeler) {
  const [kaynak, ad] = erisilebilirAd(oge, belge);
  console.log(oge.ad.padEnd(8), kaynak.padEnd(16), JSON.stringify(ad));
}
```

```
img      alt              "Yamaçtaki ölçüm direği"
img      yok              ""
img      title            "Direk"
figure   figcaption       "Şekil 1: Direğin konumu"
figure   aria-label       "Direk konumu"
figure   aria-labelledby  "Kuzey Yamaç 12 Mart"
table    caption          "Günlük ölçümler"
```

Sıra dört basamaklıdır ve üstteki kaynak alttakini bastırır: `aria-labelledby`, sonra
`aria-label`, sonra ögenin **yerel** kaynağı, en sonda `title`.

Üç satır dikkat çekicidir. İkinci satır boş `alt` değerinin adı silmesini gösterir; bu, görselin
duyurulmamasının düzeneğidir. Üçüncü satır `title` özniteliğinin son çare olduğunu gösterir: `alt`
yazılmadığında `title` ada dönüşür, ancak bu bir çözüm değil bir yedektir. Beşinci ve altıncı
satırlar, `aria-label` ve `aria-labelledby` özniteliklerinin belgede görünen açıklamayı
**bastırdığını** gösterir — ad ile görünen metin ayrışır ve bu, ayrılmayı gerektiren bir gerekçe
yoksa kaçınılması gereken bir durumdur.

Tablo satırı, önceki konudaki `caption` ögesinin aynı düzeneğin parçası olduğunu gösterir: farklı
ögelerin yerel kaynakları farklıdır, ancak hesabın sırası ortaktır.

#### Şekil Yalnızca Görsel İçin Değildir

`figure` ögesi içeriğin türünü kısıtlamaz. Bir kod listesi, bir tablo, bir alıntı, bir ses
kaydı da şekil olabilir; ölçüt yine akıştan kopabilirliktir.

```html
<figure>
  <figcaption>Şekil 2. Ham veri dosyasının ilk satırları.</figcaption>
  <pre><code>saat,sicaklik,nem
07:00,-4.2,72
07:10,-4.1,71</code></pre>
</figure>
```

Bir tablonun `caption` ögesi zaten vardır; tabloyu `figure` içine almak, ona akıştan
kopabilirlik bildirimi eklemek istendiğinde anlamlıdır. İkisi birlikte kullanılıyorsa
açıklamalardan birinin yinelenmemesine dikkat edilir.

#### Uzun Açıklama

Karmaşık bir çizelgenin ya da haritanın bilgisi bir alternatif metne sığmaz. Böyle
durumlarda `alt` özniteliği görselin ne olduğunu kısaca söyler, ayrıntılı karşılık ise
belgede metin olarak bulunur ve şekle bağlanır.

```html
<figure>
  <img src="haftalik.png" width="1200" height="700"
       alt="Haftalık sıcaklık çizelgesi; değerler aşağıdaki tabloda.">
  <figcaption>Şekil 3. Haftalık sıcaklık seyri.</figcaption>
  <details>
    <summary>Çizelgenin sayısal karşılığı</summary>
    <table>
      <caption>Günlük en düşük ve en yüksek sıcaklıklar</caption>
      <tr><th scope="col">Gün</th><th scope="col">En düşük</th></tr>
      <tr><th scope="row">Pazartesi</th><td>-4</td></tr>
    </table>
  </details>
</figure>
```

`details` ögesi kapalı başlayan bir açılır bölüm kurar; `summary` ögesi onun görünen
başlığıdır. Açma ve kapama davranışı ögenin kendisinde tanımlıdır ve betik gerektirmez.
Buradaki işlevi, ayrıntılı karşılığı belgede tutup akışı meşgul etmemektir.

Bu yaklaşımın alternatif metin uzatmaktan üstünlüğü, karşılığın **herkese** açık
olmasıdır: sayısal değerleri okumak isteyen gören bir okuyucu da aynı tabloya erişir.

#### Şekil Numaraları

Açıklamada yazılan "Şekil 1", "Şekil 2" numaraları belgenin metnidir; ögeden gelmez. Bu
numaraların değeri, metnin şekle uzaktan başvurabilmesindedir: "Şekil 2'deki dosya
biçiminde…" cümlesi, şekil sayfanın başka bir yerine taşınsa da anlamını korur.

Numaralar elle yazıldığında araya bir şekil eklendiğinde tümü kayar. Sunum katmanı bu
numaraları kendiliğinden üretebilir; bu durumda numara belgede bulunmaz ve metinden
başvurulamaz. Seçim, şekle metinden başvurulup başvurulmayacağına göre yapılır.

#### İstasyon Belgesinde

```html
<section aria-labelledby="konum">
  <h2 id="konum">Konum ve Kuruluş</h2>
  <p>İstasyon, kuzeye bakan yamacın orta kesiminde yer alır. Yamacın bu yüzü gün
     boyunca doğrudan ışık almadığı için sıcaklık ölçümünde sapma düşüktür.</p>

  <figure>
    <img src="konum-960.png"
         srcset="konum-480.png 480w, konum-960.png 960w, konum-1440.png 1440w"
         sizes="(max-width: 600px) 100vw, 640px"
         width="1440" height="960"
         alt="Yamaç kesiti: istasyon, vadi tabanından 400 metre yukarıda ve
              zirveden 260 metre aşağıda işaretli.">
    <figcaption>Şekil 1. İstasyonun yamaç üzerindeki konumu.</figcaption>
  </figure>
</section>
```

Alternatif metin kesitin taşıdığı sayısal bilgiyi verir; açıklama şekli adlandırır ve
numaralandırır. İkisi arasında yineleme yoktur.

#### Özet

- `figure`, ana akıştan kopabilen ama akışta anılan içerik birimidir; ölçüt, taşındığında
  metnin okunmayı sürdürmesidir.
- `alt` görselin yerine geçer ve yalnızca göremeyen okuyucuya sunulur; `figcaption` görseli
  çevreleyen bilgidir ve herkese sunulur.
- İkisinin aynı metni taşıması yinelemedir; açıklamanın verdiği bilgi alternatif metinden
  çıkarılır.
- Erişilebilir ad hesabı sıralıdır: `aria-labelledby`, `aria-label`, ögenin yerel kaynağı,
  `title`. Üstteki alttakini bastırır.
- Şekil içeriğin türünü kısıtlamaz; kod listeleri, tablolar ve alıntılar da şekil olabilir.

#### Sonraki Adım

Görsel içerik yerleştirildi. Zamanda akan içerik —ses ve video— ayrı sorunlar getirir:
denetimlerin kimin elinde olduğu, konuşulanın metin karşılığının nasıl taşınacağı ve
kullanıcının istemediği bir sesin kendiliğinden çalmasının neden kısıtlandığı. Sonraki
ders bu ögeleri ve kaynak seçim kurallarını ele alır.

### Ses ve Video

Source: https://academia.sh/tr/kurslar/web-temelleri-ve-html/ses-ve-video

Zamanda akan içeriğin denetimleri, kaynak seçim kuralı, altyazı ve betimleme parçaları ile otomatik oynatma kısıtının gerekçesi.

Görsel içerik yerleştirildi. Zamanda akan içerik ayrı sorunlar getirir. Bir görsel
gösterildiğinde okuyucu ona istediği kadar bakar; bir video oynatıldığında ise akış
kullanıcının denetiminde olmayabilir, sesi başka işleri bölebilir ve konuşulanın metin
karşılığı yoksa içerik duyamayanlara kapalı kalır.

`audio` ve `video` ögeleri bu içeriği taşır. İkisi de aynı öznitelik kümesini paylaşır;
`video` ayrıca görüntü boyutlarını ve kapak görselini bildirir.

#### Temel Yazım

```html
<video controls preload="metadata" width="1280" height="720" poster="kapak.jpg">
  <source src="anlatim.webm" type="video/webm">
  <source src="anlatim.mp4" type="video/mp4">
  <track kind="captions" src="anlatim-tr.vtt" srclang="tr"
         label="Türkçe altyazı" default>
  <p>Video oynatılamıyor.
     <a href="anlatim.mp4">Dosyayı indirin</a>.</p>
</video>
```

`controls` özniteliği oynatma denetimlerini gösterir. Yazılmadığında kullanıcının içeriği
durdurma, sesini kısma veya ileri sarma yolu kalmaz; bu nedenle kullanıcının denetlemesi
gereken her medyada yazılır.

`preload` ne kadar verinin önceden indirileceğini bildirir: `none` hiçbir şey, `metadata`
yalnızca süre ve boyut bilgisi, `auto` tarayıcının uygun gördüğü kadarı. Sayfada oynatılıp
oynatılmayacağı belli olmayan bir medyada `metadata` değeri, süreyi gösterip veriyi
indirmeden bekler.

Ögenin içindeki `p` bloğu, medya ögesini işlemeyen bir istemci için son çare içeriktir;
`source` ve `track` ögelerinden sonra yazılır.

#### Kaynak Seçimi

`source` ögeleri sırayla denenir ve **ilk desteklenen** kaynak seçilir. `type` özniteliği
kap biçimini ve kodek bilgisini taşır; bu bildirim sayesinde tarayıcı dosyayı indirmeden
oynatıp oynatamayacağına karar verir.

```js
// medya.mjs — source ogelerinin sirayla denenmesi
const kaynaklar = [
  { adres: "anlatim.av1.mp4", tur: 'video/mp4; codecs="av01.0.05M.08"' },
  { adres: "anlatim.vp9.webm", tur: 'video/webm; codecs="vp9"' },
  { adres: "anlatim.h264.mp4", tur: 'video/mp4; codecs="avc1.42E01E"' },
];

function sec(desteklenen) {
  for (const kaynak of kaynaklar) {
    const kod = kaynak.tur.match(/codecs="([^"]+)"/)?.[1] ?? "";
    const kap = kaynak.tur.split(";")[0].trim();
    if (desteklenen.kaplar.includes(kap) && desteklenen.kodekler.some((k) => kod.startsWith(k))) {
      return kaynak.adres;
    }
  }
  return null;
}

const istemciler = [
  { ad: "A", kaplar: ["video/mp4", "video/webm"], kodekler: ["av01", "vp9", "avc1"] },
  { ad: "B", kaplar: ["video/mp4"], kodekler: ["avc1"] },
  { ad: "C", kaplar: ["video/webm"], kodekler: ["vp9"] },
  { ad: "D", kaplar: ["video/ogg"], kodekler: ["theora"] },
];

for (const istemci of istemciler) {
  console.log("istemci " + istemci.ad + ":", sec(istemci) ?? "hicbir kaynak oynatilamaz");
}

console.log("--- altyazi parcalarinin kind degerleri ---");
const parcalar = [
  { kind: "captions", srclang: "tr", label: "Türkçe altyazı", default: true },
  { kind: "subtitles", srclang: "en", label: "English subtitles" },
  { kind: "descriptions", srclang: "tr", label: "Sesli betimleme" },
];
for (const parca of parcalar) {
  const satir = parca.kind.padEnd(14) + parca.srclang.padEnd(4)
    + parca.label.padEnd(20) + (parca.default ? "varsayilan" : "");
  console.log(satir.trimEnd());
}
```

```
istemci A: anlatim.av1.mp4
istemci B: anlatim.h264.mp4
istemci C: anlatim.vp9.webm
istemci D: hicbir kaynak oynatilamaz
--- altyazi parcalarinin kind degerleri ---
captions      tr  Türkçe altyazı      varsayilan
subtitles     en  English subtitles
descriptions  tr  Sesli betimleme
```

Çıktı üç şeyi gösterir. Sıra belirleyicidir: A istemcisi üç kaynağı da oynatabilir ve
listedeki ilkini seçer; bu nedenle en verimli biçim başa yazılır. B ve C istemcileri
listedeki ilk uygun kaynağa iner. D istemcisi hiçbirini oynatamaz ve son çare içeriği
görür.

Kap biçimi ile kodek ayrı şeylerdir: aynı kap farklı kodekler taşıyabilir ve bir istemci
kabı desteklerken içindeki kodeği desteklemeyebilir. `type` bildiriminde yalnızca kabı
yazmak bu ayrımı gizler ve tarayıcının yanlış kaynağı seçip başarısız olmasına yol açar.

#### Metin Parçaları

`track` ögesi medyaya zamanlanmış metin bağlar. `kind` değeri parçanın işlevini bildirir ve
işlevler birbirinin yerine geçmez.

`captions` konuşmanın yanı sıra anlamlı sesleri de yazar —kapı sesi, uyarı sinyali— ve
duyamayan kullanıcı içindir. `subtitles` yalnızca konuşmayı başka bir dile çevirir; sesi
duyan ama dili bilmeyen kullanıcı içindir. İkisi görsel olarak benzer görünse de içerikleri
farklıdır ve biri diğerinin yerine kullanılamaz.

`descriptions` görüntüdeki, konuşmada geçmeyen bilgiyi anlatır ve görmeyen kullanıcı
içindir. `chapters` medyayı bölümlere ayırır; kullanıcı bölüm listesinden atlayabilir.

`default` özniteliği, kullanıcı başka bir seçim yapmamışsa açık başlayacak parçayı bildirir.
`srclang` parçanın dilidir, `label` ise kullanıcıya gösterilen addır.

Altyazı, medyaya sonradan eklenen bir iyileştirme değildir. Sesli bir içeriğin metin
karşılığı yoksa, o içerik duyamayan kullanıcılar için hiç yok demektir; bu, alternatif
metni olmayan bir görselle aynı durumdur.

#### Kapak Görseli ve Ayrılan Yer

`poster` özniteliği, video oynatılmadan önce gösterilecek görseli bildirir. Yazılmadığında
tarayıcı medyanın ilk karesini bekler; `preload="none"` ile birlikte hiçbir kare
inmeyeceği için alan boş kalır.

`width` ve `height` öznitelikleri burada da görsellerdeki işi görür: Görseller dersinde
kurulduğu gibi, en boy oranını bildirerek medya inmeden yer ayrılmasını ve altındaki
içeriğin kaymamasını sağlar. Kapak görselinin oranı videonun oranıyla aynı tutulur;
farklıysa oynatma başladığında bir sıçrama görülür.

#### Zaman Aralığına Bağlanmak

Medyanın belirli bir anına, adresin parça bölümüyle bağlanılabilir. Bağlantılar dersinde
görüldüğü gibi parça bölümü sunucuya gitmez; burada oynatıcı tarafından yorumlanır.

```html
<p>Sökme adımı <a href="bakim.mp4#t=90">90. saniyede</a> başlar;
   <a href="bakim.mp4#t=90,145">90–145 saniye aralığı</a> yalnızca o adımı gösterir.</p>
```

Değer saniye cinsindendir; iki değer virgülle yazıldığında başlangıç ve bitiş belirtilir.
Bu yazım, uzun bir kaydın belirli bir bölümüne metinden başvurmayı olanaklı kılar ve
kullanıcıyı elle sarmaktan kurtarır. Desteklenmediğinde bağlantı medyayı baştan açar;
başarısızlığın bedeli düşüktür.

#### Otomatik Oynatma

`autoplay` özniteliği medyanın kullanıcı istemeden başlamasını ister. Bu isteğin yerine
getirilmesi tarayıcılarca kısıtlanmıştır ve kısıtın gerekçesi kullanıcıdır: beklenmedik
ses, ekran okuyucu çıktısını bastırır, ortak alanlarda rahatsızlık verir ve ölçülü veri
bağlantısında maliyet doğurur.

Kısıtın biçimi tarayıcıya göre değişir; ortak nokta, sesi açık başlayan bir medyanın
kullanıcı etkileşimi olmadan oynamamasıdır. Sessiz başlayan medyalar için kısıt daha
gevşektir. Bu davranış özellik saptamayla sınanmaz; başlatma isteği bir söz döndürür ve
reddedilebilir.

Uygulamada iki sonuç çıkar. Otomatik oynatmanın çalışacağı varsayılmaz; medyanın kendisi
kullanıcı başlatmadığında da anlamlı bir durumda görünmelidir. `muted` yazılmadan
`autoplay` yazmak, çoğu bağlamda hiçbir şey yapmaz.

`loop` özniteliği medyayı yineler. Süre sınırı olmayan yinelemeli hareket, hareket
duyarlılığı olan kullanıcılar için sorun oluşturur; beş saniyeyi aşan yinelemeli
içeriklerde durdurma yolu bulunmalıdır.

#### İstasyon Belgesinde

```html
<figure>
  <video controls preload="metadata" width="1280" height="720" poster="bakim-kapak.jpg">
    <source src="bakim.webm" type='video/webm; codecs="vp9"'>
    <source src="bakim.mp4" type='video/mp4; codecs="avc1.42E01E"'>
    <track kind="captions" src="bakim-tr.vtt" srclang="tr" label="Türkçe altyazı" default>
    <track kind="descriptions" src="bakim-betim.vtt" srclang="tr" label="Sesli betimleme">
    <p>Video oynatılamıyor. <a href="bakim.mp4">Dosyayı indirin</a>.</p>
  </video>
  <figcaption>Şekil 2. Nem sensörünün yerinden sökülmeden okunması.</figcaption>
</figure>
```

Video bir şekildir: metnin akışından kopabilir ve akışta anılır. Denetimler açıktır,
altyazı ve betimleme parçaları bildirilmiştir, otomatik oynatma istenmemiştir.

#### Özet

- `controls` yazılmadığında kullanıcının medyayı durdurma yolu kalmaz; denetim gerektiren
  her medyada yazılır.
- `source` ögeleri sırayla denenir ve ilk desteklenen seçilir; `type` bildirimi kap ve
  kodek bilgisini birlikte taşır.
- `captions` duyamayan, `subtitles` dili bilmeyen, `descriptions` görmeyen kullanıcı
  içindir; biri diğerinin yerine geçmez.
- Metin karşılığı olmayan sesli içerik, duyamayan kullanıcılar için yok sayılır.
- Otomatik oynatma isteği reddedilebilir; sesli otomatik oynatma çoğu bağlamda
  gerçekleşmez ve belge bunun olmadığı duruma göre yazılır.

#### Sonraki Adım

Ses ve video belgenin kendi kaynaklarıydı. Bazı içerikler ise başka bir belgenin
tamamıdır: bir harita, bir oynatıcı, başka bir siteden gelen bir bölüm. Bunları belgeye
gömmek, denetimi kısmen dışarıya vermek demektir. Sonraki ders çerçeve gömmeyi ve
gömülen belgenin yeteneklerini kısıtlayan yalıtım özniteliklerini ele alır.

### Gömülü İçerik

Source: https://academia.sh/tr/kurslar/web-temelleri-ve-html/gomulu-icerik

Başka bir belgenin çerçeveyle gömülmesi, gömülen belgenin yalıtımı, yetenek kısıtlama düzeneği ve gömmenin başarım ile erişilebilirlik sonuçları.

Ses ve video belgenin kendi kaynaklarıydı. Bazı içerikler ise başka bir belgenin
tamamıdır: bir harita, bir oynatıcı, başka bir siteden gelen bir bölüm. Bunları gömmek,
belgenin içine ikinci bir belge yerleştirmek demektir.

`iframe` ögesi bu işi yapar. Ögenin adı "satır içi çerçeve" anlamına gelir ve tam olarak
budur: belgenin akışı içinde, kendi başına yüklenen ve kendi ağacı olan ayrı bir belge.

#### Gömülen Belge Ayrı Bir Belgedir

`iframe` içindeki belge, dış belgenin ağacının parçası değildir. Kendi kökü, kendi baş
bölümü, kendi biçemleri ve kendi betikleri vardır. Dış belgenin biçem kuralları içeriye
uygulanmaz; içerideki betik dış belgenin ağacına doğrudan erişemez.

Bu ayrım bir kısıt değil, gömmenin tüm gerekçesidir. Denetlemediğiniz bir kaynaktan gelen
içeriği belgenize doğrudan yerleştirmek, o içeriğin belgenizin tamamına erişmesi demektir.
Çerçeve, aradaki sınırı kurar.

Sınırın dayanağı **kaynak (origin)** kavramıdır: şema, konak ve bağlantı noktası
üçlüsü. İki belge aynı kaynaktan geliyorsa birbirinin ağacına erişebilir; farklı
kaynaklardan geliyorsa erişemez. İnternet Nasıl Çalışır kursundaki Tarayıcı Ne Yapar
dersinde ayrılan adres bölümleri burada bir güvenlik sınırına dönüşür.

#### Yalıtım Özniteliği

Farklı kaynak ayrımı temel bir sınır çizer ama yeterli değildir: gömülen belge yine de
betik çalıştırabilir, form gönderebilir, yeni pencere açabilir, üst belgeyi başka bir
adrese götürebilir. `sandbox` özniteliği bu yetenekleri kapatır.

Özniteliğin çalışma biçimi terstir: yazıldığı anda **hiçbir yetenek verilmez**; geri
istenen yetenekler tek tek sayılır.

```js
// gomulu.mjs — sandbox ozniteliginin yetenek modeli
const YETENEKLER = [
  ["allow-scripts", "betik çalıştırma"],
  ["allow-forms", "form gönderme"],
  ["allow-popups", "yeni pencere açma"],
  ["allow-same-origin", "kendi kaynağıyla aynı sayılma"],
  ["allow-top-navigation", "üst belgeyi başka adrese götürme"],
  ["allow-modals", "kip pencere açma"],
];

function cozumle(oznitelikDegeri) {
  if (oznitelikDegeri === null) return new Set(YETENEKLER.map(([ad]) => ad)); // sandbox yok
  return new Set(oznitelikDegeri.split(/\s+/).filter(Boolean));
}

const durumlar = [
  ["sandbox ozniteligi yok", null],
  ["sandbox (bos)", ""],
  ["sandbox=allow-scripts", "allow-scripts"],
  ["sandbox=allow-scripts allow-forms", "allow-scripts allow-forms"],
];

for (const [ad, deger] of durumlar) {
  const acik = cozumle(deger);
  console.log("[" + ad + "]");
  for (const [anahtar, aciklama] of YETENEKLER) {
    console.log("  " + (acik.has(anahtar) ? "acik" : "kapali").padEnd(6) + "  " + aciklama);
  }
}

console.log("--- tehlikeli birlesim ---");
const ikisi = cozumle("allow-scripts allow-same-origin");
console.log(
  "allow-scripts + allow-same-origin:",
  ikisi.has("allow-scripts") && ikisi.has("allow-same-origin")
    ? "cerceve kendi sandbox ozniteligini kaldirabilir"
    : "yalitim korunur",
);
```

```
[sandbox ozniteligi yok]
  acik    betik çalıştırma
  acik    form gönderme
  acik    yeni pencere açma
  acik    kendi kaynağıyla aynı sayılma
  acik    üst belgeyi başka adrese götürme
  acik    kip pencere açma
[sandbox (bos)]
  kapali  betik çalıştırma
  kapali  form gönderme
  kapali  yeni pencere açma
  kapali  kendi kaynağıyla aynı sayılma
  kapali  üst belgeyi başka adrese götürme
  kapali  kip pencere açma
[sandbox=allow-scripts]
  acik    betik çalıştırma
  kapali  form gönderme
  kapali  yeni pencere açma
  kapali  kendi kaynağıyla aynı sayılma
  kapali  üst belgeyi başka adrese götürme
  kapali  kip pencere açma
[sandbox=allow-scripts allow-forms]
  acik    betik çalıştırma
  acik    form gönderme
  kapali  yeni pencere açma
  kapali  kendi kaynağıyla aynı sayılma
  kapali  üst belgeyi başka adrese götürme
  kapali  kip pencere açma
--- tehlikeli birlesim ---
allow-scripts + allow-same-origin: cerceve kendi sandbox ozniteligini kaldirabilir
```

İlk iki durum arasındaki fark, özniteliğin yazılıp yazılmamasının bütün farkı yarattığını
gösterir. Boş `sandbox`, gömülen belgeyi ayrı bir kaynağa da yerleştirir: kendi sunucusuyla
aynı kaynaktan sayılmadığı için depolanmış verilere ve çerezlere erişemez.

Son satır, birlikte yazılmaması gereken iki değeri bildirir. `allow-same-origin` gömülen
belgeyi kendi kaynağıyla aynı sayar; `allow-scripts` betik çalıştırmasına izin verir. İkisi
birlikteyken ve gömülen belge sizinle aynı kaynaktaysa, çerçeve içindeki betik dış
belgedeki `sandbox` özniteliğini kaldırıp çerçeveyi yeniden yükleyebilir — yalıtım kendi
kendini iptal eder.

#### Yetenek İzinleri

`sandbox` belgenin **davranışını** kısıtlar; `allow` özniteliği ise aygıt ve platform
yeteneklerine erişimi düzenler: kamera, mikrofon, konum, tam ekran.

```html
<iframe src="https://harita.test/kuzey-yamac"
        title="İstasyonun harita üzerindeki konumu"
        width="640" height="480" loading="lazy"
        sandbox="allow-scripts"
        allow="fullscreen; geolocation 'none'"
        referrerpolicy="no-referrer"></iframe>
```

Varsayılan, çoğu yetenek için erişimin kapalı olmasıdır; `allow` bildirimi bunu açar ve
`'none'` değeri açıkça kapatır. Bu iki öznitelik birbirinin yerine geçmez ve birlikte
kullanılır.

`referrerpolicy` özniteliği, gömülen kaynağa yapılan istekte hangi belgeden gelindiği
bilgisinin ne kadarının gönderileceğini bildirir. `no-referrer` değeri hiç göndermez;
gömülen üçüncü taraf, kullanıcının hangi sayfada olduğunu öğrenemez.

#### Ad ve Yükleme

`title` özniteliği çerçeveye bir erişilebilir ad verir. Yazılmadığında ekran okuyucu
çerçeveyi adsız bir bölge olarak duyurur ve kullanıcı içine girmeden ne olduğunu bilemez.
Bu öznitelik gömülü içerikte isteğe bağlı değildir.

`loading="lazy"` çerçevenin görüntü alanına yaklaşana kadar yüklenmemesini bildirir.
Gömülen belge kendi alt kaynaklarını da istediği için maliyeti tek bir görselden çok daha
yüksektir; sayfanın altında duran bir harita ya da oynatıcı için bu bildirim, açılış
maliyetini belirgin biçimde düşürür.

`width` ve `height` öznitelikleri burada da yer ayırma işini görür; boyut bildirimi
olmayan bir çerçeve, yüklendiğinde altındaki içeriği kaydırır.

#### Gömülmeye Karşı Korunmak

Gömme iki yönlüdür: kendi belgenizi gömebildiğiniz gibi, başkaları da onu kendi
belgelerine gömebilir. Bu, kullanıcının hangi siteyle etkileştiğini yanıltmak için
kullanılabilir — görünen belge sizinki, üzerine yerleştirilen saydam katman başkasınındır
ve kullanıcının tıkladığı yer ile bastığını sandığı düğme ayrışır.

Korunma, belgede değil sunucu yanıtında bildirilir. `Content-Security-Policy` başlığındaki
`frame-ancestors` yönergesi, belgenin hangi kaynaklarca gömülebileceğini sayar.

```
Content-Security-Policy: frame-ancestors 'self'
```

Bu bildirim belgenin yalnızca kendi kaynağındaki sayfalarca gömülmesine izin verir;
`'none'` değeri hiçbir gömmeye izin vermez. Meta Etiketleri dersinde belirtildiği gibi
aynı bildirim `http-equiv` ile de yazılabilir, ancak bu yönerge için geçerli değildir:
gömme kararı belge ayrıştırılmadan önce verilir, dolayısıyla bildirim de yanıt başlığında
bulunmalıdır.

Oturum verisi taşıyan ya da işlem başlatan her belgede bu başlık yazılır. Yazılmadığında
belgenin gömülmesi serbesttir ve bu, belgenin denetimi dışındaki bir sayfanın onu
kullanıcının önüne istediği bağlamda koymasına izin verir.

#### Gömmenin Bedeli

Gömülen her belge bağımsız bir yükleme zinciri başlatır: kendi ad çözümlemesi, kendi
bağlantısı, kendi alt kaynakları. Kaynak Yükleme Sırası dersinde sayılan adımların tümü
yeniden yürütülür ve bu adımlar dış belgenin kaynaklarıyla aynı bant genişliğini paylaşır.

Bir işlevin gömme olmadan gerçekleştirilebildiği durumlarda gömmemek yeğlenir. Bir
konumun gösterilmesi için etkileşimli bir harita gerekmiyorsa, bir görsel ve koordinat
metni aynı bilgiyi çok daha ucuza verir. İstasyon belgesinde de tercih bu yöndedir:
konum bir şekille anlatılmış, harita gömülmemiştir.

#### Özet

- `iframe` içindeki belge ayrı bir belgedir; kendi ağacı, biçemleri ve betikleri vardır ve
  farklı kaynaktan geliyorsa dış belgeye erişemez.
- `sandbox` yazıldığı anda tüm yetenekleri kapatır; istenen yetenekler tek tek geri verilir.
- `allow-scripts` ile `allow-same-origin` birlikte ve aynı kaynakta yazıldığında yalıtım
  çerçevenin kendisi tarafından kaldırılabilir.
- `sandbox` davranışı, `allow` aygıt ve platform yeteneklerini düzenler; ikisi birbirinin
  yerine geçmez.
- `title` gömülü içerikte zorunludur; `loading="lazy"` ve boyut bildirimi, gömmenin açılış
  maliyetini ve içerik kaymasını sınırlar.

#### Sonraki Adım

Bu konu belgeye anlam kazandıran ögeleri tamamladı. Geriye, hiçbir tanımlı anlamı olmayan
ama belgeye veri iliştirmeye yarayan bir düzenek kalıyor: her ögeye takılabilen, ad
çakışması üretmeyen ve betiklerce okunabilen öznitelikler. Sonraki ders bu düzeneği ve
öznitelik adının program tarafındaki karşılığına nasıl çevrildiğini ele alır.

### Veri Öznitelikleri

Source: https://academia.sh/tr/kurslar/web-temelleri-ve-html/veri-oznitelikleri

Belgeye ad çakışması olmadan veri iliştirmek, öznitelik adı ile program tarafındaki anahtarın dönüşümü ve değerlerin dizgi olma sonucu.

Önceki dersler belgeye anlam kazandıran ögeleri tamamladı. Geriye tanımlı bir anlamı
olmayan ama işe yarayan bir düzenek kalıyor: her ögeye takılabilen, ad çakışması üretmeyen
ve program tarafından okunabilen öznitelikler.

Sorun şudur: bir ögeye, dilin tanımlamadığı bir bilgi iliştirmek gerekebilir — bir
sensörün kimliği, bir kaydın durumu, bir bölümün yenilenme aralığı. Bu bilgi belgede
görünmez ama davranış katmanının ona erişmesi gerekir.

#### Neden Ayrı Bir Düzenek

Böyle bir bilgiyi taşımanın kendiliğinden akla gelen yolları sorunludur.

Var olan bir özniteliği amacı dışında kullanmak —örneğin `title` ya da `class` içine veri
gömmek— o özniteliğin tanımlı davranışını bozar: `title` ekran okuyucuda duyurulur, `class`
sunum katmanının seçicilerine karışır.

Kendi adını uydurmak —`sensor-id="4"` gibi— geçerli bir belge üretmez. İşaretleme Dili
Kavramı dersinde belirtildiği gibi ayrıştırıcı bilinmeyen özniteliği saklar, dolayısıyla
çalışır; ancak dile ileride gerçekten `sensor-id` adında bir öznitelik eklenirse belgedeki
anlam sessizce değişir.

`data-` öneki bu iki soruna birden yanıt verir. Önek dile ayrılmıştır: dilin kendi
öznitelikleri hiçbir zaman bu önekle başlamaz, dolayısıyla çakışma olanaksızdır. Ve
tanımlı bir davranışı yoktur: değeri okunmadıkça hiçbir şey yapmaz.

```html
<article data-kayit-kimligi="4"
         data-sensor-turu="nem"
         data-olcum-araligi="600"
         data-dogrulandi="false">
  <h3>Nem sensörü yeniden ayarlandı</h3>
</article>
```

#### Ad Dönüşümü

Öznitelik adı ile program tarafındaki anahtar arasında tanımlı bir dönüşüm vardır.
Öznitelik adı küçük harfle ve tireli yazılır; program tarafında önek düşer ve tireler
kaldırılıp sonraki harf büyütülür.

```js
// veri.mjs — data-* adi ile dataset anahtari arasindaki donusum
function ozniteliktenAnahtara(yazilanAd) {
  const ad = yazilanAd.toLowerCase(); // ayristirici oznitelik adlarini kucultur
  if (!ad.startsWith("data-")) return null;
  return ad.slice(5).replace(/-([a-z])/g, (_, harf) => harf.toUpperCase());
}

function anahtardanOznitelige(anahtar) {
  if (/-[a-z]/.test(anahtar)) return null; // kucuk harften once tire yasaktir
  return "data-" + anahtar.replace(/[A-Z]/g, (harf) => "-" + harf.toLowerCase());
}

const oznitelikler = [
  "data-istasyon",
  "data-olcum-araligi",
  "data-son-kalibrasyon-tarihi",
  "data-x2",
  "data-Sensor",
  "class",
];

console.log("oznitelik adi                 dataset anahtari       geri donusum");
for (const ad of oznitelikler) {
  const anahtar = ozniteliktenAnahtara(ad);
  const geri = anahtar === null ? "—" : anahtardanOznitelige(anahtar);
  console.log(ad.padEnd(30) + String(anahtar ?? "—").padEnd(23) + String(geri));
}

console.log("--- degerler her zaman dizgidir ---");
const kumesi = { istasyon: "kuzey-yamac", olcumAraligi: "600", etkin: "false" };
for (const [anahtar, deger] of Object.entries(kumesi)) {
  console.log(anahtar.padEnd(14), JSON.stringify(deger), "-> typeof:", typeof deger);
}
console.log("Number(olcumAraligi) =", Number(kumesi.olcumAraligi));
console.log('Boolean("false")     =', Boolean(kumesi.etkin));
```

```
oznitelik adi                 dataset anahtari       geri donusum
data-istasyon                 istasyon               data-istasyon
data-olcum-araligi            olcumAraligi           data-olcum-araligi
data-son-kalibrasyon-tarihi   sonKalibrasyonTarihi   data-son-kalibrasyon-tarihi
data-x2                       x2                     data-x2
data-Sensor                   sensor                 data-sensor
class                         —                      —
--- degerler her zaman dizgidir ---
istasyon       "kuzey-yamac" -> typeof: string
olcumAraligi   "600" -> typeof: string
etkin          "false" -> typeof: string
Number(olcumAraligi) = 600
Boolean("false")     = true
```

Beşinci satır bir tuzağı gösterir: `data-Sensor` yazımı beklenen anahtarı üretmez, çünkü
Etiketler, Öznitelikler ve Varlıklar dersinde belirtildiği gibi ayrıştırıcı öznitelik
adlarını küçültür. Öznitelik adında büyük harf kullanılmaz; çok sözcüklü adlar tireyle
ayrılır.

Dördüncü satır rakamların dönüşüme girmediğini gösterir: tire yalnızca kendisinden sonra
küçük harf geliyorsa kaldırılır.

#### Değerler Dizgidir

Çıktının ikinci bölümü, veri özniteliklerinin en sık gözden kaçan özelliğini gösterir:
değerler her zaman dizgidir. Sayı yazılmış olsa bile dizgi olarak okunur ve
karşılaştırmalarda çevrilmesi gerekir.

Son satır bunun mantıksal değerlerdeki sonucudur. `"false"` dizgisi boş olmadığı için
doğru sayılır; `Boolean` çevrimi beklenen sonucu vermez. Mantıksal bir bilgi taşınacaksa
iki yol vardır: değeri açıkça karşılaştırmak, ya da mantıksal özniteliklerin kuralını
izleyip **varlığı** anlam saymak — özniteliği yazmak doğru, hiç yazmamak yanlış demektir.

#### Ne Zaman Uygun Değil

Veri özniteliği bir depolama alanı değildir. Üç durumda kullanılmaz.

**İçerik taşımak için.** Belgede görünmesi gereken bir metin bir veri özniteliğine
yazılırsa, kaynak metinde vardır ama belgede yoktur: seçilemez, aranamaz, çevrilmez ve
ekran okuyucuya sunulmaz.

**Dilin karşılığı olan bir bilgi için.** Bir ögenin devre dışı olduğu, bir bölümün
gizlendiği, bir alanın zorunlu olduğu bilgisi dilde tanımlıdır ve o öznitelikle bildirilir.
Bu bilgiyi veri özniteliğine yazmak, erişilebilirlik ağacına hiçbir şey iletmez.

**Büyük veri için.** Öznitelik değerleri belgeyle birlikte her istekte gönderilir ve
belge büyüdükçe ayrıştırma da yavaşlar. Bir kaydın tamamını ögeye iliştirmek yerine
kimliğini iliştirip veriyi ayrı istemek yeğlenir.

#### Durum Bildirimi: Sınıf mı, Veri mi

Bir ögenin o andaki durumunu —seçili, yükleniyor, hatalı— bildirmenin iki yolu vardır ve
ikisi de kullanımdadır.

Sınıf adı eklemek yaygındır ama sınıf listesi bir küme olduğu için değer taşımaz: "durum
yükleniyor" bilgisi ancak `yukleniyor` adlı bir sınıfın varlığıyla anlatılır ve birden çok
durumdan yalnızca birinin geçerli olması elle korunur. Eski durumu silmek unutulursa iki
durum aynı anda görünür.

Veri özniteliği bir ad–değer çiftidir; `data-durum="yukleniyor"` yazımında yeni değer
eskisinin yerini alır ve iki durumun aynı anda bulunması olanaksızdır. Sunum katmanı bu
değeri öznitelik seçicisiyle okur; seçiciler sonraki kursun konusudur.

Bu ayrım, kullanıcıya duyurulması gereken durumlar için geçerli değildir. Bir alanın
hatalı olduğu, bir düğmenin basılı olduğu, bir bölümün açık olduğu bilgisi erişilebilirlik
ağacına iletilmelidir ve bunun yolu dilin kendi öznitelikleri ile ARIA durum
öznitelikleridir. Veri özniteliği bu ağaca hiçbir şey iletmez.

#### Dışarıya Açılan Veri

Veri öznitelikleri belgeyi yazan ile belgenin kendi betikleri arasındaki bir
sözleşmedir. Adları serbesttir, anlamları belgeye özgüdür ve dışarıdan bir program
onlardan bir şey çıkaramaz.

Bilgi dışarıya —dizinleyicilere, toplayıcılara— açılacaksa bu düzenek yetmez; ortak bir
sözcük dağarcığı gerekir. Belgeye gömülen yapılandırılmış veri sözleşmeleri bu işi görür
ve tanımlı bir kavram kümesine dayanır. Ayrım şudur: `data-` kendi programınız için,
yapılandırılmış veri başkasının programı için.

#### İstasyon Belgesinde

```html
<section aria-labelledby="kayitlar" data-yenileme-araligi="600">
  <h2 id="kayitlar">Kalibrasyon Kayıtları</h2>

  <article data-kayit-kimligi="4" data-sensor-turu="nem">
    <h3>Nem sensörü yeniden ayarlandı</h3>
    <p>Referans değere göre yüzde iki sapma düzeltildi.</p>
    <footer>
      <p>Kayıt tarihi: <time datetime="2024-03-12T07:30">12 Mart sabahı</time></p>
    </footer>
  </article>
</section>
```

İliştirilen üç değerin üçü de belgede görünmesi gerekmeyen, davranış katmanının işine
yarayacak bilgilerdir. Kaydın tarihi ise veri özniteliğine değil `time` ögesine yazılmıştır:
o bilgi hem görünür hem de dilde karşılığı olan bir bilgidir.

#### Özet

- `data-` öneki dile ayrılmıştır; bu önekle yazılan öznitelikler dilin özellikleriyle
  çakışmaz ve tanımlı bir davranışları yoktur.
- Öznitelik adı küçük harfle ve tireli yazılır; program tarafındaki anahtar önek düşürülüp
  tireden sonraki harf büyütülerek üretilir.
- Değerler her zaman dizgidir; sayısal karşılaştırmalarda çevrilir, mantıksal bilgide
  `"false"` dizgisi doğru sayılır.
- Belgede görünmesi gereken içerik, dilde karşılığı olan bilgi ve büyük veri bu
  özniteliklere yazılmaz.

#### Sonraki Adım

Belge bu noktada yapılı, bölümlenmiş ve medya taşır durumda; ancak tek yönlü. Okuyucu
bilgi alıyor, veremiyor. Sonraki konu bu yönü açar: bir formun sunucuya gerçekte ne gönderdiğini, o
gövdenin nasıl kodlandığını ve gönderim başlamadan önce hangi denetimlerin yapıldığını
ele alır. İlk ders, gönderimin kendisiyle başlar.

## Formlar

Form gönderiminin sunucuya ulaşan gövdesi, girdi türleri, etiketleme, kısıt doğrulaması, dosya yükleme ve erişilebilir hata bildirimi.

### Form Yapısı

Source: https://academia.sh/tr/kurslar/web-temelleri-ve-html/form-yapisi

Formun ürettiği giriş listesi, gönderim yönteminin adres ve gövdeye etkisi, yöntem seçiminin gerekçesi ve alanların gruplanması.

Belge bu noktada yapılı, bölümlenmiş ve medya taşır durumda; ancak tek yönlü. Okuyucu
bilgi alıyor, veremiyor. Bu konu o yönü açar.

Form, kullanıcıdan alınan değerleri bir isteğe çeviren düzenektir. İnternet Nasıl Çalışır
kursundaki HTTP İstek ve Yanıtı dersi isteğin parçalarını —istek satırı, başlıklar, gövde—
tanımlamıştı. Bu ders, o parçalara giden değerin belgede nasıl toplandığını ve hangi
biçimde yazıldığını ele alır.

#### Formun Parçaları

`form` ögesi bir gönderim birimidir. İki özniteliği gönderimin nereye ve nasıl
yapılacağını belirler.

`action` isteğin gideceği adrestir. Yazılmadığında belgenin kendi adresi kullanılır.

`method` HTTP yöntemidir: `get` ya da `post`. Yazılmadığında `get` varsayılır.

Form içindeki alanlardan yalnızca **adı olanlar** gönderilir. `name` özniteliği bir
alanı gönderime katan şeydir; adı olmayan bir alan ekranda görünür, değeri girilir ve
sunucuya hiç ulaşmaz.

#### Giriş Listesi

Gönderim başladığında tarayıcı form içindeki alanları dolaşır ve ad–değer çiftlerinden
oluşan bir **giriş listesi (entry list)** kurar. Bu liste sıralıdır ve belgedeki alan
sırasını izler; aynı ad birden çok kez bulunabilir.

Listeye girmeyen alanlar vardır: adı olmayanlar, devre dışı bırakılmış olanlar ve
işaretlenmemiş onay kutuları. Bu son kural sık gözden kaçar — işaretlenmemiş bir onay
kutusu "yanlış" değeriyle gönderilmez, hiç gönderilmez.

#### Yöntem Gövdeyi Belirler

Giriş listesi kurulduktan sonra nereye yazılacağı yönteme bağlıdır. Aşağıdaki betik yerel
bir sunucu açar, iki gönderimi de yapar ve sunucunun gördüğünü yazdırır.

```js
// gonderim.mjs — get ve post gonderimlerinin sunucuda gorunumu
import { createServer } from "node:http";

// Formun urettigi ad-deger ciftleri (giris listesi).
const girisListesi = [
  ["istasyon", "kuzey-yamac"],
  ["olcum", "sicaklik"],
  ["deger", "-4.2"],
  ["not", "rüzgâr kuvvetli & yağış yok"],
];

const sunucu = createServer((istek, yanit) => {
  const parcalar = [];
  istek.on("data", (parca) => parcalar.push(parca));
  istek.on("end", () => {
    const govde = Buffer.concat(parcalar);
    console.log("istek satiri :", istek.method, istek.url);
    console.log("content-type :", istek.headers["content-type"] ?? "(yok)");
    console.log("govde        :", govde.length ? JSON.stringify(govde.toString()) : "(bos)");
    console.log("---");
    yanit.writeHead(204).end();
  });
});

sunucu.listen(0, "127.0.0.1", async () => {
  const taban = "http://127.0.0.1:" + sunucu.address().port;
  const sorgu = new URLSearchParams(girisListesi).toString();

  // method="get": eylem adresinin sorgu bolumu silinir, yerine giris listesi yazilir.
  const adres = new URL("/kayit?eski=deger", taban);
  adres.search = sorgu;
  await fetch(adres);

  // method="post", varsayilan enctype: giris listesi govdeye yazilir.
  await fetch(new URL("/kayit?eski=deger", taban), {
    method: "POST",
    headers: { "content-type": "application/x-www-form-urlencoded" },
    body: sorgu,
  });

  sunucu.close();
});
```

```
istek satiri : GET /kayit?istasyon=kuzey-yamac&olcum=sicaklik&deger=-4.2&not=r%C3%BCzg%C3%A2r+kuvvetli+%26+ya%C4%9F%C4%B1%C5%9F+yok
content-type : (yok)
govde        : (bos)
---
istek satiri : POST /kayit?eski=deger
content-type : application/x-www-form-urlencoded
govde        : "istasyon=kuzey-yamac&olcum=sicaklik&deger=-4.2&not=r%C3%BCzg%C3%A2r+kuvvetli+%26+ya%C4%9F%C4%B1%C5%9F+yok"
---
```

Çıktı dört şey söyler.

**`get` yöntemi gövde göndermez.** Liste, adresin sorgu bölümüne yazılır. Bunun sonucu,
değerlerin tarayıcı geçmişinde, yer imlerinde ve sunucu kütüklerinde görünmesidir.

**Eylem adresinin var olan sorgusu silinir.** İstek satırındaki `eski=deger` çifti
kaybolmuştur. `get` ile gönderilen bir formda `action` adresine sorgu yazmak işe yaramaz;
gizli bir alanla taşınması gerekir.

**`post` yöntemi listeyi gövdeye yazar** ve adresteki sorguyu korur.

**Kodlama biçimi aynıdır.** İki durumda da liste `ad=deger` çiftleri hâlinde `&` ile
birleştirilir; boşluklar `+`, ASCII dışı karakterler ve `&` yüzde kodlamasıyla yazılır.
`rüzgâr kuvvetli & yağış yok` değerindeki ayıraç `%26` olmuştur — Bağlantılar dersindeki
yüzde kodlamasının aynısıdır. Bu biçimin adı `application/x-www-form-urlencoded` ve
`post` gönderiminde bu ad `Content-Type` başlığında bildirilir.

#### Yöntem Seçimi

Seçim bir alışkanlık değil, isteğin doğasından çıkar. HTTP İstek ve Yanıtı dersinde
tanımlanan iki özellik burada doğrudan kullanılır: **güvenli** yöntem sunucuda değişiklik
yapmaz, **değişmez** yöntem birden çok kez uygulandığında aynı durumu bırakır.

`get` güvenlidir. Yalnızca sunucuda bir değişiklik yapmayan gönderimlerde kullanılır: bir
arama, bir süzme, bir sorgu. Sonucun adres olarak paylaşılabilir ve yer imine eklenebilir
olması bir kazançtır.

`post` güvenli değildir. Sunucuda bir değişiklik yapan her gönderim bu yöntemle yapılır:
bir kayıt eklemek, bir bildirim göndermek. Değerler adreste görünmez ve gönderimin
yinelenmesi tarayıcı tarafından uyarıyla karşılanır.

Bir kayıt gönderimini `get` ile yapmak, bağlantının paylaşılması ya da adresin
yenilenmesi durumunda işlemin yeniden yapılmasına yol açar. Ayrım bir gizlilik önlemi
değildir — `post` gövdesi de şifresizdir; koruma HTTPS'in Rolü dersinde anlatılan taşıma
katmanından gelir.

#### Alanların Gruplanması

`fieldset` ögesi birbirine bağlı alanları gruplar, `legend` ögesi gruba bir ad verir ve
grubun ilk çocuğu olarak yazılır.

```html
<fieldset>
  <legend>Ölçüm kaynağı</legend>
  <p><label><input type="radio" name="kaynak" value="otomatik" checked> Otomatik</label></p>
  <p><label><input type="radio" name="kaynak" value="elle"> Elle girildi</label></p>
</fieldset>
```

Gruplama, seçenek düğmeleri için isteğe bağlı değildir. Tek tek etiketler yalnızca
"Otomatik" ve "Elle girildi" der; grubun neyin seçeneği olduğunu söyleyen şey `legend`
metnidir. Ekran okuyucu her seçeneği duyururken grup adını da duyurur.

`fieldset` ögesine yazılan `disabled` özniteliği içindeki alanları devre dışı bırakır — grubun
`legend` ögesi içinde kalan alanlar bu kuralın dışındadır — ve devre dışı alanlar giriş listesine
girmez; çok adımlı formlarda bir bölümü toptan kapatmanın yolu budur.

#### Gönderim Düğmesi

`button` ögesinin `type` özniteliği üç değer alır: `submit` formu gönderir, `reset` alanları
başlangıç değerlerine döndürür, `button` hiçbir şey yapmaz. Bir form içinde yazılmayan
`type` özniteliği `submit` sayılır — bir formun içine konan sıradan bir düğme, farkında
olunmadan formu gönderir.

`formaction` ve `formmethod` öznitelikleri, o düğmeyle yapılan gönderim için formun
kendi ayarlarını geçersiz kılar; aynı formun iki farklı adrese gönderilmesi böyle
sağlanır.

#### Form Dışındaki Alanlar

Bir alanın hangi forma ait olduğu genellikle iç içelikten çıkar: `form` ögesinin içinde
duran her alan ona aittir. Formlar iç içe yazılamaz, dolayısıyla bu ilişki tektir.

İç içelik her zaman istenen düzeni vermez — bir tablonun içindeki alanların tablo dışındaki
bir forma ait olması gibi. Bu durumda alana yazılan `form` özniteliği, ait olduğu formun
kimliğini bildirir.

```html
<form id="duzeltme" action="/olcum/duzeltme" method="post"></form>

<table>
  <tr>
    <th scope="row">Pazartesi</th>
    <td><input form="duzeltme" name="pzt" type="number" value="-4"></td>
  </tr>
</table>

<button type="submit" form="duzeltme">Düzeltmeyi gönder</button>
```

Gönderim düğmesi de bu özniteliği kullanabilir; formun kendisi boş kalır ve yalnızca
gönderim ayarlarını taşır.

`autocomplete` özniteliği tarayıcının daha önce girilmiş değerleri önermesini düzenler. Alan
düzeyinde yazıldığında değerin türünü de bildirir; bu bildirim, bilişsel yük veya devinim güçlüğü
olan kullanıcılar için formu doldurmayı kolaylaştıran düzeneklerden biridir; açık etiketler ve
yönergelerle birlikte çalışır. Yalnızca paylaşılan makinelerde saklanmaması gereken alanlarda
kapatılır.

#### İstasyon Belgesinde

```html
<section aria-labelledby="bildirim">
  <h2 id="bildirim">Ölçüm Düzeltmesi Bildir</h2>

  <form action="/olcum/duzeltme" method="post">
    <fieldset>
      <legend>Ölçüm bilgisi</legend>
      <p><label for="istasyon">İstasyon kodu</label>
         <input id="istasyon" name="istasyon" type="text" value="kuzey-yamac"></p>
      <p><label for="deger">Ölçülen değer (°C)</label>
         <input id="deger" name="deger" type="number"></p>
    </fieldset>

    <p><button type="submit">Düzeltmeyi gönder</button></p>
  </form>
</section>
```

Yöntem `post` seçilmiştir çünkü gönderim sunucuda bir kayıt oluşturur. Alanlar bir grupta
toplanmış ve gruba ad verilmiştir.

#### Özet

- Form, alanlardan bir giriş listesi kurar; yalnızca adı olan, devre dışı olmayan ve
  işaretli olan alanlar bu listeye girer.
- `get` yönteminde liste adresin sorgu bölümüne yazılır ve eylem adresindeki var olan
  sorgu silinir; `post` yönteminde gövdeye yazılır ve adresteki sorgu korunur.
- İki yöntemde de varsayılan kodlama aynıdır: `ad=deger` çiftleri, boşluk `+`, özel
  karakterler yüzde kodlamasıyla.
- Yöntem seçimi güvenli ve değişmez olma özelliklerinden çıkar; sunucuda değişiklik yapan
  gönderim `post` ile yapılır.
- `fieldset` ve `legend`, seçenek gruplarında zorunludur: grubun neyin seçeneği olduğunu
  yalnızca grup adı söyler.

#### Sonraki Adım

Bu ders formu tek tür alanla kurdu. Oysa alınacak veri her zaman serbest metin değildir:
sayı, tarih, saat, seçim, çoktan seçim, dosya. Sonraki ders girdi türlerini ele alır ve
tümünün sunucuya nasıl ulaştığını —tür bilgisinin gönderimde ne kadarının kaldığını—
gerçek bir gövde üzerinden gösterir.

### Girdi Türleri

Source: https://academia.sh/tr/kurslar/web-temelleri-ve-html/girdi-turleri

Metin, sayı, tarih, seçim ve dosya alanlarının ayrımı; tür bilgisinin gönderimde ne kadarının kaldığı ve değerlerin sunucuya ulaşan biçimi.

Önceki ders formu tek tür alanla kurdu. Alınacak veri her zaman serbest metin değildir:
sayı, tarih, saat, tek seçim, çok seçim, uzun metin, dosya. `input` ögesinin `type`
özniteliği bu ayrımı yapar.

Tür seçimi üç şeyi birden etkiler: kullanıcının gördüğü arayüz, tarayıcının uyguladığı
doğrulama ve değerin gönderimdeki biçimi. Bu ders üçüncüsüne odaklanır, çünkü ilk ikisi
ancak üçüncüsü bilinerek doğru yorumlanır.

#### Türün Üç Etkisi

Bir tür bildirildiğinde tarayıcı ona uygun bir arayüz sunar: sayı alanında artırma
düğmeleri, tarih alanında bir takvim, renk alanında bir seçici. Bu arayüzler tarayıcıya ve
aygıta göre değişir; dar ekranlarda tür bildirimi klavye düzenini de belirler — sayı için
rakam tuşları, e-posta için `@` karakterini içeren düzen.

Tür ayrıca bir kısıt getirir: geçersiz bir değer girildiğinde gönderim engellenir. Bu
davranış sonraki iki dersin konusudur.

Üçüncü etki gönderim biçimidir ve tanımlıdır.

#### Değerler Sunucuya Nasıl Ulaşır

```js
// girdi.mjs — farkli turlerin urettigi degerler ve sunucudaki gorunumu
import { createServer } from "node:http";

// Isaretlenmemis onay kutusu ve devre disi alan giris listesine hic girmez.
const girisListesi = [
  ["ad", "Kuzey Yamaç"],           // type="text"
  ["sicaklik", "-4.2"],            // type="number"
  ["olcumTarihi", "2024-03-12"],   // type="date"
  ["olcumSaati", "07:30"],         // type="time"
  ["esik", "18"],                  // type="range"
  ["dogrulandi", "on"],            // isaretli type="checkbox", value yazilmamis
  ["kaynak", "otomatik"],          // secili type="radio"
  ["etiket", "nem"],               // <select> secili secenek
  ["etiket", "ruzgar"],            // <select multiple> ikinci secili secenek
  ["not", "ilk satır\r\nikinci satır"], // <textarea>: satir sonlari CRLF'e cevrilir
];

const sunucu = createServer((istek, yanit) => {
  const parcalar = [];
  istek.on("data", (parca) => parcalar.push(parca));
  istek.on("end", () => {
    const govde = Buffer.concat(parcalar).toString();
    console.log("ham govde:");
    console.log(govde);
    console.log("--- sunucuda cozulmus ciftler ---");
    for (const [ad, deger] of new URLSearchParams(govde)) {
      console.log(ad.padEnd(12), JSON.stringify(deger), "(" + typeof deger + ")");
    }
    yanit.writeHead(204).end();
  });
});

sunucu.listen(0, "127.0.0.1", async () => {
  await fetch("http://127.0.0.1:" + sunucu.address().port + "/olcum", {
    method: "POST",
    headers: { "content-type": "application/x-www-form-urlencoded" },
    body: new URLSearchParams(girisListesi).toString(),
  });
  sunucu.close();
});
```

```
ham govde:
ad=Kuzey+Yama%C3%A7&sicaklik=-4.2&olcumTarihi=2024-03-12&olcumSaati=07%3A30&esik=18&dogrulandi=on&kaynak=otomatik&etiket=nem&etiket=ruzgar&not=ilk+sat%C4%B1r%0D%0Aikinci+sat%C4%B1r
--- sunucuda cozulmus ciftler ---
ad           "Kuzey Yamaç" (string)
sicaklik     "-4.2" (string)
olcumTarihi  "2024-03-12" (string)
olcumSaati   "07:30" (string)
esik         "18" (string)
dogrulandi   "on" (string)
kaynak       "otomatik" (string)
etiket       "nem" (string)
etiket       "ruzgar" (string)
not          "ilk satır\r\nikinci satır" (string)
```

Çıktının son sütunu dersin en önemli sonucudur: **her değer dizgidir.** Tür bilgisi
gönderimde taşınmaz. Sunucu, `sicaklik` alanının bir sayı alanı olduğunu bilmez;
`"-4.2"` dizgisini alır ve sayıya çevirmek onun işidir.

Bunun doğrudan sonucu, tür bildiriminin bir sunucu güvencesi olmamasıdır. Konu, dördüncü
derste ayrıntılandırılacaktır.

Üç ayrıntı daha görünür.

**Tarih ve saat biçimi tanımlıdır.** Kullanıcının gördüğü arayüz yerel biçimi kullansa da
gönderilen değer `YYYY-AA-GG` ve `SS:DD` düzenindedir. Sunucu, kullanıcının hangi ülkede
olduğunu bilmeden değeri çözebilir.

**Aynı ad birden çok kez bulunabilir.** `etiket` alanı iki kez gönderilmiştir — çoklu
seçim listesinin iki seçili seçeneği. Sunucu tarafında bu alanın bir liste olarak
okunması gerekir; yalnızca ilk değeri alan bir çözümleme sessizce veri kaybeder.

**Uzun metin alanındaki satır sonları CRLF'e çevrilir.** Gövdede `%0D%0A` görülür.
Kullanıcı hangi işletim sisteminde olursa olsun gönderilen değer aynıdır.

#### Tür Kümesi

| Tür | Toplanan veri | Gönderilen değer |
|---|---|---|
| `text` | tek satır metin | girilen metin |
| `search` | arama terimi | girilen metin |
| `email` | e-posta adresi | girilen metin |
| `url` | adres | girilen metin |
| `tel` | telefon numarası | girilen metin, biçim denetlenmez |
| `password` | gizlenen metin | girilen metin, şifresiz |
| `number` | sayı | ondalık ayıracı nokta |
| `range` | aralıktan sayı | sayı; alan değeri göstermez |
| `date`, `time`, `month`, `week` | tarih ve saat | tanımlı biçimde |
| `checkbox` | evet/hayır ya da çoklu seçim | `value` ya da `on`; işaretsizse hiç |
| `radio` | gruptan tek seçim | seçilinin `value` değeri |
| `file` | dosya | ayrı bir kodlama gerektirir |
| `hidden` | görünmeyen taşınan değer | `value` değeri |

`password` türü değeri yalnızca ekranda gizler. Gövde şifresiz gider; koruma HTTPS'in
Rolü dersindeki taşıma katmanından gelir.

`range` türü seçilen değeri kullanıcıya göstermez ve bir değeri kesin olarak girmek
gerektiğinde uygun değildir; kesin olmayan bir ayar için uygundur.

`hidden` türü bir gizlilik düzeneği değildir: değeri kaynak metinde görünür ve
değiştirilebilir. Yalnızca sunucudan gelen bir bilgiyi geri taşımak içindir.

#### Ayrı Ögeler

Üç girdi türü `input` ögesiyle yazılmaz.

`textarea` çok satırlı metindir. Başlangıç değeri `value` özniteliğiyle değil, ögenin
içeriğiyle verilir.

`select` bir listeden seçimdir; `option` ögeleri seçenekleri, `optgroup` ögesi seçenek
gruplarını taşır. `multiple` özniteliği çoklu seçime izin verir ve yukarıdaki çıktıdaki
yinelenen adı üretir.

```html
<p><label for="etiket">Etiketler</label>
   <select id="etiket" name="etiket" multiple size="4">
     <optgroup label="Atmosfer">
       <option value="nem" selected>Bağıl nem</option>
       <option value="ruzgar" selected>Rüzgâr</option>
     </optgroup>
     <optgroup label="Yüzey">
       <option value="toprak">Toprak sıcaklığı</option>
     </optgroup>
   </select></p>

<p><label for="not">Açıklama</label>
   <textarea id="not" name="not" rows="4">ilk satır
ikinci satır</textarea></p>
```

Seçeneğin `value` özniteliği yazılmazsa, gönderilen değer seçeneğin metni olur. Görünen
metin değiştiğinde gönderilen değerin de değişmemesi için `value` yazılır.

`output` ögesi bir hesap sonucunu taşır ve gönderime katılmaz; hesabı yapan davranış
katmanıdır.

#### Öneri Listesi

Bir alanın hem serbest metin kabul etmesi hem de hazır seçenekler önermesi gerekebilir.
`datalist` ögesi bu iki davranışı birleştirir: seçenekleri taşır ancak alanı onlarla
sınırlamaz.

```html
<p><label for="sensor">Sensör kodu</label>
   <input id="sensor" name="sensor" type="text" list="sensor-listesi">
   <datalist id="sensor-listesi">
     <option value="NEM-01">
     <option value="SIC-02">
     <option value="RZG-03">
   </datalist></p>
```

Alanın `list` özniteliği listenin kimliğini gösterir. Kullanıcı listedeki bir değeri
seçebilir ya da kendi değerini yazabilir; gönderilen değer her iki durumda da alanın
metnidir. `select` ögesiyle ayrım budur: `select` seçenekleri kısıtlar, `datalist` yalnızca
önerir.

Listenin görünüp görünmediği ve nasıl sunulduğu tarayıcıya bağlıdır. Bu nedenle
`datalist` içeriği bir kısıt olarak değil, bir kolaylık olarak yazılır; gerçekten
kısıtlanması gereken bir alanda `select` ya da bir doğrulama kuralı kullanılır.

#### İstasyon Belgesinde

```html
<fieldset>
  <legend>Ölçüm bilgisi</legend>
  <p><label for="tarih">Ölçüm tarihi</label>
     <input id="tarih" name="tarih" type="date"></p>
  <p><label for="saat">Ölçüm saati</label>
     <input id="saat" name="saat" type="time" step="600"></p>
  <p><label for="deger">Ölçülen değer (°C)</label>
     <input id="deger" name="deger" type="number" step="0.1"></p>
  <input type="hidden" name="istasyon" value="kuzey-yamac">
</fieldset>
```

Saat alanındaki `step="600"` değeri, ölçüm aralığının on dakika olmasının işaretlemedeki
karşılığıdır: saniye cinsinden altı yüz. Gizli alan istasyon kodunu taşır ve kullanıcının
girmesi gerekmez.

#### Özet

- Tür seçimi arayüzü, doğrulamayı ve gönderim biçimini birlikte etkiler.
- Dosya alanı dışında gönderilen her değer dizgidir; tür bilgisi sunucuya taşınmaz ve
  çevirme sunucunun işidir.
- Tarih ve saat değerleri tanımlı bir biçimde gönderilir; kullanıcının gördüğü yerel biçim
  gönderime yansımaz.
- İşaretlenmemiş onay kutusu hiç gönderilmez; çoklu seçim aynı adı birden çok kez üretir.
- `password` ve `hidden` türleri gizlilik sağlamaz; ilki yalnızca ekranda gizler, ikincisi
  kaynak metinde görünür.

#### Sonraki Adım

Alanlar tanımlandı ama her birinin ne istediği yalnızca yanındaki metinden anlaşılıyor. Bu metnin
alana bağlı olduğu belgede bildirilmedikçe, alan adsızdır: fareyle etikete tıklandığında odak
alana gitmez, ekran okuyucu ise alanı "düzenlenebilir metin" diye duyurur. Sonraki ders bu bağı
kurar ve bağın kurulmadığı durumları bir denetimle görünür kılar.

### Etiket–Alan İlişkisi

Source: https://academia.sh/tr/kurslar/web-temelleri-ve-html/etiket-alan-iliskisi

Etiketin alana açık ve örtük bağlanması, kimlik ile ad özniteliklerinin ayrımı, bağın kurulmadığı durumların denetimi ve yer tutucunun sınırı.

Alanlar tanımlandı ama her birinin ne istediği yalnızca yanındaki metinden anlaşılıyor.
Bu metnin alana bağlı olduğu belgede bildirilmedikçe alan **adsızdır**.

Adsız bir alanın üç somut sonucu vardır. Etikete tıklandığında odak alana gitmez —
bu, küçük onay kutularında tıklanabilir alanı birkaç kat büyüten bir kolaylıktır. Ekran
okuyucu alanı yalnızca türüyle duyurur: "düzenlenebilir metin". Ve sesle denetim
kullanıcısı alana adıyla başvuramaz.

#### İki Bağlama Yolu

**Açık bağ**: `label` ögesinin `for` özniteliği, alanın `id` değerini gösterir.

```html
<p><label for="istasyon">İstasyon kodu</label>
   <input id="istasyon" name="istasyon_kodu" type="text"></p>
```

**Örtük bağ**: `label` ögesi alanı sarar; `for` ve `id` gerekmez.

```html
<p><label>Sıcaklık <input name="sicaklik" type="number"></label></p>
```

İki yol da geçerlidir. Açık bağ, etiketin ve alanın belgede ayrı yerlerde bulunmasına izin
verir; bir tablonun başlık hücresindeki etiketin gövde hücresindeki alana bağlanması ancak
böyle yapılabilir. Örtük bağ daha kısadır ve kimlik üretmeyi gerektirmez, ancak sarılan
alanın etiketin içinde kalmasını zorunlu kılar.

Bir alanın birden çok etiketi olabilir; erişilebilir adı tümünün metninden oluşur. Bir
etiketin ise yalnızca bir alanı olabilir.

#### Kimlik ve Ad Ayrı Şeylerdir

İki öznitelik sık karıştırılır ve karışıklık sessiz bir kusur üretir.

`id`, ögenin **belge içindeki** kimliğidir. Belgede tek olmalıdır. Etiket bağı, parça
adresleri ve `aria-describedby` gibi bildirimler bunu kullanır.

`name`, alanın **gönderimdeki** adıdır. Belge içinde tek olması gerekmez — seçenek düğmesi
grupları aynı adı paylaşır. Sunucunun gördüğü ad budur.

İkisi aynı değeri taşıyabilir ama aynı şey değildir. Aşağıdaki betik bir formu tarar ve
her alanın etiket kaynağını bulur.

```js
// etiket.mjs — etiket-alan baginin denetimi
const form = `
<form action="/olcum" method="post">
  <p><label for="istasyon">İstasyon kodu</label>
     <input id="istasyon" name="istasyon_kodu" type="text"></p>
  <p><label>Sıcaklık <input name="sicaklik" type="number"></label></p>
  <p><label for="nem">Bağıl nem</label>
     <input id="bagil-nem" name="nem" type="number"></p>
  <p><input name="not" type="text" placeholder="Açıklama"></p>
  <p><input name="gizli" type="hidden" value="kuzey-yamac"></p>
</form>`;

const acikBaglar = new Map();
for (const e of form.matchAll(/<label for="([^"]+)">([^<]*)<\/label>/g)) {
  acikBaglar.set(e[1], e[2].trim());
}
const ortukBaglar = new Map();
for (const e of form.matchAll(/<label>([^<]*)<input([^>]*)>[^<]*<\/label>/g)) {
  const kimlik = e[2].match(/id="([^"]*)"/)?.[1] ?? null;
  const ad = e[2].match(/name="([^"]*)"/)?.[1] ?? null;
  ortukBaglar.set(kimlik ?? "name:" + ad, e[1].trim());
}

console.log("alan (name)   id            etiket kaynagi   erisilebilir ad");
for (const e of form.matchAll(/<input([^>]*)>/g)) {
  const oz = e[1];
  const ad = oz.match(/name="([^"]*)"/)?.[1] ?? "";
  const kimlik = oz.match(/id="([^"]*)"/)?.[1] ?? "";
  const tur = oz.match(/type="([^"]*)"/)?.[1] ?? "text";
  if (tur === "hidden") {
    console.log(ad.padEnd(14) + kimlik.padEnd(14) + "gerekmez".padEnd(17) + "(gizli alan)");
    continue;
  }
  let kaynak = "YOK";
  let etiketMetni = "";
  if (kimlik && acikBaglar.has(kimlik)) { kaynak = "for/id"; etiketMetni = acikBaglar.get(kimlik); }
  else if (ortukBaglar.has(kimlik) || ortukBaglar.has("name:" + ad)) {
    kaynak = "saran label";
    etiketMetni = ortukBaglar.get(kimlik) ?? ortukBaglar.get("name:" + ad);
  }
  console.log(ad.padEnd(14) + (kimlik || "—").padEnd(14) + kaynak.padEnd(17) + (etiketMetni || "—"));
}

console.log("--- bosa dusen for degerleri ---");
const kimlikler = new Set([...form.matchAll(/id="([^"]*)"/g)].map((e) => e[1]));
for (const hedef of acikBaglar.keys()) {
  if (!kimlikler.has(hedef)) console.log('for="' + hedef + '" -> boyle bir id yok');
}
```

```
alan (name)   id            etiket kaynagi   erisilebilir ad
istasyon_kodu istasyon      for/id           İstasyon kodu
sicaklik      —             saran label      Sıcaklık
nem           bagil-nem     YOK              —
not           —             YOK              —
gizli                       gerekmez         (gizli alan)
--- bosa dusen for degerleri ---
for="nem" -> boyle bir id yok
```

Üçüncü satır, kimlik–ad karışıklığının tipik biçimidir. Etikette `for="nem"` yazılmış ve alanın
`name` değeri de `nem`; ancak `id` değeri `bagil-nem`. Bağ `id` üzerinden kurulduğu için hiç
kurulmamıştır. Belgede hiçbir hata görünmez: etiket metni yerinde durur, alan çalışır, form
gönderilir. Yalnızca ad kaybolmuştur.

Birinci satır tersini gösterir: `id` ve `name` farklı değerler taşıyabilir ve bu bir sorun
değildir. Bağ `id` üzerinden kurulmuş ve gönderimde `name` kullanılmıştır.

Dördüncü satır etiketi hiç yazılmamış bir alandır — yerine `placeholder` konmuştur.

Beşinci satır, gizli alanların etiket gerektirmediğini gösterir: kullanıcıya sunulmazlar.

#### Yer Tutucu Etiket Değildir

`placeholder` özniteliği alanın içinde görünen ve yazmaya başlanınca kaybolan metindir.
Etiket yerine kullanılamaz ve bunun birden çok gerekçesi vardır.

Metin, alan doldurulduğu anda kaybolur; kullanıcı ne istendiğini denetlemek istediğinde
bakacağı bir yer kalmaz. Form uzunsa ya da kullanıcı ara verip döndüyse bu somut bir
kayıptır.

Yer tutucu metin tarayıcı varsayılanlarında düşük karşıtlıkla gösterilir; görme gücü
sınırlı kullanıcılar için okunması güçtür ve karşıtlığı yükseltmek onu girilmiş bir
değerle karıştırılır duruma getirir.

Bazı ekran okuyucu ve tarayıcı birleşimlerinde yer tutucu erişilebilir ad olarak
duyurulur, bazılarında duyurulmaz; davranış tutarlı değildir.

Yer tutucunun yerinde kullanımı, etikete ek olarak bir **biçim örneği** vermektir:
etiket "Ölçüm saati" derken yer tutucu `07:30` yazabilir.

#### Etiket Metni Ne Söylemeli

Etiket, alanın **ne istediğini** söyler; nasıl doldurulacağını değil. "Ad" yeterliyse
"Adınızı buraya yazın" yazılmaz: ekran okuyucu bu metni her odaklanmada yeniden duyurur ve
uzun etiketler formu yorucu kılar.

Etiket metnine yazılan ek işaretler de duyurulur. Zorunlu alanları yıldızla göstermek
yaygındır; yıldızın ne anlama geldiği formun başında açıklanmalı ve zorunluluk ayrıca
alanın kendi özniteliğiyle bildirilmelidir. Görsel işaret bir bildirim değildir.

Birim bilgisi etikette taşınabilir — "Ölçülen değer (°C)" gibi — ya da ek açıklamaya
bırakılabilir. Birim değeri yorumlamak için zorunluysa etikette olması yeğlenir; ekran
okuyucu ek açıklamayı bazı kiplerde atlar, adı hiçbir zaman atlamaz.

Etiketin alanın öncesinde mi sonrasında mı yazılacağı bir sunum kararı gibi görünse de
kaynak sırası okuma sırasını belirler. Onay kutusu ve seçenek düğmelerinde etiket alandan
sonra, diğer alanlarda önce yazılır; bu, denetimin durumunun ne için olduğunun sırayla
duyulmasını sağlar.

#### Ek Açıklama

Alanla ilgili yardım metni, birim bilgisi ya da biçim kuralı `aria-describedby` ile
bağlanır. Etiket adı verir; açıklama ek bilgi verir ve ekran okuyucuda addan sonra
duyurulur.

```html
<p>
  <label for="deger">Ölçülen değer</label>
  <input id="deger" name="deger" type="number" step="0.1"
         aria-describedby="deger-yardim">
  <span id="deger-yardim">Santigrat derece, ondalık ayıracı nokta.</span>
</p>
```

Öznitelik boşlukla ayrılmış birden çok kimlik alabilir; metinler sırayla birleştirilir. Bu,
sonraki derste hata iletisini alana bağlarken kullanılacaktır.

#### Özet

- Etiket alana `for`/`id` ile açık, sarma ile örtük bağlanır; bağ kurulmazsa alan adsız
  kalır ve tıklama, duyurma ve sesle denetim davranışları bozulur.
- `id` belge içindeki kimliktir ve tek olmalıdır; `name` gönderimdeki addır ve
  yinelenebilir. İkisi karıştırıldığında bağ sessizce kurulmaz.
- Bir alanın birden çok etiketi olabilir; bir etiketin yalnızca bir alanı olabilir.
- `placeholder` etiket yerine geçmez: doldurulunca kaybolur, düşük karşıtlıkla gösterilir
  ve ad olarak duyurulması tutarlı değildir.
- Yardım metinleri `aria-describedby` ile bağlanır ve addan sonra duyurulur.

#### Sonraki Adım

Alanlar adlandırıldı; sıra girilen değerin kabul edilip edilmeyeceğine geldi. Tarayıcı,
gönderim öncesinde tanımlı kısıtları sınar ve geçersiz bir alan varsa gönderimi durdurur.
Sonraki ders bu kısıtları, ürettikleri geçerlilik durumlarını ve aynı denetimin sunucuda
neden yeniden yapılması gerektiğini ele alır.

### Yerleşik Doğrulama

Source: https://academia.sh/tr/kurslar/web-temelleri-ve-html/yerlesik-dogrulama

Kısıt özniteliklerinin ürettiği geçerlilik durumları, gönderimin durdurulması ve aynı kuralların sunucuda neden yeniden uygulandığı.

Alanlar adlandırıldı; sıra girilen değerin kabul edilip edilmeyeceğine geldi.

Tarayıcı, gönderim başlamadan önce form içindeki alanları sınar. Bir alan tanımlı bir
kısıtı çiğniyorsa gönderim durdurulur ve kullanıcıya bir ileti gösterilir. Bu düzeneğin
adı **kısıt doğrulaması (constraint validation)** ve tümüyle işaretlemede bildirilir;
betik gerektirmez.

Bu ders iki yanı birlikte ele alır: tarayıcının uyguladığı kuralları ve aynı kuralların
sunucuda neden yeniden uygulanması gerektiğini.

#### Kısıt Öznitelikleri

Kısıtlar alanlara öznitelik olarak yazılır ve her biri belirli türlerde anlamlıdır.

| Öznitelik | Kısıt |
|---|---|
| `required` | Değer boş olamaz |
| `min`, `max` | Sayısal ve tarihsel alanlarda alt ve üst sınır |
| `step` | Değerin hangi adımlarla artabileceği |
| `minlength`, `maxlength` | Metin uzunluğunun alt ve üst sınırı |
| `pattern` | Metnin uyması gereken düzenli ifade |
| `type` | Türün kendi biçim kuralı (`email`, `url`) |

`pattern` değeri bir düzenli ifadedir ve Kabuk Programlama kursundaki Düzenli İfadeler
dersinde tanımlanan kavramları kullanır. Bir ayrım vardır: örüntü değerin **tamamına**
uygulanır; başına ve sonuna çapa yazmak gerekmez, yazılması da bir şey değiştirmez.

#### Geçerlilik Durumları

Bir kısıt çiğnendiğinde alan bir **geçerlilik durumu (validity state)** kazanır. Durumların adları
tanımlıdır ve bir alanda birden çoğu aynı anda doğru olabilir.

```js
// dogrulama.mjs — kisit dogrulama kurallarinin modeli
const ALANLAR = {
  istasyon: { tur: "text", required: true, maxlength: 20, pattern: "[a-z-]+" },
  eposta: { tur: "email", required: true },
  sicaklik: { tur: "number", required: true, min: -60, max: 60, step: 0.1 },
  tarih: { tur: "date", required: true },
};

const EPOSTA = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/;
const TARIH = /^\d{4}-\d{2}-\d{2}$/;

function dogrula(ad, deger) {
  const kural = ALANLAR[ad];
  const durum = [];
  if (kural.required && deger === "") durum.push("valueMissing");
  if (deger === "") return durum;
  if (kural.tur === "email" && !EPOSTA.test(deger)) durum.push("typeMismatch");
  if (kural.tur === "date" && !TARIH.test(deger)) durum.push("typeMismatch");
  if (kural.maxlength !== undefined && [...deger].length > kural.maxlength) durum.push("tooLong");
  if (kural.pattern && !new RegExp("^(?:" + kural.pattern + ")$", "u").test(deger)) {
    durum.push("patternMismatch");
  }
  if (kural.tur === "number") {
    const sayi = Number(deger);
    if (deger.trim() === "" || Number.isNaN(sayi)) return [...durum, "badInput"];
    if (kural.min !== undefined && sayi < kural.min) durum.push("rangeUnderflow");
    if (kural.max !== undefined && sayi > kural.max) durum.push("rangeOverflow");
    if (kural.step) {
      const taban = kural.min ?? 0;
      const adim = Math.round(((sayi - taban) / kural.step) * 1e6) / 1e6;
      if (Math.abs(adim - Math.round(adim)) > 1e-9) durum.push("stepMismatch");
    }
  }
  return durum;
}

const denemeler = [
  ["istasyon", "kuzey-yamac"],
  ["istasyon", ""],
  ["istasyon", "Kuzey Yamac"],
  ["istasyon", "cok-uzun-bir-istasyon-adi"],
  ["eposta", "gozlem@ornek.test"],
  ["eposta", "gozlem(at)ornek"],
  ["sicaklik", "-4.2"],
  ["sicaklik", "-4.25"],
  ["sicaklik", "95"],
  ["sicaklik", "dort"],
  ["tarih", "2024-03-12"],
  ["tarih", "12.03.2024"],
];

for (const [ad, deger] of denemeler) {
  const durum = dogrula(ad, deger);
  console.log(
    ad.padEnd(10) + JSON.stringify(deger).padEnd(28)
    + (durum.length === 0 ? "gecerli" : durum.join(", ")),
  );
}
```

```
istasyon  "kuzey-yamac"               gecerli
istasyon  ""                          valueMissing
istasyon  "Kuzey Yamac"               patternMismatch
istasyon  "cok-uzun-bir-istasyon-adi" tooLong
eposta    "gozlem@ornek.test"         gecerli
eposta    "gozlem(at)ornek"           typeMismatch
sicaklik  "-4.2"                      gecerli
sicaklik  "-4.25"                     stepMismatch
sicaklik  "95"                        rangeOverflow
sicaklik  "dort"                      badInput
tarih     "2024-03-12"                gecerli
tarih     "12.03.2024"                typeMismatch
```

Dört satır açıklama gerektirir.

`stepMismatch`, adım kuralının nasıl işlediğini gösterir. `step="0.1"` bildirimi "ondalık kabul
edilir" demez; değerin bir **taban değerden** başlayarak adım katlarına düşmesini ister. Taban,
`min` yazılmışsa odur — burada `-60` —; yazılmamışsa alanın başlangıç değeri, o da yoksa sıfırdır.
`-4.25` değeri bu ızgaraya düşmez.

`badInput`, diğerlerinden ayrı bir durumdur: alan **bir değer bile üretememiştir**. Bir
sayı alanına sayı olmayan bir metin girildiğinde alanın değeri boş dizgi olur; kullanıcı
bir şey yazmış olsa da alan boş görünür. Bu durum sunucuya hiçbir zaman ulaşmaz.

`tooLong` durumu tarayıcıda seyrek görülür, çünkü `maxlength` yazmayı da engeller —
kullanıcı sınırdan fazlasını giremez. Durum ancak kullanıcı değeri yapıştırdığında ya da
davranış katmanı değeri sonradan yazdığında doğar; `value` özniteliğiyle verilen ve hiç
düzenlenmemiş bir başlangıç değeri bu durumu üretmez. Bu, kısıtın yalnızca gönderim
öncesinde değil, giriş sırasında da işlemesinin bir örneğidir.

Son satır tarih alanının biçim kuralını gösterir: yerel yazım kabul edilmez, alanın
ürettiği değer tanımlı biçimdedir.

#### Gönderimin Durdurulması

Geçersiz bir alan varsa gönderim yapılmaz ve tarayıcı ilk geçersiz alana odaklanıp bir
ileti gösterir. İletinin metni tarayıcının kendi diliyle üretilir ve belgeden
değiştirilemez; biçimi ve süresi de tarayıcıya bağlıdır.

Bu davranışın iki sınırı vardır. İleti tek seferde yalnızca bir alan için görünür; on
alanlık bir formda dokuz hata varsa kullanıcı bunları teker teker keşfeder. İleti kısa
sürede kaybolabilir ve kullanıcı geri dönemez.

`novalidate` özniteliği form üzerine yazıldığında bu davranış kapatılır; gönderim
kısıtlara bakılmadan yapılır. Öznitelik iki durumda anlamlıdır: kısıtları belgede
bildirip denetimi sunucuya bırakmak, ya da hata sunumunu tarayıcıya bırakmadan kendi
denetimini yapmak. İkincisi sonraki dersin konusudur.

#### İstemci Denetimi Bir Güvence Değildir

Tarayıcının uyguladığı her kural **istemcide** uygulanır ve istemci kullanıcının
denetimindedir. Kısıtları kaldırmak için özel bir araç gerekmez: gönderim, belgeyi
kullanmadan da yapılabilir. Girdi Türleri dersinde gösterildiği gibi sunucuya ulaşan şey
yalnızca ad–değer dizgileridir; hangi alandan geldikleri, o alanda hangi kısıtların
yazılı olduğu bilgisi gövdede yoktur.

Aşağıdaki sunucu aynı kısıtları yeniden uygular ve dört farklı gövdeye verdiği yanıtı
yazdırır. Gövdelerin ikisi, belgede yazılı kısıtları çiğneyerek gönderilmiştir.

```js
// sunucu-dogrulama.mjs — ayni kisitlarin sunucuda yeniden uygulanmasi
import { createServer } from "node:http";

const ALANLAR = {
  istasyon: { required: true, maxlength: 20, pattern: "[a-z-]+" },
  sicaklik: { required: true, sayisal: true, min: -60, max: 60, step: 0.1 },
};

function dogrula(ciftler) {
  const hatalar = [];
  for (const [ad, kural] of Object.entries(ALANLAR)) {
    const deger = ciftler.get(ad) ?? "";
    if (kural.required && deger === "") { hatalar.push([ad, "valueMissing"]); continue; }
    if (kural.maxlength && [...deger].length > kural.maxlength) hatalar.push([ad, "tooLong"]);
    if (kural.pattern && !new RegExp("^(?:" + kural.pattern + ")$", "u").test(deger)) {
      hatalar.push([ad, "patternMismatch"]);
    }
    if (kural.sayisal) {
      const sayi = Number(deger);
      if (Number.isNaN(sayi)) { hatalar.push([ad, "badInput"]); continue; }
      if (sayi < kural.min) hatalar.push([ad, "rangeUnderflow"]);
      if (sayi > kural.max) hatalar.push([ad, "rangeOverflow"]);
      const adim = (sayi - kural.min) / kural.step;
      if (Math.abs(adim - Math.round(adim)) > 1e-9) hatalar.push([ad, "stepMismatch"]);
    }
  }
  // Bildirilmemis alanlar: giris listesinde olmamasi gereken adlar.
  for (const ad of new Set(ciftler.keys())) {
    if (!(ad in ALANLAR)) hatalar.push([ad, "bilinmeyenAlan"]);
  }
  return hatalar;
}

const sunucu = createServer((istek, yanit) => {
  const parcalar = [];
  istek.on("data", (parca) => parcalar.push(parca));
  istek.on("end", () => {
    const ciftler = new URLSearchParams(Buffer.concat(parcalar).toString());
    const hatalar = dogrula(ciftler);
    const durum = hatalar.length === 0 ? 201 : 400;
    yanit.writeHead(durum, { "content-type": "text/plain; charset=utf-8" });
    yanit.end(hatalar.map(([ad, kod]) => ad + ": " + kod).join("\n") || "kayit alindi");
  });
});

sunucu.listen(0, "127.0.0.1", async () => {
  const adres = "http://127.0.0.1:" + sunucu.address().port + "/olcum";
  const govdeler = [
    "istasyon=kuzey-yamac&sicaklik=-4.2",
    "istasyon=Kuzey%20Yamac&sicaklik=95",
    "sicaklik=-4.25",
    "istasyon=kuzey-yamac&sicaklik=-4.2&yonetici=evet",
  ];
  for (const govde of govdeler) {
    const yanit = await fetch(adres, {
      method: "POST",
      headers: { "content-type": "application/x-www-form-urlencoded" },
      body: govde,
    });
    console.log("govde :", govde);
    console.log("durum :", yanit.status);
    console.log("yanit :", (await yanit.text()).replace(/\n/g, " | "));
    console.log("---");
  }
  sunucu.close();
});
```

```
govde : istasyon=kuzey-yamac&sicaklik=-4.2
durum : 201
yanit : kayit alindi
---
govde : istasyon=Kuzey%20Yamac&sicaklik=95
durum : 400
yanit : istasyon: patternMismatch | sicaklik: rangeOverflow
---
govde : sicaklik=-4.25
durum : 400
yanit : istasyon: valueMissing | sicaklik: stepMismatch
---
govde : istasyon=kuzey-yamac&sicaklik=-4.2&yonetici=evet
durum : 400
yanit : yonetici: bilinmeyenAlan
---
```

Üçüncü gövde, belgede `required` yazılı bir alanı hiç göndermemiştir; ikinci gövde iki
kısıtı birden çiğnemiştir. Tarayıcı bu gönderimleri engellerdi; sunucuya doğrudan
yapıldıklarında engelleyen tek şey sunucunun kendi denetimidir.

Dördüncü gövde ayrı bir kusur türünü gösterir. Formda bulunmayan bir alan gönderilmiştir
ve sunucu bunu reddeder. Beklenmeyen alanları sessizce yok saymak yerine reddetmek, gövdeyi
doğrudan bir kayda dönüştüren gerçekleştirimlerde ayrıcalık yükseltmeyi önler.

Yanıtın durum kodu da bilgi taşır: HTTP İstek ve Yanıtı dersindeki sınıflandırmaya göre
`400` isteğin kusurlu olduğunu, `201` bir kaynağın oluşturulduğunu bildirir.

#### İki Yerde Tutulan Kural

İstemci ve sunucu denetimleri aynı kuralı iki kez ifade eder ve bu bir yineleme gibi
görünür. Görevleri farklıdır.

İstemci denetimi bir **kolaylıktır**: kullanıcıya gecikmesiz geri bildirim verir ve
sunucuya boşa istek gitmesini önler. Kaldırılabilir olması sorun değildir, çünkü kimseyi
korumak için orada değildir.

Sunucu denetimi bir **kuraldır**: verinin tutarlılığını sağlayan tek yerdir. İstemcide ne
yazıldığından bağımsız olarak uygulanır.

Yinelemeyi azaltmanın yolu, kuralları tek bir tanımdan üretmektir: alan tanımları bir veri
yapısında tutulur ve hem işaretleme hem sunucu denetimi ondan türetilir. Yukarıdaki iki betikte
`ALANLAR` nesnesi bu tanımın karşılığıdır.

#### İstasyon Belgesinde

```html
<fieldset>
  <legend>Ölçüm bilgisi</legend>
  <p><label for="istasyon">İstasyon kodu</label>
     <input id="istasyon" name="istasyon" type="text" value="kuzey-yamac"
            required maxlength="20" pattern="[a-z-]+"
            aria-describedby="istasyon-yardim">
     <span id="istasyon-yardim">Yalnızca küçük harf ve tire.</span></p>

  <p><label for="deger">Ölçülen değer (°C)</label>
     <input id="deger" name="deger" type="number"
            required min="-60" max="60" step="0.1"></p>
</fieldset>
```

Kısıtlar hem kullanıcıya yardım eder hem de belgenin taşıdığı sözleşmeyi bildirir: bu
alanın hangi değerleri kabul ettiği belgede yazılıdır.

#### Özet

- Kısıtlar öznitelik olarak bildirilir ve çiğnendiklerinde adları tanımlı geçerlilik
  durumları üretir; bir alanda birden çok durum aynı anda doğru olabilir.
- `step` kuralı taban değerden başlayan bir ızgara tanımlar; taban `min` yazılmışsa odur.
- `badInput` alanın hiç değer üretemediği durumdur ve sunucuya ulaşmaz; `maxlength`
  yazmayı da engellediği için `tooLong` seyrek doğar.
- Tarayıcı geçersiz alanda gönderimi durdurur; `novalidate` bu davranışı kapatır.
- İstemci doğrulaması bir kolaylıktır ve kaldırılabilir; verinin tutarlılığını sağlayan tek
  yer sunucudur ve aynı kısıtlar orada yeniden uygulanır.

#### Sonraki Adım

Doğrulama, metin ve sayı alanları üzerinde kuruldu. Dosya alanı bu düzenlere uymaz: değeri
bir dizgi değildir, gövdeye sığdırılması ayrı bir kodlama gerektirir ve boyut sınırı
belgede bildirilemez. Sonraki ders dosya yüklemeyi ele alır ve iki gönderim kodlamasının
gövdedeki farkını gerçek baytlar üzerinden gösterir.

### Dosya Yükleme

Source: https://academia.sh/tr/kurslar/web-temelleri-ve-html/dosya-yukleme

Dosya alanının ürettiği değer, çok parçalı gönderim kodlamasının gövdedeki karşılığı, kabul türü bildirimi ve boyut sınırının nerede uygulandığı.

Doğrulama, metin ve sayı alanları üzerinde kuruldu. Dosya alanı bu düzenlere uymaz. Değeri bir
dizgi değildir; ikilik içerik taşır ve kendi adı ile kendi içerik türü vardır.

Form Yapısı dersinde tanımlanan varsayılan kodlama —`ad=deger` çiftlerinin `&` ile
birleştirilmesi— bu içeriği taşıyamaz. Dosya alanı olan bir formda gönderim kodlaması
değiştirilir.

#### Üç Öznitelik

```html
<form action="/olcum/yukle" method="post" enctype="multipart/form-data">
  <p><label for="kayit">Ham kayıt dosyası</label>
     <input id="kayit" name="kayit" type="file" accept=".csv,text/csv" required></p>
  <p><button type="submit">Yükle</button></p>
</form>
```

`enctype="multipart/form-data"` gönderim kodlamasını değiştirir. Yazılmadığında dosya
alanı yalnızca **dosya adını** gönderir; içerik gitmez. Bu, sessizce yanlış çalışan bir
formun en sık nedenidir: form gönderilir, sunucu bir değer alır, ama aldığı şey bir
dizgidir.

`accept` özniteliği, dosya seçicide hangi türlerin öne çıkarılacağını bildirir. Değeri
uzantı listesi, içerik türü listesi ya da ikisinin karışımı olabilir.

`multiple` özniteliği birden çok dosya seçilmesine izin verir; her dosya giriş listesine
aynı adla ayrı bir giriş olarak katılır.

#### Gövdenin Biçimi

Aşağıdaki betik bir sunucu açar, aynı iki alanı iki farklı kodlamayla gönderir ve
sunucunun aldığı ham gövdeyi yazdırır. Satır sonları görünür olsun diye `\r\n` dizisi
açıkça yazdırılmıştır.

```js
// dosya.mjs — iki gonderim kodlamasinin govdedeki karsiligi
import { createServer } from "node:http";

// Sinir dizgisi her gonderimde rastgele uretilir; ciktinin yinelenebilir olmasi icin
// yazdirmadan once sabit bir yer tutucuyla degistirilir.
const sunucu = createServer((istek, yanit) => {
  const parcalar = [];
  istek.on("data", (parca) => parcalar.push(parca));
  istek.on("end", () => {
    const govde = Buffer.concat(parcalar);
    const tur = istek.headers["content-type"];
    const sinir = tur.match(/boundary=(.+)$/)?.[1];
    const maskele = (d) => (sinir ? d.split(sinir).join("SINIR") : d);
    console.log("content-type  :", maskele(tur));
    console.log("govde uzunlugu:", govde.length, "bayt");
    console.log("--- ham govde ---");
    console.log(maskele(govde.toString()).replace(/\r\n/g, "\\r\\n\n"));
    console.log("===");
    yanit.writeHead(204).end();
  });
});

sunucu.listen(0, "127.0.0.1", async () => {
  const adres = "http://127.0.0.1:" + sunucu.address().port + "/yukle";

  // enctype="multipart/form-data"
  const veri = new FormData();
  veri.append("istasyon", "kuzey-yamac");
  veri.append("kayit", new Blob(["saat,sicaklik\n07:00,-4.2\n"], { type: "text/csv" }), "olcum.csv");
  await fetch(adres, { method: "POST", body: veri });

  // enctype="application/x-www-form-urlencoded" — ayni iki alan, dosya icerigi olmadan
  await fetch(adres, {
    method: "POST",
    headers: { "content-type": "application/x-www-form-urlencoded" },
    body: new URLSearchParams([["istasyon", "kuzey-yamac"], ["kayit", "olcum.csv"]]).toString(),
  });

  sunucu.close();
});
```

```
content-type  : multipart/form-data; boundary=SINIR
govde uzunlugu: 295 bayt
--- ham govde ---
--SINIR\r\n
Content-Disposition: form-data; name="istasyon"\r\n
\r\n
kuzey-yamac\r\n
--SINIR\r\n
Content-Disposition: form-data; name="kayit"; filename="olcum.csv"\r\n
Content-Type: text/csv\r\n
\r\n
saat,sicaklik
07:00,-4.2
\r\n
--SINIR--\r\n

===
content-type  : application/x-www-form-urlencoded
govde uzunlugu: 36 bayt
--- ham govde ---
istasyon=kuzey-yamac&kayit=olcum.csv
===
```

Çıktıdaki `SINIR` bir yer tutucudur: gerçek sınır dizgisi her gönderimde rastgele
üretilir ve betik onu yazdırmadan önce sabit bir metinle değiştirir. Sınırın metni
gönderen tarafça seçilir; tek koşulu, gövdenin hiçbir yerinde geçmemesidir. Gövde
uzunluğu bu betikte değişmez, çünkü üretilen sınırın uzunluğu sabittir.

Gövde yapısı şöyledir. `Content-Type` başlığında bir **sınır (boundary)** dizgisi
bildirilir. Gövde bu sınırla parçalara ayrılır; her parça kendi başlıklarını, bir boş
satırı ve içeriğini taşır. `Content-Disposition` başlığı alanın adını verir; dosya
alanlarında ayrıca `filename` parametresi bulunur. Dosya parçasında bir de `Content-Type`
başlığı vardır ve dosyanın türünü bildirir. Gövde, sınırın sonuna iki tire eklenmiş
biçimiyle biter.

İki temel fark görünür.

**İçerik kaçırılmaz.** Çok parçalı kodlamada dosya içeriği olduğu gibi taşınır; yüzde
kodlaması uygulanmaz. Ayırma işini sınır dizgisi yapar. İkilik bir dosya, kodlanmadan
gönderilebilir.

**Yük oranı yüksektir.** Aynı iki alan için 36 bayta karşılık 295 bayt kullanılmıştır. Her
parça kendi başlıklarını taşır ve sınır dizgisi her parçada yinelenir. Bu maliyet dosya
büyüdükçe oransal olarak küçülür; ancak dosya alanı olmayan bir formu çok parçalı
kodlamayla göndermek gereksiz bir yüktür.

Çıktının ikinci bölümü, yanlış kodlamanın sonucunu gösterir: `kayit` alanı yalnızca dosya
adını taşır. Sunucu bir değer alır ve içerik yoktur.

#### Kabul Türü Bir Kısıt Değildir

`accept` özniteliği dosya seçici penceresini süzer. Süzgeç kullanıcı tarafından
kaldırılabilir ve gönderim belge kullanılmadan da yapılabilir; Yerleşik Doğrulama dersindeki
sonuç burada da geçerlidir.

Dosyanın gerçekten bildirdiği türde olup olmadığı, gövdedeki `Content-Type` başlığından
anlaşılmaz: o başlığı da gönderen taraf yazar. Sunucu türü ancak dosyanın **içeriğine**
bakarak doğrulayabilir — dosya biçimlerinin baş baytlarındaki tanımlayıcı diziler bu iş
içindir.

Uzantıya güvenmek de yeterli değildir. Yüklenen dosya adı sunucuda doğrudan kullanılmaz:
kullanıcıdan gelen ad yol ayıracı içerebilir, var olan bir dosyanın üzerine yazabilir ya
da sunucunun çalıştırdığı bir uzantı taşıyabilir. Güvenli davranış, sunucunun kendi
ürettiği bir ad kullanması ve özgün adı yalnızca üstveri olarak saklamasıdır.

#### Alanın Değeri Belgeden Yazılamaz

Dosya alanı, diğer alanlardan bir yönüyle daha ayrılır: değeri belgeden verilemez.
`value` özniteliği yazılsa da yok sayılır, çünkü aksi hâlde bir belge, kullanıcının
diskindeki herhangi bir dosyayı sessizce gönderebilirdi. Alanın değeri yalnızca
kullanıcının seçimiyle oluşur.

Aynı gerekçe dosyanın **yolunu** da gizler. Belgeye açılan bilgi dosyanın adı, boyutu ve
türüdür; bulunduğu dizin değildir. Kullanıcının dizin yapısı, o kullanıcı hakkında bilgi
taşır ve belgeye verilmez.

Bunun bir sonucu, bir formun önceki gönderimden kalan dosyayı yeniden göstermesinin
olanaksız olmasıdır. Doğrulama hatası nedeniyle geri dönülen bir formda diğer alanların
değerleri belgede yeniden yazılabilir; dosya alanı boş kalır ve kullanıcının dosyayı
yeniden seçmesi gerekir. Çok adımlı formlarda bu, dosyanın erken bir adımda yüklenip
sunucuda geçici olarak saklanmasını ve sonraki adımlarda bir kimlikle anılmasını
gerektirir.

#### Boyut Sınırı Belgede Bildirilemez

Dosya boyutunu sınırlayan bir öznitelik yoktur. Bunun bir eksiklik değil, tutarlı bir
tasarım olduğu söylenebilir: belgede bildirilen bir sınır zaten uygulanamazdı.

Sınır iki yerde uygulanır. Sunucu, gövdenin tamamını okumadan `Content-Length` başlığına
bakarak isteği reddedebilir ve okurken de bir üst sınır uygulayabilir; bu, belleğin bir
istekle doldurulmasını önler. Sunucu yazılımının kendi yapılandırması da genellikle bir
istek gövdesi üst sınırı taşır.

Kullanıcıya erken geri bildirim vermek isteniyorsa dosya boyutu gönderim öncesinde
davranış katmanında okunabilir; bu bir kolaylıktır, kısıt değildir. Beklenen sınır belgede
metin olarak yazılır — kullanıcı, seçtiği dosyanın reddedileceğini yükleme bittikten sonra
öğrenmemelidir.

#### İstasyon Belgesinde

```html
<fieldset>
  <legend>Ham kayıt</legend>
  <p><label for="kayit">Ölçüm dosyası</label>
     <input id="kayit" name="kayit" type="file"
            accept=".csv,text/csv" aria-describedby="kayit-yardim">
     <span id="kayit-yardim">Virgülle ayrılmış değer dosyası, en çok 5 MB.</span></p>
</fieldset>
```

Formun `enctype` özniteliği bu alan eklendiği anda değiştirilmelidir; unutulduğunda form
çalışıyor görünür ve dosya içeriği hiç ulaşmaz.

#### Özet

- Dosya alanı olan formda `enctype="multipart/form-data"` yazılır; yazılmadığında yalnızca
  dosya adı gönderilir ve içerik kaybolur.
- Çok parçalı gövde bir sınır dizgisiyle parçalara ayrılır; her parça kendi başlıklarını
  taşır ve içerik kaçırılmadan aktarılır.
- Bu kodlamanın yük oranı yüksektir; dosya alanı olmayan formlarda kullanılmaz.
- `accept` yalnızca dosya seçiciyi süzer; tür denetimi sunucuda, dosyanın içeriğine
  bakılarak yapılır ve yüklenen ad doğrudan kullanılmaz.
- Boyut sınırı belgede bildirilemez; sunucuda uygulanır ve kullanıcıya metinle bildirilir.

#### Sonraki Adım

Form bu noktada veri topluyor, doğruluyor ve dosya alabiliyor. Geriye bir soru kalıyor:
gönderim reddedildiğinde kullanıcı ne görüyor? Tarayıcının kendi iletisi tek alanı
gösteriyor, sunucudan dönen hata ise yeni bir belgeyle geliyor. Son ders hata bildirimini
ve odak yönetimini ele alır ve kursu, bu formun erişilebilir sürümünü kurarak kapatır.

### Form Erişilebilirliği

Source: https://academia.sh/tr/kurslar/web-temelleri-ve-html/form-erisilebilirligi

Hata iletilerinin alana bağlanması, sunucudan dönen hata özetinin kurulması, odak yönetimi ve klavyeyle kullanımın gerekleri.

Form bu noktada veri topluyor, doğruluyor ve dosya alabiliyor. Geriye bir soru kalıyor:
gönderim reddedildiğinde kullanıcı ne görüyor?

Yerleşik Doğrulama dersinde tarayıcının kendi iletisinin iki sınırı belirtilmişti: tek
seferde tek alan gösterir ve kısa sürede kaybolabilir. Sunucudan dönen hata ise yeni bir
belgeyle gelir ve kullanıcı, hangi alanların düzeltilmesi gerektiğini bulmak zorundadır.

Bu ders hata bildirimini belgede kurar. Kurulacak yapı betik gerektirmez; sunucu tarafından
üretilen işaretlemedir ve bu nedenle her koşulda çalışır.

#### Bir Hata Bildiriminin Karşılaması Gerekenler

Dört gereklilik vardır ve dördü de işaretlemeyle karşılanır.

**Bulunabilirlik.** Kullanıcı, sayfanın neresine döndüğünü ve kaç hata olduğunu görmelidir.
Sayfanın ortasında tek bir kırmızı satır, o satıra bakmayan kullanıcıya hiçbir şey söylemez.

**Bağlılık.** Her ileti, ait olduğu alana bildirimle bağlanmalıdır. Alanın altına
yerleştirilmiş bir metin görsel olarak bağlıdır; erişilebilirlik ağacında bağlı değildir.

**Duyurulabilirlik.** Alana odaklanıldığında hata iletisi de duyurulmalıdır.

**Erişilebilirlik.** İletiye ulaşmak, hatanın hangi alanda olduğunu tahmin etmeyi
gerektirmemelidir.

Rengin tek başına bir bildirim olmadığı da eklenmelidir: kırmızı bir kenarlık, rengi
ayırt edemeyen kullanıcıya bir şey söylemez. Durum metinle de bildirilir.

#### Sunucudan Dönen Hata Özeti

Aşağıdaki betik, sunucunun doğrulama sonrasında ürettiği belgeyi kurar. Girdi, alan
tanımları ve hata listesidir; çıktı, gönderilecek işaretlemedir.

```js
// erisilebilirlik.mjs — sunucuda uretilen hata ozeti ve alan-hata baglari
const ALANLAR = [
  { kimlik: "istasyon", ad: "istasyon", etiket: "İstasyon kodu", tur: "text" },
  { kimlik: "sicaklik", ad: "sicaklik", etiket: "Sıcaklık (°C)", tur: "number" },
  { kimlik: "eposta", ad: "eposta", etiket: "Bildirim adresi", tur: "email" },
];

const ILETI = {
  valueMissing: "Bu alan zorunludur.",
  patternMismatch: "Yalnızca küçük harf ve tire kullanın.",
  rangeOverflow: "Değer en çok 60 olabilir.",
  typeMismatch: "Geçerli bir e-posta adresi yazın.",
};

function kac(metin) {
  return metin.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;");
}

function formYaz(degerler, hatalar) {
  const satirlar = [];
  if (hatalar.length > 0) {
    satirlar.push('<div role="alert" tabindex="-1" id="hata-ozeti">');
    satirlar.push("  <h2>Form gönderilemedi: " + hatalar.length + " alan düzeltilmeli</h2>");
    satirlar.push("  <ul>");
    for (const [alanKimligi, kod] of hatalar) {
      const alan = ALANLAR.find((a) => a.kimlik === alanKimligi);
      satirlar.push(
        '    <li><a href="#' + alan.kimlik + '">' + kac(alan.etiket) + ": " + kac(ILETI[kod]) + "</a></li>",
      );
    }
    satirlar.push("  </ul>");
    satirlar.push("</div>");
  }
  satirlar.push('<form action="/olcum" method="post" novalidate>');
  for (const alan of ALANLAR) {
    const hata = hatalar.find(([kimlik]) => kimlik === alan.kimlik);
    satirlar.push('  <p><label for="' + alan.kimlik + '">' + kac(alan.etiket) + "</label>");
    const ozellikler = [
      'id="' + alan.kimlik + '"',
      'name="' + alan.ad + '"',
      'type="' + alan.tur + '"',
      'value="' + kac(degerler[alan.ad] ?? "") + '"',
    ];
    if (hata) {
      ozellikler.push('aria-invalid="true"');
      ozellikler.push('aria-describedby="' + alan.kimlik + '-hata"');
    }
    satirlar.push("     <input " + ozellikler.join(" ") + ">");
    if (hata) {
      satirlar.push(
        '     <span id="' + alan.kimlik + '-hata" class="hata">' + kac(ILETI[hata[1]]) + "</span>",
      );
    }
    satirlar.push("  </p>");
  }
  satirlar.push('  <p><button type="submit">Kaydı gönder</button></p>');
  satirlar.push("</form>");
  return satirlar.join("\n");
}

const degerler = { istasyon: "Kuzey Yamac", sicaklik: "95", eposta: "gozlem@ornek.test" };
const hatalar = [["istasyon", "patternMismatch"], ["sicaklik", "rangeOverflow"]];
console.log(formYaz(degerler, hatalar));
```

```
<div role="alert" tabindex="-1" id="hata-ozeti">
  <h2>Form gönderilemedi: 2 alan düzeltilmeli</h2>
  <ul>
    <li><a href="#istasyon">İstasyon kodu: Yalnızca küçük harf ve tire kullanın.</a></li>
    <li><a href="#sicaklik">Sıcaklık (°C): Değer en çok 60 olabilir.</a></li>
  </ul>
</div>
<form action="/olcum" method="post" novalidate>
  <p><label for="istasyon">İstasyon kodu</label>
     <input id="istasyon" name="istasyon" type="text" value="Kuzey Yamac" aria-invalid="true" aria-describedby="istasyon-hata">
     <span id="istasyon-hata" class="hata">Yalnızca küçük harf ve tire kullanın.</span>
  </p>
  <p><label for="sicaklik">Sıcaklık (°C)</label>
     <input id="sicaklik" name="sicaklik" type="number" value="95" aria-invalid="true" aria-describedby="sicaklik-hata">
     <span id="sicaklik-hata" class="hata">Değer en çok 60 olabilir.</span>
  </p>
  <p><label for="eposta">Bildirim adresi</label>
     <input id="eposta" name="eposta" type="email" value="gozlem@ornek.test">
  </p>
  <p><button type="submit">Kaydı gönder</button></p>
</form>
```

Üretilen işaretleme, dört gerekliliğin her birini karşılar.

**Özet, formdan önce durur** ve kaç alanın düzeltileceğini bir başlıkla söyler. Belgenin
başında bulunması, kullanıcının ona ek bir gezinme yapmadan varmasını sağlar; `role="alert"`
bildirimi ise bölümü ivedi duyurulacak bir bölge olarak işaretler. Belge yüklenirken
duyurulup duyurulmayacağı gerçekleştirime bağlıdır, bu nedenle özet yalnızca duyuruya
dayanmaz: konumu ve başlığıyla da bulunabilir.

**Her hata bir bağlantıdır** ve hedefi ilgili alanın kimliğidir. Bağlantı metni yalnızca
"bu alan zorunludur" demez; hangi alan olduğunu da söyler — Bağlantılar dersindeki
"bağlamdan koparıldığında anlaşılır olma" ölçütü burada da geçerlidir.

**Alanlar `aria-invalid="true"` taşır.** Bu, erişilebilirlik ağacına geçersizlik
bildirimidir ve rengin taşımadığı bilgiyi taşır.

**İleti alana `aria-describedby` ile bağlıdır.** Etiket–Alan İlişkisi dersinde kurulan
düzenek burada hata iletisi için kullanılır: alana odaklanıldığında ad, sonra tür, sonra
açıklama duyurulur.

Girilen değerlerin **korunduğuna** dikkat edilmelidir. Geçersiz değerler bile alanlara geri
yazılır; kullanıcı ne yazdığını görür ve düzeltir. Formu boşaltarak geri döndürmek, uzun
formlarda kullanıcının çalışmasını yok eder.

Kaçırma işlevi de gereklidir: alan değerleri kullanıcıdan gelir ve Etiketler, Öznitelikler
ve Varlıklar dersinde belirtildiği gibi `<`, `&` ve tırnak karakterleri kaçırılmadan
belgeye yazılırsa yapı bozulur.

#### Odak Yönetimi

Özetin `tabindex="-1"` özniteliği taşıması, ona program yoluyla odaklanılabilmesi içindir.
Değer eksi bir olduğunda öge sekme sırasına girmez ama odak alabilir.

Sunucudan dönen bir belgede odak, belgenin başındadır ve özet zaten oradadır; bu durumda
ek bir işlem gerekmez. Sayfa yenilenmeden yapılan denetimlerde odağın özete taşınması
gerekir, yoksa kullanıcı yeni eklenen bölümden habersiz kalır.

Özetteki bağlantıya tıklandığında odak hedef alana gider. Bunun için alanın odaklanabilir
olması yeterlidir; girdi alanları zaten odaklanabilirdir.

`tabindex` özniteliğinin sıfırdan büyük değerleri kullanılmaz. Bu değerler ögeyi sekme
sırasının başına alır ve belgenin geri kalanının sırasını bozar; sıra, belgedeki kaynak
sırasından gelmelidir. Odak sırası ile görsel sıra ayrıştığında klavye kullanıcısı ekranda
sıçrar.

#### Sayfa Yenilenmeden Bildirim

Denetim sayfa yenilenmeden yapılıyorsa, ekranda beliren bir iletinin duyurulması gerekir.
Bir **canlı bölge (live region)**, içeriği değiştiğinde ekran okuyucunun duyurduğu
bölümdür.

```html
<div role="alert" id="hata-ozeti" tabindex="-1"></div>
```

`role="alert"` bildirimi ivedi duyuru ister ve kullanıcının o anki okumasını böler; hata
bildirimleri için uygundur. Daha az ivedi bilgiler —"kayıt gönderiliyor" gibi— için
kesintisiz duyuru yapan bir canlı bölge kullanılır.

Canlı bölgenin belgede **önceden** bulunması gerekir. Belgeye sonradan eklenen bir canlı
bölgenin içeriği bazı gerçekleştirimlerde duyurulmaz; boş bir kap önceden konur ve içeriği
sonradan doldurulur.

#### Klavyeyle Kullanım

Bir formun klavyeyle kullanılabilmesi, belgede hiçbir şey yapılmadığı sürece kendiliğinden
sağlanır: `input`, `select`, `textarea` ve `button` ögeleri odaklanabilirdir, sekme sırası
kaynak sırasıdır ve gönderim, alanlardan birindeyken giriş tuşuyla yapılabilir.

Bu davranışın bozulduğu yerler, dilin ögelerinin yerine başkalarının konduğu yerlerdir.
Bir `div` ögesine tıklama davranışı eklenerek yapılmış bir düğme odaklanabilir değildir,
klavyeyle basılamaz ve ekran okuyucuda düğme olarak duyurulmaz. Bunları geri kazanmak için
gereken bildirimler —odaklanabilirlik, rol, tuş işleme— `button` ögesinde zaten vardır.

Odak göstergesi de kaldırılmaz. Odaklanılan ögenin görünür bir işareti olmadığında, klavye
kullanıcısı belgenin neresinde olduğunu bilemez. Gösterge sunum katmanında
değiştirilebilir; kaldırılamaz.

#### Bitmiş Form

Kurs boyunca geliştirilen belgenin son bölümü:

```html
<section aria-labelledby="bildirim">
  <h2 id="bildirim">Ölçüm Düzeltmesi Bildir</h2>

  <div role="alert" id="hata-ozeti" tabindex="-1"></div>

  <form action="/olcum/duzeltme" method="post" enctype="multipart/form-data">
    <fieldset>
      <legend>Ölçüm bilgisi</legend>

      <p><label for="istasyon">İstasyon kodu</label>
         <input id="istasyon" name="istasyon" type="text" value="kuzey-yamac"
                required maxlength="20" pattern="[a-z-]+"
                aria-describedby="istasyon-yardim">
         <span id="istasyon-yardim">Yalnızca küçük harf ve tire.</span></p>

      <p><label for="tarih">Ölçüm tarihi</label>
         <input id="tarih" name="tarih" type="date" required></p>

      <p><label for="deger">Ölçülen değer (°C)</label>
         <input id="deger" name="deger" type="number"
                required min="-60" max="60" step="0.1"></p>
    </fieldset>

    <fieldset>
      <legend>Ölçüm kaynağı</legend>
      <p><label><input type="radio" name="kaynak" value="otomatik" checked> Otomatik</label></p>
      <p><label><input type="radio" name="kaynak" value="elle"> Elle girildi</label></p>
    </fieldset>

    <fieldset>
      <legend>Ham kayıt</legend>
      <p><label for="kayit">Ölçüm dosyası</label>
         <input id="kayit" name="kayit" type="file"
                accept=".csv,text/csv" aria-describedby="kayit-yardim">
         <span id="kayit-yardim">Virgülle ayrılmış değer dosyası, en çok 5 MB.</span></p>
    </fieldset>

    <p><button type="submit">Düzeltmeyi gönder</button></p>
  </form>
</section>
```

Her alanın bir etiketi, her grubun bir adı, her kısıtın bir bildirimi ve her açıklamanın
bir bağı vardır. Belgede tek bir betik yoktur.

#### Özet

- Bir hata bildirimi bulunabilir, alana bağlı, duyurulabilir ve ulaşılabilir olmalıdır;
  renk tek başına bildirim değildir.
- Sunucudan dönen hata özeti formdan önce durur, hata sayısını söyler ve her hatayı ilgili
  alana bağlanan bir bağlantı olarak sunar.
- Alanlar `aria-invalid` ile geçersizlik durumunu, `aria-describedby` ile hata iletisini
  bildirir; girilen değerler korunur ve kaçırılarak yazılır.
- `tabindex="-1"` ögeyi sekme sırasına sokmadan odaklanabilir yapar; sıfırdan büyük
  değerler belgenin odak sırasını bozar.
- Canlı bölgeler belgede önceden bulunmalıdır; sonradan eklenen bir bölgenin içeriği
  duyurulmayabilir.
- Klavyeyle kullanım, dilin kendi ögeleri kullanıldığı sürece kendiliğinden sağlanır;
  odak göstergesi değiştirilebilir ama kaldırılamaz.

#### Kurs Kapanışı

Bu kurs tek bir soruyla başladı: sunucudan gelen yanıt gövdesi nedir? Yanıt bir belgedir ve
belge, baytlardan bir düğüm ağacına çevrilir.

Dört konu bu ağacı kurdu. **Web Nasıl Çalışır**, baytların karaktere, karakterlerin
sözcük birimine, birimlerin ağaca dönüşümünü izledi; ayrıştırıcının kurtarma kurallarını
ve kaynak yüklemenin bu zinciri nasıl beklettiğini gösterdi. **Belge Yapısı**, kök yapıdan tablolara
kadar belgenin iskeletini kurdu ve her etiketin bir yapı bildirimi olduğunu, görünüm
kararı olmadığını yineledi. **Anlamsal İşaretleme ve Medya**, belgeye bir üst düzey harita
ekledi ve görsel, ses, video ile gömülü içeriği erişilebilir biçimde yerleştirdi.
**Formlar**, tek yönlü belgeyi iki yönlü kıldı ve kullanıcıdan gelen verinin sunucuya nasıl
ulaştığını gerçek gövdeler üzerinden gösterdi.

Kurs boyunca izlenen ölçüm istasyonu belgesi, iki satırlık bir iskeletten bölümlenmiş,
çizelgeli, medya taşıyan ve doğrulanabilir bir form içeren bütün bir sayfaya dönüştü.
Belgede tek bir sunum kararı yoktur: hiçbir yerde bir renk, bir ölçü, bir konum
bildirilmemiştir.

Bu eksiklik bilinçlidir. İlk derste tanımlanan üç katmandan yalnızca birincisi kuruldu.
Belge şu anda bir tarayıcıda açıldığında okunabilir ve gezilebilir — çünkü tarayıcının
her öge için taşıdığı varsayılan biçemler vardır. Ancak o görünüm belgenin değil,
tarayıcının kararıdır.

Sonraki kurs, **CSS ile Görsel Sunum**, ikinci katmanı kurar: hangi ögeye hangi kuralın
uygulanacağını belirleyen seçicileri, çakışan kuralların nasıl çözüldüğünü, kutu modelini
ve akış düzenini. O kursun üzerine çalışacağı belge budur — ve iyi kurulmuş bir yapı, iyi
bir sunumun ön koşuludur: bir ögeye biçem vermek, önce o ögenin var olmasını gerektirir.

