Şablon, alan ve gölge

Web bileşenlerinin avantajı yeniden kullanılabilir olmalarıdır: Bir kullanıcı arayüzü widget'ını bir kez oluşturup birden çok kez yeniden kullanabilirsiniz. Web bileşenlerini oluşturmak için JavaScript'e ihtiyacınız olsa da JavaScript kitaplığına ihtiyacınız yoktur. HTML ve ilişkili API'ler ihtiyacınız olan her şeyi sağlar.

Web Bileşeni standardı; HTML şablonları, Özel Öğeler ve Gölge DOM olmak üzere üç bölümden oluşur. Bu iki yöntem birlikte kullanıldığında, önceden ele aldığımız diğer tüm HTML öğeleri gibi mevcut uygulamalara sorunsuz bir şekilde entegre edilebilen, özelleştirilmiş, bağımsız (kapsüllü) yeniden kullanılabilir öğelerin oluşturulmasını sağlar.

Bu bölümde, kullanıcıların bir deneyimi birden beş yıldıza kadar derecelendirmesine olanak tanıyan bir web bileşeni olan <star-rating> öğesini oluşturacağız. Özel öğelere ad verirken tamamen küçük harf kullanmanız önerilir. Ayrıca, normal ve özel öğeleri ayırt etmenize yardımcı olması için bir tire de ekleyin.

Kapsüllenmiş bir Gölge DOM ile şablon oluşturmak için <template> ve <slot> öğelerini, slot özelliğini ve JavaScript'i kullanmayı ele alacağız. Daha sonra, herhangi bir öğe veya web bileşeninde olduğu gibi metnin bir bölümünü özelleştirerek tanımlanan öğeyi yeniden kullanırız. Ayrıca, CSS'yi özel öğenin içinden ve dışından kullanarak kısaca ele alacağız.

<template> öğesi

<template> öğesi, klonlanacak ve JavaScript ile DOM'ye eklenecek HTML parçalarını bildirmek için kullanılır. Öğenin içeriği varsayılan olarak oluşturulmaz. Bunun yerine JavaScript kullanılarak somutlaştırılırlar.

<template id="star-rating-template">
  <form>
    <fieldset>
      <legend>Rate your experience:</legend>
      <rating>
        <input type="radio" name="rating" value="1" aria-label="1 star" required />
        <input type="radio" name="rating" value="2" aria-label="2 stars" />
        <input type="radio" name="rating" value="3" aria-label="3 stars" />
        <input type="radio" name="rating" value="4" aria-label="4 stars" />
        <input type="radio" name="rating" value="5" aria-label="5 stars" />
      </rating>
    </fieldset>
    <button type="reset">Reset</button>
    <button type="submit">Submit</button>
  </form>
</template>

Bir <template> öğesinin içerikleri ekrana yazılmadığından <form> ve içerikleri oluşturulmaz. Evet, bu Codepen boştur ancak HTML sekmesini incelerseniz <template> işaretlemesini görürsünüz.

Bu örnekte <form>, DOM'deki bir <template> öğesinin alt öğesi değildir. Daha ziyade, <template> öğelerinin içeriği, HTMLTemplateElement.content özelliği tarafından döndürülen bir DocumentFragment öğesinin alt öğeleridir. Görünür olması için içerikleri almak ve bu içerikleri DOM'ye eklemek üzere JavaScript kullanılmalıdır.

Bu kısa JavaScript, özel bir öğe oluşturmadı. Bu örnekte, <template> içeriği <body> içine eklenmiştir. İçerik, görünür ve stil verilebilir DOM'un bir parçası haline geldi.

DOM&#39;de gösterilen önceki kod kaleminin ekran görüntüsü.

JavaScript'in yalnızca bir yıldızlı değerlendirme için bir şablonun uygulanmasını zorunlu kılmak pek işe yaramaz, ancak sürekli kullanılan, özelleştirilebilir bir yıldız puanı widget'ı için bir web bileşeni oluşturmak yararlıdır.

<slot> öğesi

Her geçtiği yer için özelleştirilmiş bir açıklama eklemek üzere bir alan ekleriz. HTML, <template> içinde yer tutucu olarak bir <slot> öğesi sağlar. Bu öğe, ad sağlanırsa "adlandırılmış alan" oluşturur. Adlandırılmış alan, bir web bileşeni içindeki içeriği özelleştirmek için kullanılabilir. <slot> öğesi, özel bir öğenin alt öğelerinin kendi gölge ağacında nereye ekleneceğini kontrol etmemizi sağlar.

Şablonumuzda <legend>, <slot> olarak değiştirilir:

<template id="star-rating-template">
  <form>
    <fieldset>
      <slot name="star-rating-legend">
        <legend>Rate your experience:</legend>
      </slot>

name özelliği, öğenin değeri adlandırılmış bir alanın adıyla eşleşen bir slot özelliğine sahipse diğer öğelere slot atamak için kullanılır. Özel öğenin bir alanla eşleşmesi yoksa <slot> içeriği oluşturulur. Bu nedenle, HTML'sine içerik olmadan <star-rating></star-rating> yönergesi eklendiğinde oluşturulabilecek genel içeriğe sahip bir <legend> ekledik.

<star-rating>
  <legend slot="star-rating-legend">Blendan Smooth</legend>
</star-rating>
<star-rating>
  <legend slot="star-rating-legend">Hoover Sukhdeep</legend>
</star-rating>
<star-rating>
  <legend slot="star-rating-legend">Toasty McToastface</legend>
  <p>Is this text visible?</p>
</star-rating>

slot özelliği, <template> içindeki <slot> içeriğini değiştirmek için kullanılan genel bir özelliktir. Özel öğemizde, slot özelliğine sahip öğe <legend>'dir. Öyle olması gerekmiyor. Şablonumuzda <slot name="star-rating-legend">, <anyElement slot="star-rating-legend"> ile değiştirilir. Burada <anyElement>, herhangi bir öğe, hatta başka bir özel öğe olabilir.

Tanımlanmamış öğeler

<template> kuralımızda <rating> öğesi kullandık. Bu, özel bir öğe değildir. Aksine, bu bilinmeyen bir öğedir. Tarayıcılar bir öğeyi tanımadıklarında başarısız olmaz. Tanınmayan HTML öğeleri, tarayıcı tarafından CSS ile stil verilebilen anonim satır içi öğeler olarak ele alınır. <span> özelliğine benzer şekilde, <rating> ve <star-rating> öğelerinde de kullanıcı aracısı uygulanmış stiller veya anlamlar yoktur.

<template> ve içeriklerinin oluşturulmadığını unutmayın. <template>, oluşturulmayacak içerik barındıran bilinen bir öğedir. <star-rating> öğesi henüz tanımlanmadı. Bir öğe tanımlanana kadar, tarayıcı bunu tanınmayan tüm öğeler gibi görüntüler. Tanınmayan <star-rating>, şimdilik anonim bir satır içi öğe olarak ele alındığından, açıklamalar ve üçüncü <star-rating> içindeki <p> öğesi de dahil olmak üzere içerik, bunun yerine <span> içinde olsaydı olacakları gibi görüntülenir.

Bu tanınmayan öğeyi özel öğeye dönüştürmek için öğemizi tanımlayalım.

Özel öğeler

Özel öğeleri tanımlamak için JavaScript gerekir. Tanımlandığında, <star-rating> öğesinin içerikleri, onunla ilişkilendirdiğimiz şablonun tüm içeriklerini içeren bir gölge kökü ile değiştirilir. Şablondaki <slot> öğeleri, <star-rating> içindeki slot özellik değeri (varsa) <slot> öğesinin ad değeriyle eşleşen öğenin içeriğiyle değiştirilir. Aksi takdirde, şablon alanlarının içeriği görüntülenir.

Bir alanla ilişkili olmayan özel öğe içindeki içerik (üçüncü <star-rating> öğemizdeki <p>Is this text visible?</p>), gölge köküne dahil edilmez ve bu nedenle gösterilmez.

HTMLElement öğesini genişleterek star-rating adlı özel öğeyi tanımlıyoruz:

customElements.define('star-rating',
  class extends HTMLElement {
    constructor() {
      super(); // Always call super first in constructor
      const starRating = document.getElementById('star-rating-template').content;
      const shadowRoot = this.attachShadow({
        mode: 'open'
      });
      shadowRoot.appendChild(starRating.cloneNode(true));
    }
  });

Artık öğe tanımlandığına göre, tarayıcı bir <star-rating> öğesiyle her karşılaştığında şablonumuz olan #star-rating-template içeren öğenin tanımlandığı şekilde oluşturulur. Tarayıcı, düğüme bir gölge DOM ağacı ekler ve söz konusu gölge DOM'a şablon içeriğinin bir klonunu ekler. attachShadow() için kullanabileceğiniz öğelerin sınırlı olduğunu unutmayın.

const shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.appendChild(starRating.cloneNode(true));

Geliştirici araçlarına göz atarsanız <template> öğesindeki <form> değerinin, her özel öğenin gölge kökünün bir parçası olduğunu görürsünüz. <template> içeriğinin bir klonu geliştirici araçlarındaki her özel öğede görünür ve tarayıcıda görünür ancak özel öğenin içeriği ekranda oluşturulmaz.

Her özel öğedeki kopyalanan şablon içeriklerini gösteren DevTools ekran görüntüsü.

<template> örneğinde, şablon içeriklerini doküman gövdesine ekledik ve içeriği normal DOM'ye ekledik. customElements tanımında aynı appendChild() öğesini kullandık ancak kopyalanan şablon içerikleri, kapsüllenmiş bir gölge DOM'a eklendi.

Yıldızların stilsiz radyo düğmelerine dönüştüğünü fark ettiniz mi? Standart DOM yerine bir gölge DOM'un parçası olduğundan, Codepen'in CSS sekmesindeki stil uygulanmaz. Bu sekmenin CSS stilleri, gölge DOM'a değil, dokümanın kapsamına alınır. Bu nedenle stiller uygulanmaz. Kapsüllenmiş Gölge DOM içeriğimizi biçimlendirmek için kapsüllenmiş stiller oluşturmamız gerekiyor.

Gölge DOM

Gölge DOM, CSS stillerini her bir gölge ağacına dahil ederek onu dokümanın geri kalanından ayırır. Bu, harici CSS'nin bileşeniniz için geçerli olmadığı ve bileşen stillerinin, bilinçli olarak yönlendirmediğimiz sürece dokümanın geri kalanı üzerinde hiçbir etkisi olmadığı anlamına gelir.

İçerikleri bir gölge DOM'a eklediğimizden özel öğeye kapsüllenmiş CSS sağlayan bir <style> öğesi ekleyebiliriz.

Kapsamı özel öğeye ayarladığımızda, stillerin dokümanın geri kalanına yayılması konusunda endişelenmemiz gerekmez. Seçicilerin belirginliğini önemli ölçüde azaltabiliriz. Örneğin, özel öğede yalnızca radyo düğmeleri kullanıldığı için seçici olarak input[type="radio"] yerine input kullanabiliriz.

 <template id="star-rating-template">
  <style>
    rating {
      display: inline-flex;
    }
    input {
      appearance: none;
      margin: 0;
      box-shadow: none;
    }
    input::after {
      content: '\2605'; /* solid star */
      font-size: 32px;
    }
    rating:hover input:invalid::after,
    rating:focus-within input:invalid::after {
      color: #888;
    }
    input:invalid::after,
      rating:hover input:hover ~ input:invalid::after,
      input:focus ~ input:invalid::after  {
      color: #ddd;
    }
    input:valid {
      color: orange;
    }
    input:checked ~ input:not(:checked)::after {
      color: #ccc;
      content: '\2606'; /* hollow star */
    }
  </style>
  <form>
    <fieldset>
      <slot name="star-rating-legend">
        <legend>Rate your experience:</legend>
      </slot>
      <rating>
        <input type="radio" name="rating" value="1" aria-label="1 star" required/>
        <input type="radio" name="rating" value="2" aria-label="2 stars"/>
        <input type="radio" name="rating" value="3" aria-label="3 stars"/>
        <input type="radio" name="rating" value="4" aria-label="4 stars"/>
        <input type="radio" name="rating" value="5" aria-label="5 stars"/>
      </rating>
    </fieldset>
    <button type="reset">Reset</button>
    <button type="submit">Submit</button>
  </form>
</template>

Web bileşenleri <template> içi işaretleme ile kapsüllenirken CSS stilleri gölge DOM'a ayarlanır ve bileşenlerin dışındaki her şeyden gizlenir. Ancak <star-rating> öğesinin <anyElement slot="star-rating-legend"> kısmı olan oluşturulan alan içeriği kapsüle alınmaz.

Geçerli kapsamın dışında stil belirleme

Dokümana bir gölge DOM'un içinden stil eklemek ve gölge DOM'un içeriğini genel stillerden şekillendirmek mümkün olsa da basit değildir. Gölge DOM'un sona erdiği ve normal DOM'un başladığı gölge sınırından geçilebilir, ancak bu sınır yalnızca kasıtlı olarak çekilebilir.

Gölge ağacı, gölge DOM'un içindeki DOM ağacıdır. Gölge kökü, gölge ağacının kök düğümüdür.

:host sözde sınıfı, gölge ana makine öğesi olan <star-rating>'yi seçer. Gölge ana makinesi, gölge DOM'un ekli olduğu DOM düğümüdür. Ana makinenin yalnızca belirli sürümlerini hedeflemek için :host() kullanın. Bu, yalnızca iletilen parametreyle eşleşen gölge ana makine öğelerini (ör. bir sınıf veya özellik seçici) seçer. Tüm özel öğeleri seçmek için global CSS'de star-rating { /* styles */ } veya şablon stillerinde :host(:not(#nonExistantId)) kullanabilirsiniz. Spesifikasyon açısından, global CSS kazanır.

::slotted() sözde öğesi, gölge DOM'u sınırını gölge DOM'dan geçer. Seçiciyle eşleşirse yuvalı bir öğeyi seçer. Örneğimizde ::slotted(legend), üç göstergemizle eşleşiyor.

CSS'den bir gölge DOM'u global kapsamda hedeflemek için şablonun düzenlenmesi gerekir. part özelliği, stilini belirlemek istediğiniz herhangi bir öğeye eklenebilir. Ardından, bir gölge ağacı içinde iletilen parametreyle eşleşen öğeleri eşleştirmek için ::part() sözde öğesini kullanın. Sözde öğenin bağlayıcı veya kaynak öğesi, ana makine ya da özel öğe adıdır (bu örnekte star-rating). Parametre, part özelliğinin değeridir.

Şablon işaretlememiz şöyle başlamışsa:

<template id="star-rating-template">
  <form part="formPart">
    <fieldset part="fieldsetPart">

<form> ve <fieldset> için şu hedefleme yapılabilir:

star-rating::part(formPart) { /* styles */ }
star-rating::part(fieldsetPart) { /* styles */ }

Parça adları sınıflara benzerdir: Bir öğe birden fazla boşlukla ayrılmış parça adına ve birden fazla öğe aynı parça adına sahip olabilir.

Google'ın özel öğeler oluşturmak için harika bir yapılacaklar listesi vardır. Bildirim temelli gölge DOM'ler hakkında da bilgi edinebilirsiniz.

Öğrendiklerinizi sınayın

Şablon, alan ve gölge bilginizi test edin.

Varsayılan olarak, gölge DOM'un dışından alınan stiller, içerideki öğelerin stilini belirler.

Doğru.
Tekrar deneyin.
Yanlış.
Doğru.

Hangi yanıt <template> öğesinin doğru bir açıklamasıdır?

Sayfanızdaki herhangi bir içeriği görüntülemek için kullanılan genel öğe.
Tekrar deneyin.
Yer tutucu öğe.
Tekrar deneyin.
Varsayılan olarak oluşturulmayacak olan, HTML parçalarını tanımlamak için kullanılan bir öğe.
Doğru.