JavaScript'te bir öğenin sınıflarını değiştirmenin en pratik yolu element.classList özelliğidir: add(), remove(), toggle() ve replace() metotlarıyla sınıf ekler, kaldırır ve değiştirirsiniz. Herhangi bir özniteliği okumak ve yazmak için getAttribute() ve setAttribute(), satır içi stil vermek için element.style kullanılır. Pek çok öznitelik ayrıca öğe nesnesinde aynı adlı bir özellik olarak yansıtılır.

Bu yazıda öznitelik ile özellik arasındaki farkı, MDN'in yansıtma (reflection) kurallarını, mantıksal ve numaralandırılmış öznitelikleri, DOMTokenList metotlarının hatalarını ve style nesnesinin davranışını anlatıyoruz. data-* öznitelikleri ve dataset HTML tarafıyla ilgili olduğu için HTML öğrenme rehberi serisinde ele alınıyor. Serinin tamamı için Web API öğrenme rehberi sayfasına bakabilirsiniz.

Öznitelik (attribute) ile özellik (property) farkı

Öznitelik, HTML'de etiketin içine yazdığınız ad="değer" çiftidir: <input placeholder="Ad"> içindeki placeholder gibi. DOM'da her öznitelik bir Attr düğümüyle temsil edilir ve öğenin attributes özelliğindeki NamedNodeMap içinde durur. Özellik ise JavaScript nesnesinin bir alanıdır: input.placeholder gibi. MDN, pek çok özniteliğin ilgili DOM arayüzünde yansıtıldığını (reflected) açıklar: özelliği okumak özniteliği okumak, özelliğe yazmak özniteliğe yazmak demektir ve bu ilişki iki yönlüdür.

html
<input placeholder="Original placeholder" />
javascript
const input = document.querySelector("input");

// Genel yöntem: öznitelik metotları
let attr = input.getAttribute("placeholder");
input.setAttribute("placeholder", "Modified placeholder");

// Aynı işlem yansıtılan özellikle
attr = input.placeholder;
input.placeholder = "Modified placeholder";

MDN, yansıtılan özelliklerin getAttribute()/setAttribute() çağrılarına göre daha doğal bir programlama yolu sunduğunu belirtir. Ancak özellik adı her zaman öznitelik adıyla aynı değildir. Özellikler genellikle camelCase yazılır, özellikle kısa çizgi gibi özellik adında kullanılamayan karakterler içeren çok kelimeli adlarda: aria-checked özniteliği ariaChecked özelliğiyle yansıtılır. İki özel öznitelik de kendi adlarıyla değil, ayrı özelliklerle temsil edilir: id özniteliği id özelliğiyle, class özniteliği ise className özelliğiyle. MDN, class adının birçok dilde anahtar kelime olduğu için bu ismin seçildiğini not eder.

Mantıksal ve numaralandırılmış öznitelikler

Mantıksal (boolean) öznitelikler değer gerektirmez; varlıkları true, yoklukları false anlamına gelir. <input type="checkbox" checked> gibi. MDN'e göre bu kutu işaretliyken getAttribute("checked") "", işaretli değilken null döndürür; yansıtılan checked özelliği ise doğrudan true veya false verir. Mantıksal bir özniteliği setAttribute() ile açarken değerin ne olduğu önemli değildir; geleneksel olarak boş dize veya özniteliğin kendi adı verilir. Kapatmak için ise mutlaka removeAttribute() çağırmanız gerekir.

Numaralandırılmış (enumerated) öznitelikler önceden tanımlı sınırlı bir değer kümesi alır. Genel dir özniteliğinin geçerli değerleri ltr, rtl ve auto'dur ve bu değerler büyük-küçük harfe duyarsızdır. MDN'e göre yansıtılan dir özelliği her zaman belirtimde tanımlı standart (canonical) biçimi döndürür; yazarken de değeri bu biçime çevirir. getAttribute() ise HTML'deki değeri olduğu gibi verir:

html
<p dir="RTL">Right to left</p>
javascript
const pElement = document.querySelector("p");
console.log(pElement.dir); // "rtl"
console.log(pElement.getAttribute("dir")); // "RTL"

getAttribute, setAttribute ve removeAttribute

getAttribute, setAttribute ve removeAttribute: Kaynaktaki karakter referansları ayrıştırma sırasında çözülür..., Güvenlik nedeniyle betik dışı kaynaklardan CSP nonce değerleri...

element.getAttribute(name) özniteliğin dize değerini döndürür; öğede o ad yoksa null döner. HTML belgesindeki bir HTML öğesinde ad küçük harfe normalleştirilir. MDN'in örneğinde <div id="div1"> için getAttribute("id") "div1", olmayan lang için null döner. MDN iki önemli ayrıntı ekler:

  • Kaynaktaki karakter referansları ayrıştırma sırasında çözülür. data-payload="&lt;b&gt;hi&lt;/b&gt;" özniteliği için getAttribute() "<b>hi</b>" döndürür. Bu değeri zaten kaçırılmış sanıp innerHTML'e vermek XSS'e yol açabilir.
  • Güvenlik nedeniyle betik dışı kaynaklardan CSP nonce değerleri gizlenir; script.getAttribute("nonce") boş dize döndürür. Nonce değeri için script.nonce özelliği kullanılır.

element.setAttribute(name, value) özniteliğin değerini ayarlar; öznitelik varsa günceller, yoksa ekler. HTML öğelerinde ad otomatik olarak küçük harfe çevrilir; dize olmayan değerler dizeye dönüştürülür. Ad geçersiz karakterler içeriyorsa (boşluk, /, =, > gibi) InvalidCharacterError fırlatılır. element.removeAttribute(name) özniteliği kaldırır; öznitelik yoksa hata vermeden döner. MDN, özniteliği kaldırmak için değeri null yapmak yerine bu metodu kullanmanızı önerir, çünkü pek çok öznitelik null değeriyle beklendiği gibi davranmaz.

Diğer yardımcı metotlar şunlardır: hasAttribute(name) özniteliğin varlığını true/false olarak bildirir; hasAttributes() öğede herhangi bir öznitelik olup olmadığını söyler; getAttributeNames() tüm öznitelik adlarını bir dizi olarak döndürür. MDN, getAttributeNames() ile getAttribute()'u birlikte kullanmanın attributes'a erişmeye göre bellek açısından verimli ve performanslı bir alternatif olduğunu belirtir:

javascript
for (const name of element.getAttributeNames()) {
  const value = element.getAttribute(name);
  console.log(name, value);
}

toggleAttribute(name, force), mantıksal öznitelikler için kısa yoldur: ikinci parametre yoksa özniteliği varsa kaldırır, yoksa ekler. force true ise yalnızca ekler, false ise yalnızca kaldırır. Metot, işlem sonunda öznitelik varsa true, yoksa false döndürür.

html
<input value="text" /> <button>toggleAttribute("disabled")</button>
javascript
const button = document.querySelector("button");
const input = document.querySelector("input");

button.addEventListener("click", () => {
  input.toggleAttribute("disabled");
});

setAttribute bir enjeksiyon noktası olabilir

MDN, setAttribute()'ın da bir enjeksiyon noktası (injection sink) olabileceği konusunda uyarır: bazı özniteliklerin değeri HTML, betik veya betik URL'si olarak yorumlanır. MDN'in örneğinde kullanıcıdan gelen "alert(1)" dizesi button.setAttribute("onclick", ...) ile atandığında düğmeye basılınca çalışır. Aynı risk <iframe>'in srcdoc özniteliği ve betiklerin src veya href öznitelikleri için de geçerlidir. Trusted Types zorunlu tutulduğunda bu özniteliklere düz dize yerine TrustedScript, TrustedHTML veya TrustedScriptURL nesnesi vermeniz gerekir; aksi halde TypeError alırsınız. Kullanıcı verisini olay işleyici özniteliklerine asla yazmayın; olaylar için addEventListener kullanın. Genel XSS savunması için XSS ve CSP koruma yazısına bakabilirsiniz.

classList ve DOMTokenList metotları

classList ve DOMTokenList metotları: Liste her zaman büyük-küçük harfe duyarlıdır, Listeyi değiştiren metotlar fazla boşlukları otomatik kırpar ve yinelenen..., add() boş dizeyle...

element.classList, öğenin class özniteliğini temsil eden canlı bir DOMTokenList döndürür. className ile sınıfları boşlukla ayrılmış tek bir dize olarak yönetmek yerine, tek tek sınıf eklemek ve kaldırmak için uygun bir alternatiftir. class özniteliği yoksa veya boşsa length değeri 0 olan boş bir liste döner.

Metot veya özellikNe yaparDönüş değeri
add(t1, t2, ...)Verilen sınıfları ekler, var olanları atlaryok
remove(t1, t2, ...)Verilen sınıfları kaldırır, olmayan sınıfta hata vermezundefined
toggle(token)Sınıf varsa kaldırır, yoksa eklerişlem sonrası sınıf var mı (boolean)
toggle(token, force)force true ise yalnızca ekler, false ise yalnızca kaldırırboolean
replace(eski, yeni)Bir sınıfı başkasıyla değiştirirbaşarılıysa true, eski sınıf yoksa false
contains(token)Sınıfın listede olup olmadığını test ederboolean
item(i)İndeksteki sınıfı verirdize veya aralık dışında null
valueListenin boşlukla ayrılmış dize halidize
lengthSınıf sayısısayı
javascript
const div = document.createElement("div");
div.classList = "foo"; // classList.value özelliğine aktarılır
// başlangıç: <div class="foo"></div>

div.classList.remove("foo");
div.classList.add("another-class");
// <div class="another-class"></div>

div.classList.toggle("visible");        // varsa kaldır, yoksa ekle
div.classList.toggle("visible", i < 10); // koşula göre ekle veya kaldır

console.log(div.classList.contains("foo")); // false

const cls = ["foo", "bar"];
div.classList.add(...cls);               // yayma sözdizimiyle birden fazla sınıf
div.classList.remove(...cls);
div.classList.replace("foo", "bar");

MDN, DOMTokenList ile ilgili birkaç kuralı belirtir:

  • Liste her zaman büyük-küçük harfe duyarlıdır: Aktif ile aktif farklı sınıflardır.
  • Listeyi değiştiren metotlar fazla boşlukları otomatik kırpar ve yinelenen değerleri kaldırır. class=" d d e f" olan bir öğeye add("x") yaptıktan sonra liste d e f x olur.
  • add() boş dizeyle çağrılırsa SyntaxError, boşluk içeren bir değerle çağrılırsa InvalidCharacterError fırlatır. Bu yüzden add("btn btn-primary") yerine add("btn", "btn-primary") yazmalısınız.
  • classList salt okunurdur ama ona doğrudan bir dize atayabilirsiniz; bu, value özelliğine atamakla eşdeğerdir.
  • replace(), ilk sınıf listede yoksa yeni sınıfı eklemeden hemen false döndürür.

toggle()'ın dönüş değeri arayüz durumunu senkron tutmak için kullanışlıdır. MDN'in örneğinde bir öğeye tıklandığında classes.toggle("c") sonucu okunur ve sınıfın eklenip eklenmediği metin olarak yazılır. force parametresi ise koşula bağlı sınıflar için idealdir; MDN örneğindeki span.classList.toggle("c", window.innerWidth > 1000) sınıfı yalnızca pencere 1000 pikselden genişse ekler. DOMTokenList yalnızca classList için değil, <link> öğesinin relList'i veya <iframe>'in sandbox'ı gibi başka öznitelikler için de kullanılır; supports() metodu bu özniteliklerde bir değerin desteklenip desteklenmediğini algılamaya yarar.

className mi classList mi?

className, class özniteliğini tek bir dize olarak yansıtır. Atama yaptığınızda tüm sınıflar değişir: el.className = "active" öğedeki diğer sınıfları siler. Tek bir sınıfı değiştirmek istediğinizde classList bu yan etkiden korur. MDN, SVG öğelerinde className'in bir SVGAnimatedString olabileceğini ve bu yüzden SVG'de sınıfı getAttribute("class")/setAttribute("class", ...) ile yönetmenin daha kolay olduğunu belirtir; boş bir class özniteliğinde getAttribute()'ın "" yerine null döndürebileceğini de not eder.

style ile satır içi stil vermek

style ile satır içi stil vermek: Nesne, tarayıcının desteklediği tüm CSS özellikleri için hem kısa çizgili hem..., Kısa yazım (shorthand) özellikler uzun hallerine genişletilir...

element.style, öğenin satır içi style özniteliğini canlı bir CSSStyleProperties nesnesi olarak döndürür (MDN, eski belirtimlerin CSSStyleDeclaration döndürdüğünü not eder). Bu nesne üzerinden satır içi stilleri okuyabilir ve yazabilirsiniz:

javascript
const el = document.getElementById("kutu");

el.style.backgroundColor = "red";               // camelCase özellik adı
el.style.setProperty("border-top", "1px solid black"); // kısa çizgili ad
console.log(el.style.getPropertyValue("border-top"));

el.style.color = null;          // tek bir bildirimi sıfırlar
el.style.removeProperty("border-top");

MDN'in açıkladığı davranışlar şunlardır:

  • Nesne, tarayıcının desteklediği tüm CSS özellikleri için hem kısa çizgili hem camelCase adlı özellikler içerir; satır içi bir değeri olmayanlar "" döner. Yani el.style.color yalnızca style özniteliğinde yazılmış rengi verir, CSS dosyasından gelen rengi vermez.
  • Kısa yazım (shorthand) özellikler uzun hallerine genişletilir. border-top: 1px solid black stili borderTopColor, borderTopStyle ve borderTopWidth olarak da okunabilir.
  • Bir bildirimi sıfırlamak için değeri null veya boş dize yapmanız yeterlidir.
  • style özelliği salt okunurdur, ama ona bir dize atamak cssText özelliğine atamakla eşdeğerdir ve tüm satır içi stilleri değiştirir.
  • style ile verilen değerler, CSS basamaklamasında style özniteliğiyle yazılmış satır içi bildirimle aynı önceliğe sahiptir.

Öğe referansı tutan öznitelikler

MDN'in yansıtma rehberi, değer olarak başka öğelerin kimliklerini alan öznitelikleri ayrıca ele alır. aria-labelledby="label_1 label_2 label_3" özniteliği ariaLabelledByElements özelliğiyle yansıtılır; bu özellik dizedeki kimliklere karşılık gelen öğelerden oluşan bir dizi döndürür. Kimliği eşleşen öğe yoksa (örneğin label_3 belgede yoksa) dizide yer almaz. Normal yansıtmadan farklı olarak, özelliğe bir dizi atamak özniteliği temizler ve getAttribute() artık "" döndürür; setAttribute() ile özniteliği yeniden yazmak ilişkiyi geri getirir. Özellik dizisi statiktir; dönen diziyi değiştirmek özniteliği güncellemez. Bu özellikler için tarayıcı desteğini MDN uyumluluk tablosundan kontrol edin.

Mini örnek: sekme düğmelerini yönetmek

Aşağıdaki örnek classList, mantıksal öznitelik ve aria-selected özniteliğini birlikte kullanır:

html
<div id="sekmeler">
  <button class="sekme aktif" aria-selected="true" data-panel="p1">Genel</button>
  <button class="sekme" aria-selected="false" data-panel="p2">Ayarlar</button>
</div>
<section id="p1">Genel panel</section>
<section id="p2" hidden>Ayarlar paneli</section>
javascript
const sekmeler = document.querySelectorAll("#sekmeler .sekme");

for (const sekme of sekmeler) {
  sekme.addEventListener("click", () => {
    for (const diger of sekmeler) {
      const secili = diger === sekme;
      diger.classList.toggle("aktif", secili);
      diger.setAttribute("aria-selected", String(secili));
      const panel = document.getElementById(diger.getAttribute("data-panel"));
      panel.toggleAttribute("hidden", !secili);
    }
  });
}

toggle() ve toggleAttribute() metotlarının ikinci parametresi, koşul doğruysa ekle, yanlışsa kaldır anlamına geldiği için ayrı if dallarına gerek kalmaz. aria-selected mantıksal bir öznitelik değil, "true"/"false" dize değeri alan bir ARIA özniteliği olduğu için setAttribute() ile açıkça yazılır. Olay dinleyicisini her düğmeye ayrı eklemek yerine kapsayıcıya tek dinleyici koymak için olay delegasyonu yazısına bakabilirsiniz.

Sık yapılan hatalar

  • Mantıksal özniteliği "false" ile kapatmaya çalışmak: Öznitelik var olduğu sürece true sayılır; removeAttribute() kullanın.
  • className ile tek sınıf eklemek: el.className = "yeni" diğer sınıfları siler; classList.add() kullanın.
  • Boşluk içeren sınıf adı vermek: classList.add("a b") InvalidCharacterError fırlatır; sınıfları ayrı argüman olarak geçin.
  • style ile CSS dosyasındaki değeri okumaya çalışmak: style yalnızca satır içi stilleri gösterir.
  • Özellik ile öznitelik değerlerinin hep aynı olduğunu sanmak: Numaralandırılmış özniteliklerde özellik standart biçimi, getAttribute() ham değeri verir.
  • Kullanıcı verisini onclick veya srcdoc gibi özniteliklere yazmak: Bu öznitelikler kod veya HTML olarak çalışır.

Sık Sorulan Sorular

setAttribute mı yoksa doğrudan özellik ataması mı kullanmalıyım?

Öznitelik yansıtılıyorsa MDN özellik kullanımını daha doğal bulur: input.placeholder = "...". Yansıtılmayan öznitelikler, ARIA veya özel öznitelikler ve SVG öğeleri için setAttribute() daha genel bir yoldur.

classList.toggle neden bazen beklediğimden farklı çalışıyor?

Tek parametreli toggle() mevcut duruma göre davranır; durumu bilmediğiniz yerlerde ikinci force parametresini vererek sonucu belirleyin. Metodun dönüş değeri, işlem sonunda sınıfın listede olup olmadığını söyler.

data- öznitelikleri nasıl okunur?

getAttribute("data-panel") her zaman çalışır. dataset özelliği ve data-* özniteliklerinin HTML tarafındaki kuralları HTML öğrenme rehberi serisinde anlatılıyor.

getAttribute ile özelliğin döndürdüğü değer neden farklı?

getAttribute() HTML'deki ham dize değerini verir. Yansıtılan özellik ise değeri türüne göre yorumlar: mantıksal özniteliklerde true/false, numaralandırılmış özniteliklerde standart küçük harf biçim döner. MDN'in dir="RTL" örneğinde özellik "rtl", getAttribute() "RTL" verir.

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: Web API öğrenme rehberi · innerHTML ve textContent · öğe seçme · DOM öğe oluşturma · HTML öğrenme rehberi