HTML'de en sık yapılan hatalar çoğunlukla bir öğeyi izin verilmeyen bir yere koymaktan doğar: paragrafın içine <div>, bağlantının içine düğme, <ul> içine doğrudan <div>, formun içine başka bir form. Bunların yanında aynı id değerini iki kez kullanmak, görsellerde alt, form alanlarında <label> unutmak ve başlıkları yazı boyutu için seçmek de listenin başındadır. Tarayıcılar bu hataların çoğunu sessizce "onarır", bu yüzden sayfa düzgün görünür; ama ortaya çıkan yapı sizin yazdığınız yapı değildir ve ekran okuyucu, betik ya da stil kuralı beklenmedik davranır.

Bu yazıda önce iç içe kullanım kurallarının dayandığı içerik kategorilerini, ardından bu kuralları bozan tipik hataları ve doğru karşılıklarını anlatıyoruz. Bir öğenin MDN sayfasındaki teknik özetin nasıl okunacağı HTML öğrenme rehberinde, belge iskeleti belge yapısı yazısında anlatılıyor.

İçerik kategorileri ve içerik modeli

İçerik kategorileri ve içerik modeli: Meta veri (metadata), Akış (flow), Bölümleme (sectioning), Başlık (heading)

MDN'ye göre HTML öğelerinin çoğu bir veya birden fazla içerik kategorisine üyedir. Bu kategoriler kendi aralarında bir ilişki kurmaz, ama ortak davranışları ve kuralları tanımlamaya yarar. En önemli işlevleri bir öğenin içerik modelini, yani içine hangi öğelerin girebileceğini tanımlamaktır: <p> yalnızca akış içi (phrasing) içerik alabilir, <div> ise akış (flow) içeriği alır. Yedi ana kategori vardır:

KategoriNe anlatırÖrnek öğeler
Meta veri (metadata)Belgenin sunumunu veya davranışını etkiler, başka belgelere bağlarbase, link, meta, script, style, title
Akış (flow)Gövdede bulunabilen öğelerin çoğudiv, p, ul, table, form, a
Bölümleme (sectioning)Belgede bir bölüm oluştururarticle, aside, nav, section
Başlık (heading)Bir bölümün başlığını tanımlarh1 ile h6, hgroup
Akış içi (phrasing)Metin ve metin düzeyindeki işaretleme; paragrafları oluştururspan, strong, a, img, input, button, label
Gömülü (embedded)Başka bir kaynağı veya dili belgeye katarimg, iframe, video, audio, canvas, svg
Etkileşimli (interactive)Kullanıcı etkileşimi için tasarlanmışbutton, details, label, select, textarea, href taşıyan a

Bazı öğeler bir kategoriye yalnızca koşullu girer. MDN'nin listesine göre <a> öğesi yalnızca akış içi içerik taşıyorsa akış içi içerik sayılır; href varsa etkileşimli içeriktir. <audio> ve <video> controls varsa, <img> usemap varsa etkileşimlidir; <input> ise type="hidden" dışındaki her türde etkileşimlidir. Bir de hiçbir kategoriye girmeyen öğeler vardır: <li>, <dt>, <dd>, <tr>, <td>, <th>, <thead>, <tbody>, <option>, <legend>, <caption>, <figcaption>, <source> ve <track> gibi. Bunlar yalnızca belirli bir ebeveynin içinde anlamlıdır ve teknik özetlerindeki "izin verilen ebeveynler" satırı nereye konabileceklerini söyler.

Son kavram "saydam" (transparent) içerik modelidir. <a>, <ins> ve <del> gibi öğelerin izin verilen içeriği, ebeveynlerinin içerik modeline göre belirlenir. Örneğin akış içeriği kabul eden bir <div> içindeki <a>, içine bir <p> alabilir; ama bir <p> içindeki <a> yalnızca akış içi içerik alabilir, çünkü ebeveyn <p> yalnızca bunu kabul eder.

Hata 1: Paragrafın içine blok öğe koymak

<p> öğesinin izin verilen içeriği yalnızca akış içi içeriktir. Bu yüzden <div>, <ul>, <table>, başlıklar veya başka bir <p> bir paragrafın içine giremez. Burada bir de etiket atlama (tag omission) kuralı devreye girer: MDN'nin teknik özetine göre <p> öğesinin bitiş etiketi, ardından <address>, <article>, <aside>, <blockquote>, <details>, <div>, <dl>, <fieldset>, <figcaption>, <figure>, <footer>, <form>, başlık öğeleri, <header>, <hgroup>, <hr>, <main>, <menu>, <nav>, <ol>, <pre>, <search>, <section>, <table>, <ul> ya da başka bir <p> geldiğinde atlanabilir. Yani paragrafın içine yazdığınızı sandığınız <div> başladığında paragraf kapanmış sayılır ve <div> paragrafın dışında, onun kardeşi olarak kalır.

html
<!-- Hatali: <p>Fiyatlar: <ul><li>Aylik</li></ul></p>
     ul baslayinca p kapanmis sayilir; ul paragrafin icinde kalmaz. -->

<!-- Dogru: kapsayici akis icerigi alan bir div, metin ve liste kardes -->
<div class="fiyatlar">
  <p>Fiyatlar:</p>
  <ul>
    <li>Aylık</li>
    <li>Yıllık</li>
  </ul>
</div>

Bir yan not: MDN, paragraflar arasında boşluk yaratmak için boş <p> öğeleri kullanmanın ekran okuyucu kullanıcıları için sorunlu olduğunu belirtir; ekran okuyucu paragrafın varlığını duyurabilir ama içinde okunacak bir şey yoktur. Boşluk CSS ile verilir.

Hata 2: Etkileşimli öğenin içine etkileşimli öğe

MDN'nin teknik özetleri bu konuda açıktır. <a> öğesinin içeriği saydamdır, ancak hiçbir alt öğesi etkileşimli içerik veya başka bir <a> olamaz ve hiçbir alt öğede tabindex belirtilemez. <button> öğesi akış içi içerik alır ama içinde etkileşimli içerik olamaz. <label> de akış içi içerik alır, ancak içinde başka bir <label> bulunamaz ve etiketlediği denetim dışında başka bir etiketlenebilir öğe yer alamaz. Tipik hata, tamamı tıklanabilir bir "kart" bağlantısının içine bir de düğme koymaktır:

html
<!-- Hatali:
<a href="/paket/pro"><h3>Pro paket</h3><button>Sepete ekle</button></a>
-->

<!-- Dogru: baglanti ve dugme kardes -->
<article class="kart">
  <h3><a href="/paket/pro">Pro paket</a></h3>
  <p>Yoğun trafikli siteler için.</p>
  <button type="button">Sepete ekle</button>
</article>

Aynı kural etiketlerde de geçerlidir. MDN, örtük olarak ilişkilendirilmiş denetim dışında <label> içine <a> veya <button> gibi etkileşimli öğeler koymamanızı, çünkü bunun alanı etkinleştirmeyi zorlaştırdığını söyler. Kullanım koşulları bağlantısını onay kutusunun önüne, ayrı bir paragrafa yazın. Ayrıntılar label, fieldset ve legend yazısında.

Hata 3: Liste ve tablo parçalarını yanlış ebeveyne koymak

<li> öğesinin izin verilen ebeveynleri <ul>, <ol> ve <menu> öğeleridir (MDN, eski <dir> öğesinin de ebeveyn olabildiğini ama bunun uygun kullanım olmadığını not eder). <ul> öğesinin izin verilen içeriği ise sıfır veya daha fazla <li>, <script> ve <template> öğesidir. Yani liste öğelerini <div> ile sarmak, ya da listenin doğrudan içine bir <div> veya <p> koymak geçersizdir. İçerik <li> öğesinin içine yazılır; <li> akış içeriği alır.

html
<!-- Hatali: <ul><div class="grup"><li>Bir</li></div></ul> -->
<ul>
  <li>
    <p>Alan adı kaydı</p>
    <ul>
      <li>.com</li>
      <li>.com.tr</li>
    </ul>
  </li>
  <li>Hosting</li>
</ul>

Bu örnekte iç içe liste de doğru yerde: alt liste, üst listenin bir <li> öğesinin içinde. Tablo parçaları için de aynı mantık geçerlidir: <td> yalnızca bir <tr> içinde kullanılabilir, <thead>, <tbody>, <tfoot> ve <caption> ise <table> içinde yerlerini bilir. Listeler için listeler yazısına, tablolar için tablo oluşturma yazısına bakın.

Hata 4: Form içinde form

Hata 4: Form içinde form

MDN'nin teknik özetine göre <form> öğesi akış içeriği alır, ancak içinde başka bir <form> bulunamaz. Sayfada iki ayrı gönderim gerekiyorsa formları kardeş olarak yazın. Bir düğmenin ya da alanın görsel olarak başka bir formun içinde durması gerekiyorsa, <button> öğesinin form özniteliği denetimi id ile istediğiniz forma bağlar; MDN bu özniteliğin denetimi belgenin herhangi bir yerindeki forma bağlayabildiğini ve atası olan formu da geçersiz kılabildiğini belirtir.

html
<form id="arama-formu" action="/ara" method="get">
  <label for="q">Ara</label>
  <input type="search" id="q" name="q" />
</form>

<form id="bulten-formu" action="/bulten" method="post">
  <label for="bulten-eposta">E-posta</label>
  <input type="email" id="bulten-eposta" name="eposta" />
  <button type="submit">Abone ol</button>
</form>

<!-- Formun disinda duran ama arama formunu gonderen dugme -->
<button type="submit" form="arama-formu">Aramayı başlat</button>

Form öznitelikleri ve gönderim davranışı form etiketi yazısında.

Hata 6: Aynı id değerini birden fazla kez kullanmak

MDN'ye göre id değeri tüm belgede benzersiz olmalıdır; bir öğe yalnızca tek bir id taşıyabilir ve değer boşluk karakteri içermemelidir (tarayıcılar boşluğu kimliğin parçası sayar). Tekrar eden id değerleri sessizce bozulan özelliklere yol açar: <label> öğesinin for özniteliği belgedeki eşleşen ilk öğeyi bulur, sonrakileri dikkate almaz; parça bağlantıları (#hedef) yalnızca bir yere gidebilir; aria-labelledby ve aria-describedby yanlış metni okutabilir. Bileşenleri sayfada birden fazla kez kullanıyorsanız her kopyanın kimliklerini değiştirmeyi unutmayın.

html
<!-- Hatali: iki alan da id="eposta"; ikinci label birinci alani isaret eder -->

<label for="fatura-eposta">Fatura e-postası</label>
<input type="email" id="fatura-eposta" name="fatura-eposta" />

<label for="iletisim-eposta">İletişim e-postası</label>
<input type="email" id="iletisim-eposta" name="iletisim-eposta" />

MDN iki ek uyarı daha verir. Değer geçerli bir CSS tanımlayıcısı değilse (örneğin 1234) CSS seçicilerinde veya querySelector() ile kullanılırken kaçırılması gerekir; bu yüzden kaçırma gerektirmeyen değerler önerilir. Ayrıca id değerine window.kimlik biçiminde erişmek, tarayıcıya ileride aynı adlı bir özellik eklenirse bozulabileceği için tehlikelidir; getElementById() kullanın. Global öznitelikler için global öznitelikler yazısına bakın.

Hata 7: alt metni ve etiketi unutmak

MDN'nin <img> sayfası alt özniteliğini zorunlu ve erişilebilirlik için son derece yararlı olarak tanımlar: ekran okuyucular bu değeri okur, görsel yüklenemezse de sayfada gösterilir. Süs görselleri için boş alt="" kullanılır; özniteliği hiç yazmamak ile boş yazmak aynı şey değildir. Form tarafında MDN'nin erişilebilirlik rehberi, formdaki her denetimin amacını anlatan bir <label> taşımasını ve etiketlerin görünür olmasını ister; placeholder metni kullanıcı yazmaya başlayınca kaybolduğu için etiket yerine geçmez. Görseller için img etiketi yazısına, adlandırma için erişilebilir ad yazısına bakın.

html
<!-- Hatali: <img src="logo.png"> ve <input name="ad" placeholder="Adınız"> -->
<img src="logo.png" alt="Örnek Hosting" />
<label for="ad">Adınız</label>
<input id="ad" name="ad" placeholder="Örneğin Ayşe" />

Hata 8: Başlıkları yazı boyutu için seçmek ve seviye atlamak

MDN'nin başlık öğeleri sayfası üç kural verir: başlık öğelerini metni büyütüp küçültmek için kullanmayın (bunun için CSS font-size var), seviye atlamayın (<h1> ile başlayıp <h2>, <h3> diye ilerleyin) ve bir sayfada genellikle tek bir <h1> kullanın. Ekran okuyucu kullanıcıları sayfayı çoğu zaman başlıktan başlığa atlayarak tarar; atlanan bir seviye, eksik bir başlık varmış izlenimi yaratır. Ayrıntılar h1-h6 başlık etiketleri yazısında.

Hata 9: Etiketleri yanlış kapatmak

HTML'de bazı öğeler boş (void) öğedir: içerikleri yoktur ve kapanış etiketi almazlar. MDN'nin <source> sayfası bunu "asla </source> yazmazsınız" diye özetler; <br>, <img>, <input>, <meta>, <link>, <hr>, <area>, <base>, <col>, <embed>, <track> ve <wbr> de bu gruptadır. Bu öğelere kapanış etiketi yazmak hatadır. Tersi de geçerlidir: boş olmayan öğelerin çoğunun teknik özetinde "etiket atlama: yok, başlangıç ve bitiş etiketi zorunludur" yazar. Bu yüzden <div /> gibi bir yazım boş olmayan bir öğe için geçerli bir kapanış değildir; her zaman </div> gibi ayrı bir bitiş etiketi yazın. Sondaki eğik çizgi bir XHTML alışkanlığıdır: MDN'nin <link> sayfası XHTML 1.0'da boş öğelerin sonda eğik çizgi gerektirdiğini, <br> sayfası da XHTML belgelerinde <br /> yazılması gerektiğini not eder. Bu serideki örneklerde boş öğeler <br /> biçiminde yazılıyor.

Bazı öğelerin bitiş etiketi ise gerçekten atlanabilir: <li> bitiş etiketi, ardından başka bir <li> geliyorsa veya ebeveynde başka içerik kalmadıysa atlanabilir; <p> için yukarıdaki liste geçerlidir. Atlanabilmesi, atlamanız gerektiği anlamına gelmez; kapanış etiketlerini yazmak yapıyı okunur ve hataya kapalı tutar.

Hata 10: ARIA ile yerel anlamı bozmak

Hata 10: ARIA ile yerel anlamı bozmak: Örtük rolü tekrar etmek (<nav role="navigation">) gereksizdir; <main> öğesine..., aria-hidden="true" odaklanabilir bir öğeye veya onu içeren...
  • Örtük rolü tekrar etmek (<nav role="navigation">) gereksizdir; <main> öğesine ise MDN'ye göre hiçbir rol verilemez.
  • aria-hidden="true" odaklanabilir bir öğeye veya onu içeren bir kapsayıcıya eklenmemelidir.
  • Bir düğmenin içine başlık koymak, ARIA açısından da anlamsızdır: düğme rolündeki öğelerin alt öğeleri sunumsal kabul edilir.
  • Ayrıntılar için ARIA nedir yazısına bakın.

Hataları nasıl bulursunuz?

Tarayıcı hatalı HTML'i de bir şekilde görüntülediği için göz kontrolü yetmez. MDN'nin WCAG "Sağlam" özeti, 4.1.1 ölçütü kapsamında HTML'inizi mümkün olduğunca geçerli tutmanızı ve W3C doğrulayıcısını (validator.w3.org) kullanmanızı önerir. Bunun yanında tarayıcının geliştirici araçlarındaki DOM görünümüne bakmak, tarayıcının yapıyı nasıl yorumladığını gösterir; örneğin etiket atlama kuralı gereği <p> içine yazdığınız <ul> paragrafın içinde görünmez. Erişilebilirlik denetimi için WCAG temelleri yazısındaki kontrol listesini kullanabilirsiniz.

Hızlı kontrol tablosu

HataNeden sorunDoğrusu
p içinde div, ul, tablep yalnızca akış içi içerik alırKapsayıcı olarak div; p ve liste kardeş
a içinde button veya aa içinde etkileşimli içerik olamazBağlantı ve düğmeyi kardeş yapın
button içinde a veya inputbutton içinde etkileşimli içerik olamazAyrı denetimler
ul içinde doğrudan divul yalnızca li, script, template alırİçeriği li içine yazın
form içinde formform başka bir form içeremezKardeş formlar ve form özniteliği
Tekrar eden idid belgede benzersiz olmalıHer kopyaya ayrı id
alt yokimg için alt zorunluAnlamlı alt veya süs için boş alt
label yok, placeholder varplaceholder yazınca kaybolurGörünür label
Başlık boyut için seçilmişBaşlıkla gezinmeyi bozarDoğru seviye ve CSS font-size
Boş öğeye kapanış etiketiBoş öğelerin bitiş etiketi yokturYalnızca başlangıç etiketi

Sık Sorulan Sorular

Tarayıcı sayfamı düzgün gösteriyorsa hata önemli mi?

Evet. Tarayıcı hatalı yapıyı onarırken sizin kastetmediğiniz bir DOM ağacı kurabilir. Görünüm tutarlı kalsa bile betikler, CSS seçicileri ve yardımcı teknolojiler bu onarılmış yapı üzerinde çalışır. WCAG 4.1.1 de içeriğin sorunsuz ayrıştırılabilmesini ister.

<code>&lt;a&gt;</code> içine <code>&lt;div&gt;</code> koyabilir miyim?

<a> öğesinin içerik modeli saydam olduğu için, bağlantının ebeveyni akış içeriği kabul ediyorsa evet. Ancak bağlantının içinde düğme, başka bir bağlantı veya tabindex taşıyan bir öğe olamaz.

Bir sayfada birden fazla <code>&lt;h1&gt;</code> kullanmak hata mı?

MDN'ye göre standart, iç içe olmadıkları sürece birden fazla <h1> öğesine izin verir; ama bu iyi uygulama sayılmaz. Sayfanın içeriğini anlatan tek bir <h1> önerilir. İç içe bölümlerde birden fazla <h1> kullanmak ise artık uygun değildir.

Hangi öğenin nereye konabileceğini nereden öğrenirim?

MDN'deki her öğe sayfasının sonundaki teknik özetten: "izin verilen içerik", "izin verilen ebeveynler" ve "etiket atlama" satırları tam olarak bu soruyu yanıtlar. Kategorilerin tam listesi MDN'nin içerik kategorileri rehberindedir.

Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.

İlgili içerikler: HTML öğrenme rehberi · belge yapısı · semantik etiketler · listeler · label ve fieldset · WCAG temelleri