Gölge DOM 201

CSS ve stil

Bu makalede, Shadow DOM ile yapabileceğiniz diğer şaşırtıcı işlemler hakkında daha fazla bilgi verilmektedir. Bu makale, Gölge DOM 101 başlıklı makalede ele alınan kavramları temel alır. 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 önceden tahmin etti ve bizi ortada 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 deyişle:

<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çicisiyle eşleşen ve bu nedenle kırmızı stil uygulanan tek öğe ShadowRoot'taki öğedir. 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ı.

Sonuç olarak ne öğrendiniz? Dış dünyadan stil kapsüllememiz var. Teşekkürler Gölge DOM.

Barındıran öğeye stil uygulama

: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 bir ShadowRoot bağlamında çalışır. Bu nedenle, Shadow 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>

Öğelere tema uygulama

: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.

Tek bir gölge kökten 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'unda <x-panel> alt öğeye sahiptir. Her panel, h2 başlıkları içeren kendi gölge ağacına sahiptir. Ana sayfadaki bu başlıklar için stil belirlemek isterseniz şunları yazabilirsiniz:

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 söylemek gerekirse /deep/, bir öğenin içlerine inip herhangi bir düğümü hedeflemenize olanak tanır.

/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. En iyi örnekler arasında, bir grup özel öğeyi iç içe yerleştirme (her biri kendi gölge ağacını barındırır) veya <shadow> kullanarak başka bir öğeden devralan bir öğe oluşturma yer alı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ındaki herhangi bir yerde .library-theme sınıfına sahip tüm öğelere stil uygulayın:

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 örneklere örnek gösterilebilir:

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ına stil uygulamak 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 belirleyebilirsiniz. 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üğümlere stil uygulama

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ılmış düğümler, ana belgedeki stilleri korur. Yani ana sayfadaki stil kuralları, bir ekleme noktasında oluşturulsalar bile öğelere uygulanmaya devam eder. Yine de dağıtılmış düğümler mantıksal olarak light DOM'da kalır ve hareket etmez. 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 ana öğenin alt öğeleridir. Peki bunları Gölge DOM'dan 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>

Giriş noktalarındaki stilleri sıfırlama

ShadowRoot oluştururken devralınan stilleri sıfırlayabilirsiniz. <content> ve <shadow> ekleme noktalarında da bu seçenek mevcuttur. Bu öğeleri kullanırken JS'de .resetStyleInheritance değerini ayarlayın veya öğenin kendisinde doğru/yanlış 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 konuma üst sınır denir.

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

Sonuç

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

Gölge DOM, kapsamlı stil kapsayıcılığı ve dış dünyadan istediğimiz kadar (veya az) içeriği almamızı sağlar. 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.