6 puan yazan GN⁺ 2024-04-27 | 3 yorum | WhatsApp'ta paylaş
  • Aynı öğeye bağlı olsalar bile HTML öznitelikleri ile DOM özellikleri ayrı depolama alanları gibi çalışır; bu yüzden bir taraftaki değişiklik diğer tarafa otomatik olarak yansımayabilir
  • Öznitelikler HTML serileştirmesinde ve geliştirici araçlarındaki Elements panelinde görünür, ancak özellikler yalnızca DOM durumu olduğundan outerHTML gibi çıktılarda yer almaz
  • Öznitelik değerleri her zaman string'dir ve adları büyük/küçük harfe duyarlı değildir; buna karşılık özellikler nesne dahil farklı türler alabilir ve adları büyük/küçük harfe duyarlıdır
  • id, className, htmlFor, ariaLabel gibi öznitelikleri yansıtan özellikler bulunurken, input.value gibi mevcut durum ile varsayılan ayarın ayrıldığı istisnalar da vardır
  • Çerçeveler arasında hangi hedefin ayarlandığı değişir: Preact ve VueJS çoğunlukla özellikleri, React çoğunlukla öznitelikleri tercih eder; Lit ise .value sözdizimiyle ikisini açıkça ayırır

Öznitelikler ve özellikler aynı adı taşısa da farklı olabilir

  • Öznitelikler (attributes) ile özellikler (properties) birbirinden farklı depolama alanları gibi çalışabilir
    • `` üzerinde getAttribute('foo') 'bar' döndürürken, div.foo undefined olabilir
    • Sonrasında div.foo = 'hello world' ayarlansa bile foo öznitelik değeri 'bar' olarak kalır
  • Framework şablonlarında `` gibi öznitelikmiş gibi görünen sözdizimi kullanılsa da, içeride duruma göre özellik ayarlanabilir
    • Hangi durumda özellik ayarlanacağı framework'e göre değişir
    • Bazı ayarlar hem özelliği hem özniteliği değiştiren yan etkiler oluşturabilir

HTML serileştirmesinde kalan şey özniteliktir

  • Öznitelikler hem HTML'de hem DOM'da bulunur, ancak özellikler yalnızca DOM'da vardır
  • div.setAttribute('foo', 'bar') ve div.hello = 'world' birlikte çalıştırılsa bile div.outerHTML içinde `

` gibi yalnızca öznitelikler görünür

  • Tarayıcı geliştirici araçlarındaki Elements paneli öğenin özniteliklerini gösterir; tüm özellikleri göstermez

Değer türü ve büyük/küçük harf davranışı

  • Öznitelik değerleri serileştirilmiş biçimde çalışmak zorunda olduğundan her zaman string'dir
    • Bir nesne setAttribute ile verilirse "[object Object]" string'i olarak saklanır
    • Aynı nesne bir özelliğe verilirse nesne türü korunabilir
  • Öznitelik adları büyük/küçük harfe duyarlı değildir
    • HeLlO="world" DOM içinde hello özniteliğine normalize edilebilir
    • setAttribute('FOO', 'bar') da foo özniteliği olarak görünür
  • Özellik adları büyük/küçük harfe duyarlıdır
    • div.TeSt = 'value' ayarlansa bile div.test undefined olabilir
  • Öznitelik adlarının aksine öznitelik değerleri büyük/küçük harfe duyarlıdır

Özniteliği yansıtan özellikler

  • Bazı özellikler belirli bir özniteliği yansıtır (reflect)
    • div.id, id özniteliğini okuyup yazan bir getter/setter gibi çalışır
    • div.id = 'bar' çalıştırıldığında id özniteliği de 'bar' olur
  • Yansıtma ilişkisinde veri kaynağı özniteliktir
    • Özellik ayarlanırsa öznitelik güncellenir
    • Özellik okunursa öznitelik değeri okunur
  • Spesifikasyonda tanımlanan öznitelikler için kullanım kolaylığı adına karşılık gelen özellikler çoğu zaman oluşturulur
    • Rastgele bir foo özniteliği spesifikasyonda tanımlı olmadığından, karşılık gelen bir foo özelliği otomatik oluşmaz
  • `` spesifikasyonu “Content attributes” bölümünde öznitelikleri, “DOM interface” bölümünde ise özellikleri tanımlar
    • reversed ve type IDL özellikleri, aynı adlı içerik özniteliklerini reflect etmelidir

Adın değiştiği yansıtma ilişkileri

  • Bazı özelliklerin yansıttığı öznitelikle adı farklıdır
  • Bazen ad, JavaScript özellik stiline uyması için değişir
    • `` üzerindeki el.crossOrigin, crossorigin özniteliğini yansıtır
    • Tüm öğelerde el.ariaLabel, aria-label özniteliğini yansıtır
    • ARIA yansıtma özellikleri 2023 sonlarında tarayıcılar arasında ortak destek kazandı; öncesinde yalnızca öznitelik kullanılabiliyordu
  • Bazen de geçmişteki JavaScript rezerve sözcükleri nedeniyle ad değişmiştir
    • Tüm öğelerde el.className, class özniteliğini yansıtır
    • `` üzerindeki el.htmlFor, for özniteliğini yansıtır

Doğrulama, tür dönüşümü ve varsayılan değerler

  • Özelliklerde doğrulama ve varsayılan değerler bulunabilir, ancak özniteliğin kendisinde böyle bir davranış yoktur
  • input.type, öznitelik ile özellik arasındaki farkı iyi gösterir
    • input.getAttribute('type') null olsa bile input.type varsayılan değer olarak 'text' döndürür
    • input.type = 'number' hem öznitelikte hem özellikte 'number' gibi görünür
    • input.type = 'foo' özniteliğe 'foo' yazar, ama özellik getter'ı geçersiz değeri görüp 'text' döndürür
  • Bazı özellikler tür dönüşümü yapar
    • `` için details.getAttribute('open') boş string iken details.open true olur
    • details.open = false, open özniteliğini kaldırır
    • details.open = 'hello', string'i doğrudan özniteliğe yazmaz; boolean'a çevirir ve open özniteliğinin mevcut olduğu bir durum oluşturur
  • img.height gibi özellikler öznitelik değerini sayıya dönüştürür
    • Setter gelen değeri sayıya çevirir ve negatif değerleri 0 kabul eder

input.value, mevcut değer ile varsayılan değeri ayırır

  • input üzerinde hem value özelliği hem value özniteliği vardır, ancak value özelliği value özniteliğini yansıtmaz
  • value özniteliğini yansıtan özellik defaultValue'dur
    • defaultValue getter'ı value özniteliğini ya da boş string'i döndürür
    • defaultValue setter'ı value özniteliğini string olarak ayarlar
  • value özelliği başlangıçta defaultValue'yu takip eder, ancak JavaScript veya kullanıcı etkileşimiyle ayarlandıktan sonra iç değeri kullanır
    • Başlangıçtaki `` içinde value, defaultValue, getAttribute('value') üçü de 'default' değerindedir
    • input.defaultValue = 'new default' çalıştırıldığında öznitelik ve value birlikte 'new default' gibi görünür
    • Sonrasında input.value = 'hello!' çalıştırılırsa value 'hello!' olur, ancak öznitelik ile defaultValue 'new default' olarak kalır
    • setAttribute('value', 'another new default') çalıştırılsa bile mevcut value 'hello!' olarak korunur
  • Form sıfırlandığında iç value durumunun da sıfırlanması bu şekilde çalışabilir

Öznitelikler ayar, özellikler durum için daha doğaldır

  • Öznitelikleri ayar (configuration), özellikleri ise durum (state) için kullanmak daha doğal bir ayrımdır
  • `` adı hariç bu ayrımı iyi gösteren bir örnektir
    • value özniteliği varsayılan değeri ayarlar
    • value özelliği mevcut durumu sunar
  • Özellik okunup yazılırken doğrulamanın uygulanması, buna karşılık öznitelik okunup yazılırken uygulanmaması da bu yaklaşımla uyumludur
  • ve açık durumu open özniteliğiyle ifade eder; tarayıcı kullanıcı etkileşimine göre bu özniteliği doğrudan ekleyip kaldırır
    • Bu tasarım, özniteliğin ayar için olduğu ayrımını bozar
    • DOM yöneten framework'lerin veya sıradan JavaScript kodunun, DOM'un kendi kendine değişebildiği durumlara hazırlıklı olması gerekir
  • Alternatif bir tasarım; `` gibi varsayılan ayar özniteliği, mevcut durumu okuyup yazan details.open özelliği ve durumu hedeflemek için bir CSS sözde sınıfına ayrılabilirdi
  • Simon Peters bununla ilgili erken tasarım tartışmasını buldu
  • contenteditable de bu ayrımı bozar, ancak çok sayıda kırılmayı göze alan bir opt-in olarak değerlendirilir

Framework'lere göre işleme biçimi

  • Preact ve VueJS, bazı önceden tanımlı istisnalar dışında propName in element true olduğunda özellik ayarlar; değilse öznitelik ayarlar
    • Varsayılan olarak öznitelikten çok özelliği tercih ederler
    • String tabanlı render yöntemleri bunun tersine çalışır ve yalnızca özelliğe özel girdileri yok sayar
    • İlgili uygulamalar: Preact'in setProperty, VueJS'in shouldSetAsProp
  • React, bazı önceden tanımlı istisnalar dışında öznitelik ayarlar
    • Bu yaklaşım, string tabanlı render yöntemlerine daha yakın bir mantık üretir
    • Custom element özellikleri React'in önceden tanımlı listesinde yer almadığından öznitelik olarak ayarlanır ve yalnızca özelliğe özel API'ler çalışmayabilir
    • React 19'da custom element'ler için Preact/VueJS modeline geçilmesi planlanıyor
    • React, className adlı özellik adını öznitelik benzeri bir sözdizimiyle yaygınlaştırdı; ancak içeride class özniteliğini ayarlar
    • İlgili uygulama: React'in setProp
  • lit-html, öznitelik ile özellik ayrımını sözdiziminde korur
    • `` gibi adın başına . koymak, öznitelik değil özellik ayarlamak anlamına gelir
    • İlgili dokümantasyon: Lit expressions

3 yorum

 
GN⁺ 2024-04-27
Hacker News yorumları
  • data- özniteliklerini* kullanmak, yazıda ele alınan yöntemler arasında makul bir uzlaşma sağlar
    Bu öznitelikler JavaScript’te dataset üzerinde okuma/yazma özellikleri olarak otomatik biçimde açığa çıkar ve document.getElementById('myDiv').dataset.payload == "something" gibi erişilebilir
    Ancak HTML öznitelikleri kebab-case, JavaScript ise camelCase kullandığı için data-my-cool-data-attribute değerinin dataset.myCoolDataAttribute olmasına dikkat etmek gerekir

    • Büyük/küçük harf gösteriminin otomatik dönüştürülmesinden gerçekten nefret ediyorum
      camelCase kullanan çeşitli ekosistemler genelinde büyük harfli kısaltmaların nasıl ele alınacağına dair yerleşik bir teamül yok; bu yüzden dataset.myID gibi durumlar muğlaklaşıyor
    • kebab-case ile camelCase dönüşümünden kaçınmak için Element.hasAttribute('data-thing'), Element.getAttribute('data-thing'), Element.setAttribute('data-thing', '...') gibi metotları kullanıyorum
      DOM’da inceleme yaparken veya hata ayıklarken tuhaf bir dönüşümden geçmek zorunda kalınmadığı için niyet çok daha net oluyor
    • MDN’nin data attributes belgesi HTML sözdizimini, JavaScript erişimini, CSS erişimini ve erişilebilirlikle ilgili dikkat edilmesi gerekenleri ele alıyor: https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Us...
      Ayrıca https://developer.mozilla.org/en-US/docs/Web/HTML/Global_att... adresinde data-* küresel öznitelikleri, “betiklerin HTML ile DOM temsili arasında kendilerine özgü bilgileri değiş tokuş etmesini sağlayan özel veri öznitelikleri” olarak açıklanıyor
    • Bu özniteliklere bir ölçüde CSS’ten de erişilebiliyor
    • JavaScript yürütme bağlamları arasında iletişim kurmanın ilginç bir başka yolu da bu
      Burada kastedilen bağlamlar mutlaka farklı iframe’ler değil; tarayıcı eklentilerinde olduğu gibi aynı DOM’a erişen ayrı JavaScript yürütme bağlamları oluşturan durumlar
      Geliştirici araçları protokolü terminolojisinde bunlara isolated worlds da deniyor
  • Yazıda ele alınanlar, hayata pek katkısı olmayan karmaşıklık ve davranışları gösteriyor
    Bu tür davranışların artısı yok, eksisi var; spesifikasyonun DOM nesneleri ile JavaScript nesnelerini aynı şey olarak tanımlayıp özniteliklerle özellikleri de aynı kavram hâline getirmesi gerekirdi

    • Böyle bir kısıtlama “basitçe” var olsaydı, modern UI framework’lerinin çoğu hiç çalışmazdı
      DOM düğümlerinde string olmayan değerler saklayabilmek bariz bir avantaj ve hafife alınacak bir konu değil
    • “Basitçe” kısmını çıkarırsak katılıyorum
      Tüm özelliklerin yalnızca string olabileceği şart koşulmadıkça bu hiç de basit değil
      Sayılarla dönüşüm, nesne atanabilen özellikler ve özniteliklerle eşleşmeyen özellikler de hesaba katılmalı
      Yine de %100 sabit bir teamül ya da kural olsaydı daha iyi olurdu; ama bu basit bir iş değil
    • Öyle yapılırsa DOM arayüzüne string olmayan değerler koyamazsınız; metotlar da dahil olduğu için DOM işlevlerinin çoğu ortadan kalkar
    • Öznitelikleri ve özellikleri birbirine simetrik yapmak mevcut DOM API’sinde zor
      addEventListener gibi metotlar tek tek DOM öğelerinin prototip özellikleridir, ama string öznitelik olarak bakıldığında anlamlı değildir
      O zaman DOM öğesi metotlarının HTMLElement.addEventListener(foo, 'some-event', e => console.log(e)); gibi serbest fonksiyonlar olması gerekirdi; bu bir zevk meselesi olsa da JavaScript’te nesne metodu çağrısı kullanabilmek daha iyi
    • İkisinin neden farklı olması gerektiğini pek anlamıyorum
      DOM öğelerinde durumu setAttribute ile değiştiren çok belirli bir arayüz var; bu arayüz üzerinden DOM’a mesaj göndermek için oluşturulmuş JavaScript nesnesine rastgele özellik eklediğinizde bunun neden otomatik olarak yönlendirileceğini beklemek gerektiğini sorguluyorum
      Böyle bir API daha iyi bir deneyim sunabilir, ama bazı zor sorunları var; yine de daha temiz olacağı kesin
      Belki de backend koduyla çok uzun süre uğraştığım içindir; bu tür arayüzlere bakınca içeride tuhaf pek çok şey varmış ve geçmişteki iyi niyetli bir geliştirici mümkün olan en güvenli sarmalayıcıyı vermiş gibi hissediyorum
  • Anlamaya yardımcı olan bakış açısı, dar anlamdaki özniteliğin bir DOM düğümü, yani Attr düğümü olduğudur
    Bu düğüm name, value, ownerElement, prefix gibi özelliklere sahip olabilir ve bütünüyle, dizi olmayan bir NamedNodeMap biçiminde sahibi olan öğenin attributes özelliğinin değeri olarak erişilebilir
    Bu yüzden getAttribute(name) adı yanıltıcıdır; gerçekte öznitelik düğümünün kendisini değil, ilgili öznitelik düğümünün value özelliğini döndürdüğü için getAttributeValue(name) adına daha yakındır
    Referans: https://developer.mozilla.org/en-US/docs/Web/API/Element/get..., https://developer.mozilla.org/en-US/docs/Web/API/Element/get..., https://developer.mozilla.org/en-US/docs/Web/API/Attr

    • Bu açıklama doğru, ama setAttribute, getAttribute, getAttributeNames, hasAttribute ile yapılamayıp öznitelik düğümleri ya da el.attributes ile yapılabilen pratik bir şey var mı bilmiyorum
      Öznitelik düğümlerini öğeler arasında taşıyamazsınız, bir öğe içinde öznitelik düğümlerinin sırasını da değiştiremezsiniz
  • Jake, peki buna ne olacak: https://jakearchibald.com/2023/against-self-closing-tags-in-...

    • “Blogumda bu sözdizimini göreceksiniz; bunun nedeni Prettier’ın böyle yapması ve ben Prettier’ı gerçekten seviyorum”
  • Property’yi güncellememek gerektiği yönündeki mantığı anlıyorum, ama CSS ile hedeflenebilmesi kullanışlı

    • Bu yüzden, input öğelerindeki :checked’a benzer şekilde pseudo-class da olması gerektiğini söyledim
    • Benim için de diğer tüm nedenlerden çok bu durum öne çıkıyor
      Debugger’dan geçmektense DOM’u anında kontrol etmek hata ayıklamayı çok daha kolay hale getiriyor
  • Belki de en önemli fark kalıcılık, ama yazıda buna değinilmiyor
    Attribute’lar, sayfadan ayrılmamak gibi o DOM ağacının aynı kök nesnesine erişilebildiği sürece korunur
    Nesne property’leri ise yalnızca o nesne garbage collection’a uğrayana kadar korunur

    • Nesne, attribute’a sahip DOM node’u var olduğu sürece var olduğundan yaşam döngüleri aynıdır
      DOM node’u klonlanırsa ya da serialize/deserialize edilirse, aynı attribute’lara sahip tamamen farklı bir DOM node’u olur; bu yüzden diğer node’un varsayılan olmayan property’lerine sahip olmaz
    • Burada attribute ve property arasındaki mevcut fark dışında pratik bir davranış farkı yok
    • Bu farkın pratikte nasıl ortaya çıktığını merak ediyorum
  • İlginç bir yazı
    JavaScript’teki DOM öğelerini hep bir proxy gibi gördüm
    Gerçek DOM öğesi üzerinde çalışmanızı sağlıyorlar; ama aynı zamanda sadece JavaScript nesneleri oldukları için, gerçek öğeyle etkileşen fonksiyonları kullanmanın yanında değer atayıp istediğiniz şeyi yapabileceğiniz hissini veriyorlar

  • Örneklerle iyi açıklanmış olması güzel; özellikle de sık sık gözden kaçabilen birkaç ayrıntı içerdiği için birçok kişiye yardımcı olabilir

  • DOM property’leri IDL attributes olarak da adlandırılır

    • Doğru, ama bu terimden bilerek kaçındım
      Spesifikasyon yazarları dışında IDL attributes diyen pek az kişi var; ikisine de attribute denince de çok kafa karıştırıcı oluyor
  • Güzel bir yazı; daha iyi bir ifade biçiminin nasıl görünebileceğini merak ettiriyor
    HTML’in string olmayan attribute’lara sahip olabilmesi ve bu değerlerin kapsamı olan bir script etiketindeki yerel değerlere referans verebilmesi ilginç olurdu
    Attribute’ları varsayılan ayarlar olarak görmek biraz tuhaf geldi
    Örneğin class attribute’u yalnızca varsayılan değeri gösterseydi, sınıfları toggle ederken DOM denetçisinde ya da string’e çevrilmiş HTML’de aktif sınıfın görünmemesi veya pasif sınıfın görünmesi gibi epey tuhaf durumlar ortaya çıkardı

    • Varsayılan ayar demedim, sadece ayar dedim
      class attribute’u bu sistem içinde gayet iyi çalışıyor
      Belgenin sahibi olarak class attribute’unu ben ayarlarım ve zaman içinde bu ayarı değiştirebilirim; ama bunu yalnızca ben yaparım
      Alternatif bir sistemde tarayıcı, hover veya focus durumuna tepki olarak sınıfları kendi kendine ekleyip kaldırırdı; neyse ki bunu yapmıyor ve bunun yerine pseudo-class kullanılıyor
 
superwoou 2024-04-27

attribute ile property aynı "özellik" olarak çevrilmiş..