Gölge DOM 201

CSS ve stil

Bu makalede, Shadow DOM ile yapabileceğiniz şaşırtıcı şeylerden daha fazlası ele alınmaktadır. Bu kod, Gölge DOM 101'de açıklanan kavramların üzerine inşa edilmiştir. Giriş için bu makaleyi inceleyin.

Giriş

Bunu kabul edelim. Stillendirilmemiş işaretlemede çekici bir şey yoktur. Neyse ki Web Bileşenleri'nin arkasındaki muhteşem ekip bunu öngördü ve bizi yarı yolda bırakmadı. CSS Kapsamlandırma Modülü, gölge ağacındaki içeriğe stil uygulamak için birçok seçenek tanımlar.

Stil kapsülleme

Gölge DOM'un temel özelliklerinden biri gölge sınırıdır. Bu paketin birçok güzel özelliği vardır ancak en iyi özelliklerinden biri, stil kapsüllemeyi ücretsiz olarak sağlamasıdır. Başka bir şekilde ifade edildi:

<div><h3>Light DOM</h3></div>
<script>
var root = document.querySelector('div').createShadowRoot();
root.innerHTML = `
  <style>
    h3 {
      color: red;
    }
  </style>
  <h3>Shadow DOM</h3>
`;
</script>

Bu demoyla ilgili iki ilginç gözlem vardır:

  • Bu sayfada başka h3'ler de var ancak h3 seçiciyle eşleşen ve bu nedenle kırmızı renkle işaretlenmiş tek öğe, ShadowRoot'tur. Yine, varsayılan olarak kapsamlı stiller.
  • Bu sayfada tanımlanan ve h3'leri hedefleyen diğer stil kuralları, içeriğime etki etmiyor. Bunun nedeni, seçicilerin gölge sınırını aşmaması.

Hikayenin manevi nedir? Dış dünyadan stil kapsüllememiz var. Teşekkürler Gölge DOM.

Barındırma öğesinin stilini belirleme

:host, gölge ağacı barındıran öğeyi seçmenize ve öğeye stil uygulamanıza olanak tanır:

<button class="red">My Button</button>
<script>
var button = document.querySelector('button');
var root = button.createShadowRoot();
root.innerHTML = `
  <style>
    :host {
      text-transform: uppercase;
    }
  </style>
  <content></content>
`;
</script>

Bir nokta, üst sayfadaki kuralların öğede tanımlanan :host kurallarından daha ayrıntılı ancak barındıran öğede tanımlanan style özelliğinden daha az ayrıntılı olmasıdır. Bu sayede kullanıcılar, stilinizi dışarıdan geçersiz kılabilir. :host ayrıca yalnızca ShadowRoot bağlamında çalıştığından bunu Gölge DOM dışında kullanamazsınız.

:host(<selector>) işlevsel biçimi, bir <selector> ile eşleşirse ana makine öğesini hedeflemenize olanak tanır.

Örnek: Yalnızca öğenin kendisi .different sınıfına (ör. <x-foo class="different"></x-foo>) sahipse eşleştirilir:

:host(.different) {
    ...
}

Kullanıcı durumlarında tepki verme

:host için yaygın bir kullanım alanı, özel öğe oluştururken farklı kullanıcı durumlarına (:hover, :focus, :active vb.) tepki vermek istediğinizde ortaya çıkar.

<style>
  :host {
    opacity: 0.4;
    transition: opacity 420ms ease-in-out;
  }
  :host(:hover) {
    opacity: 1;
  }
  :host(:active) {
    position: relative;
    top: 3px;
    left: 3px;
  }
</style>

Bir öğenin temasını belirleme

:host-context(<selector>) sözde sınıfı, kendisi veya üst öğelerinden herhangi biri <selector> ile eşleşirse ana öğeyle eşleşir.

:host-context(), bir öğenin temasını çevresine göre belirlemek için yaygın olarak kullanılır. Örneğin, birçok kullanıcı <html> veya <body> öğesine sınıf uygulayarak tema oluşturma işlemini gerçekleştirir:

<body class="different">
  <x-foo></x-foo>
</body>

.different sınıfına sahip bir öğenin alt öğesi olduğunda <x-foo> öğesine stil uygulamak için :host-context(.different) kullanabilirsiniz:

:host-context(.different) {
  color: red;
}

Bu sayede, öğenin bağlamına göre benzersiz bir şekilde stil uygulayan stil kurallarını öğenin gölge DOM'una yerleştirebilirsiniz.

Bir gölge kökünden birden fazla ana makine türünü destekleme

:host'ün bir diğer kullanımı, tema kitaplığı oluşturuyorsanız ve aynı Shadow DOM içinden birçok türde ana öğe stilini desteklemek istiyorsanız da geçerlidir.

:host(x-foo) {
    /* Applies if the host is a <x-foo> element.*/
}

:host(x-foo:host) {
    /* Same as above. Applies if the host is a <x-foo> element. */
}

:host(div) {
    /* Applies if the host element is a <div>. */
}

Gölge DOM'un iç kısımlarını dışarıdan biçimlendirme

::shadow sözde öğesi ve /deep/ birleştiricisi, CSS yetkisine sahip bir Vorpal kılıcına sahip olmak gibidir. Gölge ağaçları içindeki öğeleri biçimlendirmek için Gölge DOM'un sınırını aşmanıza olanak tanır.

::shadow sözde öğesi

Bir öğenin en az bir gölge ağacı varsa ::shadow sözde öğesi, gölge köküyle eşleşir. Bir öğenin gölge dom'undaki düğümleri biçimlendiren seçiciler yazmanıza olanak tanır.

Örneğin, bir öğe gölge kökü barındırıyorsa gölge ağacındaki tüm span'lara stil uygulamak için #host::shadow span {} yazabilirsiniz.

<style>
  #host::shadow span {
    color: red;
  }
</style>

<div id="host">
  <span>Light DOM</span>
</div>

<script>
  var host = document.querySelector('div');
  var root = host.createShadowRoot();
  root.innerHTML = `
    <span>Shadow DOM</span>
    <content></content>
  `;
</script>

Örnek (özel öğeler): <x-tabs>, Gölge DOM'sinde <x-panel> alt öğe içeriyor. Her panel, h2 başlıkları içeren kendi gölge ağacına sahiptir. Ana sayfadaki başlıkların stilini belirlemek için kullanıcı şöyle yazabilir:

x-tabs::shadow x-panel::shadow h2 {
    ...
}

/deep/ birleştirici

/deep/ birleştirici, ::shadow'e benzer ancak daha güçlüdür. Tüm gölge sınırlarını tamamen yoksayar ve herhangi bir sayıda gölge ağacına geçer. Basitçe anlatırsak /deep/, bir öğenin iç organlarını ayrıntılı olarak incelemenizi ve herhangi bir düğümü hedeflemenizi sağlar.

/deep/ birleştirici, özellikle birden fazla gölge DOM düzeyine sahip olmanın yaygın olduğu özel öğeler dünyasında kullanışlıdır. Birincil örnekler, birkaç özel öğeyi iç içe yerleştirmek (her biri kendi gölge ağacını barındıran) veya <shadow> kullanarak başka bir öğeden devralan bir öğe oluşturmaktır.

Örnek (özel öğeler): Ağacın herhangi bir yerinde <x-tabs> öğesinin alt öğeleri olan tüm <x-panel> öğelerini seçin:

x-tabs /deep/ x-panel {
    ...
}

Örnek - gölge ağacının herhangi bir yerinde, .library-theme sınıfıyla tüm öğelerin stilini belirleme:

body /deep/ .library-theme {
    ...
}

querySelector() ile çalışma

.shadowRoot'in DOM tarama için gölge ağaçları açması gibi birleştiriciler de seçici tarama için gölge ağaçları açar. İç içe yerleştirilmiş bir zincir yazmak yerine tek bir ifade yazabilirsiniz:

// No fun.
document.querySelector('x-tabs').shadowRoot
        .querySelector('x-panel').shadowRoot
        .querySelector('#foo');

// Fun.
document.querySelector('x-tabs::shadow x-panel::shadow #foo');

Yerel öğeleri biçimlendirme

Yerel HTML kontrolleri, stil açısından zorlu bir konudur. Birçok kişi bu durumdan vazgeçip kendi çözümünü üretir. Ancak ::shadow ve /deep/ ile web platformundaki Gölge DOM kullanan tüm öğelere stil verilebilir. <input> türleri ve <video> bu tür örneklerin en iyileridir:

video /deep/ input[type="range"] {
  background: hotpink;
}

Stil kancaları oluşturma

Özelleştirme iyidir. Bazı durumlarda, Gölge'nizin stil korumasında delikler açmak ve diğer kullanıcıların stil oluşturması için kanca oluşturmak isteyebilirsiniz.

::shadow ve /deep/ kullanma

/deep/, çok güçlü bir araçtır. Bileşen yazarlarına, tek tek öğeleri stillenebilir veya bir dizi öğeyi temalanabilir olarak tanımlama olanağı sunar.

Örnek: Tüm gölge ağaçları yoksayılarak .library-theme sınıfına sahip tüm öğelere stil uygulama:

body /deep/ .library-theme {
    ...
}

Özel sözde öğeleri kullanma

Hem WebKit hem de Firefox, doğal tarayıcı öğelerinin dahili parçalarını biçimlendirmek için sözde öğeler tanımlar. Bunun iyi bir örneği input[type=range]. ::-webkit-slider-thumb öğesini hedefleyerek kaydırma çubuğu imlecini <span style="color:blue">blue</span> biçimlendirebilirsiniz:

input[type=range].custom::-webkit-slider-thumb {
  -webkit-appearance: none;
  background-color: blue;
  width: 10px;
  height: 40px;
}

Tarayıcıların bazı dahili öğelere stil ekleme kancaları sağlamasına benzer şekilde, gölge DOM içeriğinin yazarları da belirli öğeleri dışarıdan stillenebilir olarak belirleyebilir. Bu işlem özel sözde öğeler aracılığıyla yapılır.

pseudo özelliğini kullanarak bir öğeyi özel sözde öğe olarak tanımlayabilirsiniz. Değerinin veya adının başına "x-" eklenmelidir. Bu işlem, gölge ağacında söz konusu öğeyle ilişki oluşturur ve dışarıdan gelenlere gölge sınırını geçmeleri için özel bir şerit verir.

Özel bir kaydırma çubuğu widget'ı oluşturma ve kullanıcıların kaydırma çubuğu düğmesini mavi olarak biçimlendirmesine izin verme örneğini aşağıda bulabilirsiniz:

<style>
  #host::x-slider-thumb {
    background-color: blue;
  }
</style>
<div id="host"></div>
<script>
  var root = document.querySelector('#host').createShadowRoot();
  root.innerHTML = `
    <div>
      <div pseudo="x-slider-thumb"></div>' +
    </div>
  `;
</script>

CSS Değişkenlerini Kullanma

Tema bağlantıları oluşturmanın güçlü bir yolu CSS değişkenleri kullanmaktır. Diğer kullanıcıların doldurması için "stil yer tutucuları" oluşturmaktır.

Gölge DOM'unda değişken yer tutucuları işaretleyen bir özel öğe yazarı düşünün. Biri dahili düğmenin yazı tipini, diğeri de rengini biçimlendirmek için:

button {
  color: var(--button-text-color, pink); /* default color will be pink */
  font-family: var(--button-font);
}

Ardından, öğeyi yerleştiren kişi bu değerleri kendi istediği şekilde tanımlar. Belki de kendi sayfalarının süper havalı Comic Sans temasına uymak için:

#host {
  --button-text-color: green;
  --button-font: "Comic Sans MS", "Comic Sans", cursive;
}

CSS değişkenlerinin devralma şekli nedeniyle her şey yolunda ve bu işlem mükemmel şekilde çalışıyor. Büyük resmin tamamı şu şekildedir:

<style>
  #host {
    --button-text-color: green;
    --button-font: "Comic Sans MS", "Comic Sans", cursive;
  }
</style>
<div id="host">Host node</div>
<script>
  var root = document.querySelector('#host').createShadowRoot();
  root.innerHTML = `
    <style>
      button {
        color: var(--button-text-color, pink);
        font-family: var(--button-font);
      }
    </style>
    <content></content>
  `;
</script>

Stilleri sıfırlama

Yazı tipleri, renkler ve satır yükseklikleri gibi devralınabilir stiller, Gölge DOM'daki öğeleri etkilemeye devam eder. Ancak maksimum esneklik için Gölge DOM, gölge sınırında neler olduğunu kontrol etmemizi sağlayan resetStyleInheritance mülkünü bize sunar. Bu özelliği, yeni bir bileşen oluştururken sıfırdan başlamanın bir yolu olarak düşünebilirsiniz.

resetStyleInheritance

Aşağıda, gölge ağacının resetStyleInheritance değerinin değiştirilmesinden nasıl etkilendiğini gösteren bir demo verilmiştir:

<div>
  <h3>Light DOM</h3>
</div>

<script>
  var root = document.querySelector('div').createShadowRoot();
  root.resetStyleInheritance = <span id="code-resetStyleInheritance">false</span>;
  root.innerHTML = `
    <style>
      h3 {
        color: red;
      }
    </style>
    <h3>Shadow DOM</h3>
    <content select="h3"></content>
  `;
</script>

<div class="demoarea" style="width:225px;">
  <div id="style-ex-inheritance"><h3 class="border">Light DOM</div>
</div>
<div id="inherit-buttons">
  <button id="demo-resetStyleInheritance">resetStyleInheritance=false</button>
</div>

<script>
  var container = document.querySelector('#style-ex-inheritance');
  var root = container.createShadowRoot();
  //root.resetStyleInheritance = false;
  root.innerHTML = '<style>h3{ color: red; }</style><h3>Shadow DOM<content select="h3"></content>';

  document.querySelector('#demo-resetStyleInheritance').addEventListener('click', function(e) {
    root.resetStyleInheritance = !root.resetStyleInheritance;
    e.target.textContent = 'resetStyleInheritance=' + root.resetStyleInheritance;
    document.querySelector('#code-resetStyleInheritance').textContent = root.resetStyleInheritance;
  });
</script>
DevTools&#39;un devralınan mülkleri

.resetStyleInheritance değerini anlamak biraz daha zordur. Bunun başlıca nedeni, yalnızca devralınabilen CSS özelliklerini etkilemesidir. Burada, devralınacak bir özellik ararken sayfa ile ShadowRoot arasındaki sınırda, değerleri ana makineden devralmayın, bunun yerine initial değerini kullanın (CSS spesifikasyonuna göre) şeklinde bir ifade yer alıyor.

CSS'de hangi özelliklerin devralındığından emin değilseniz bu kullanışlı listeye göz atın veya Öğe panelinde "Devralınanları göster" onay kutusunu etkinleştirin.

Dağıtılmış düğümlerin stilini belirleme

Dağıtılmış düğümler, bir giriş noktasında (<content> öğesi) oluşturulan öğelerdir. <content> öğesi, ışık DOM'dan düğümler seçmenize ve bunları gölge DOM'unuzdaki önceden tanımlanmış konumlarda oluşturmanıza olanak tanır. Mantıksal olarak Gölge DOM'da değillerdir; hâlâ ana öğenin alt öğeleridirler. Ekleme noktaları yalnızca oluşturma işlemiyle ilgilidir.

Dağıtılan düğümler ana dokümandaki stilleri korur. Yani ana sayfadaki stil kuralları, bir ekleme noktasında oluşturulsalar bile öğelere uygulanmaya devam eder. Burada da, dağıtılmış düğümler mantıksal olarak hâlâ ışık alanındadır ve hareket etmezler. Yalnızca başka bir yerde oluşturulur. Ancak düğümler Gölge DOM'a dağıtıldığında, gölge ağacında tanımlanan ek stilleri alabilirler.

::content sözde öğesi

Dağıtılmış düğümler, barındırma öğesinin alt öğeleridir. Peki bunları Gölge DOM'nin içinden nasıl hedefleyebiliriz? Yanıt, CSS ::content sözde öğesidir. Bir ekleme noktasından geçen ışık DOM düğümlerini hedeflemenin bir yoludur. Örneğin:

::content > h3, bir ekleme noktasından geçen tüm h3 etiketlerine stil uygular.

Bir örnekle açıklayalım:

<div>
  <h3>Light DOM</h3>
  <section>
    <div>I'm not underlined</div>
    <p>I'm underlined in Shadow DOM!</p>
  </section>
</div>

<script>
var div = document.querySelector('div');
var root = div.createShadowRoot();
root.innerHTML = `
  <style>
    h3 { color: red; }
      content[select="h3"]::content > h3 {
      color: green;
    }
    ::content section p {
      text-decoration: underline;
    }
  </style>
  <h3>Shadow DOM</h3>
  <content select="h3"></content>
  <content select="section"></content>
`;
</script>

Ekleme noktalarındaki stilleri sıfırlama

ShadowRoot oluştururken devralınan stilleri sıfırlayabilirsiniz. <content> ve <shadow> kampanya noktalarında da bu seçenek bulunur. Bu öğeleri kullanırken JS'de .resetStyleInheritance özelliğini ayarlayın veya öğenin kendisinde boole reset-style-inheritance özelliğini kullanın.

  • ShadowRoot veya <shadow> ekleme noktaları için: reset-style-inheritance, devralınabilir CSS özelliklerinin, gölge içeriğinize ulaşmadan önce barındırıcıda initial olarak ayarlandığı anlamına gelir. Bu konum, üst sınır olarak bilinir.

  • <content> kampanya noktası için: reset-style-inheritance, devralınabilir CSS özelliklerinin, ana makinenin alt öğeleri kampanya noktasında dağıtılmadan önce initial olarak ayarlandığı anlamına gelir. Bu konum, alt sınır olarak bilinir.

Sonuç

Özel öğelerin yazarları olarak, içeriğimizin görünümünü ve tarzını kontrol etmek için çok sayıda seçeneğimiz vardır. Gölge DOM, bu cesur yeni dünyanın temelini oluşturuyor.

Gölge DOM, bize kapsamlı stil kapsüllemesi ve dış dünyaya istediğimiz kadar (veya az) izin vermemiz için bir araç sunar. Yazarlar, özel sözde öğeler tanımlayarak veya CSS değişken yer tutucuları ekleyerek içeriklerini daha da özelleştirmek için üçüncü taraflara uygun stil kancaları sağlayabilir. Özetlemek gerekirse, web yazarları içeriklerinin nasıl temsil edileceği üzerinde tam kontrole sahiptir.