Gölge DOM 301

İleri düzey kavramlar ve DOM API'leri

Bu makalede Shadow DOM ile yapabileceğiniz diğer muhteşem şeyler anlatılmaktadır. Bu sürüm, Gölge DOM 101 ve Gölge DOM 201'de açıklanan kavramları temel alır.

Birden çok gölge kökü kullanma

Bir parti düzenliyorsanız herkes aynı odaya sığarsa havalar karışır. Kullanıcı gruplarını birden çok odaya dağıtma seçeneğini kullanmak istiyorsunuz. Gölge DOM barındıran öğeler de bunu yapabilir. Diğer bir deyişle, aynı anda birden fazla gölge kökü barındırabilirler.

Bir ana bilgisayara birden çok gölge kökü eklemeye çalışırsak ne olacağına bakalım:

<div id="example1">Light DOM</div>
<script>
  var container = document.querySelector('#example1');
  var root1 = container.createShadowRoot();
  var root2 = container.createShadowRoot();
  root1.innerHTML = '<div>Root 1 FTW</div>';
  root2.innerHTML = '<div>Root 2 FTW</div>';
</script>

Zaten bir gölge ağacı eklemiş olmamıza rağmen, oluşturulan öğe "Kök 2 FTW" olur. Bunun nedeni, ana makineye eklenen son gölge ağacının kazanmasıdır. Oluşturma söz konusu olduğunda bu, bir LIFO yığınıdır. Geliştirici Araçları'nı incelemek, bu davranışı doğrular.

Öyleyse oluşturan tarafa yalnızca son gölgenin davet edilmesi durumunda birden fazla gölge kullanmanın anlamı nedir? Gölge ekleme noktaları girin.

Gölge Ekleme Noktaları

"Gölge ekleme noktaları" (<shadow>), yer tutucu olmaları nedeniyle normal ekleme noktalarına (<content>) benzer. Ancak ana makinenin içeriğinin yer tutucusu değil, diğer gölge ağaçları için ana makine görevi görürler. Gölge DOM Başlangıcı!

Tahmin edebileceğiniz gibi, tavşan deliğinin derinliklerini kazıdıkça işler daha karmaşık hale gelir. Bu nedenle, spesifikasyonda birden fazla <shadow> öğesi olduğunda ne olacağı oldukça nettir:

Baştaki örneğimize döndüğümüzde, ilk gölge (root1) davet listesinden çıkarılmış. <shadow> kampanya noktası eklendiğinde nokta geri getirilir:

<div id="example2">Light DOM</div>
<script>
var container = document.querySelector('#example2');
var root1 = container.createShadowRoot();
var root2 = container.createShadowRoot();
root1.innerHTML = '<div>Root 1 FTW</div><content></content>';
**root2.innerHTML = '<div>Root 2 FTW</div><shadow></shadow>';**
</script>

Bu örnekle ilgili birkaç ilginç nokta var:

  1. "Kök 2 FTW", "Kök 1 FTW" üzerinde oluşturulmaya devam eder. Bu durum, <shadow> ekleme noktasını nereye yerleştirdiğimiz için geçerlidir. Tersini istiyorsanız ekleme noktasını taşıyın: root2.innerHTML = '<shadow></shadow><div>Root 2 FTW</div>';.
  2. Şu anda root1 konumunda bir <content> ekleme noktası olduğuna dikkat edin. Bu, "Light DOM" metin düğümünün oluşturma yolculuğu için birlikte gelmesini sağlar.

<shadow> ürününde ne oluşturulur?

Bazen eski gölge ağacının <shadow> konumunda oluşturulacağını bilmek yararlı olur. Bu ağacın referansını .olderShadowRoot üzerinden alabilirsiniz:

**root2.olderShadowRoot** === root1 //true

Ana makinenin gölge kökünü alma

Bir öğe Gölge DOM barındırıyorsa .shadowRoot kullanarak en genç gölge köküne erişebilirsiniz:

var root = host.createShadowRoot();
console.log(host.shadowRoot === root); // true
console.log(document.body.shadowRoot); // null

İnsanların gölgelerinize geçmesinden endişe ediyorsanız .shadowRoot özelliğini null olacak şekilde yeniden tanımlayın:

Object.defineProperty(host, 'shadowRoot', {
  get: function() { return null; },
  set: function(value) { }
});

Küçük bir ipucu gibi ama işe yarıyor. Son olarak, Shadow DOM'un bir güvenlik özelliği olarak tasarlanmadığını unutmayın. İçeriğin tamamen izole edilmesi için bu özelliğe güvenmeyin.

JS'de Gölge DOM Oluşturma

JS'de DOM oluşturmayı tercih ederseniz HTMLContentElement ve HTMLShadowElement bunun için arayüzlere sahiptir.

<div id="example3">
  <span>Light DOM</span>
</div>
<script>
var container = document.querySelector('#example3');
var root1 = container.createShadowRoot();
var root2 = container.createShadowRoot();

var div = document.createElement('div');
div.textContent = 'Root 1 FTW';
root1.appendChild(div);

 // HTMLContentElement
var content = document.createElement('content');
content.select = 'span'; // selects any spans the host node contains
root1.appendChild(content);

var div = document.createElement('div');
div.textContent = 'Root 2 FTW';
root2.appendChild(div);

// HTMLShadowElement
var shadow = document.createElement('shadow');
root2.appendChild(shadow);
</script>

Bu örnek, bir önceki bölümde verilen örnekle neredeyse aynıdır. Tek fark, yeni eklenen <span> öğesini çekmek için artık select kullanıyorum.

Ekleme noktalarıyla çalışma

Barındırıcı öğesinin dışında seçilen ve gölge ağacına "dağıtılan" düğümlere...drumroll...drumroll...dağıtılmış düğümler denir. Ekleme noktaları onları davet ettiğinde gölge sınırını geçmelerine izin verilir.

Ekleme noktalarının DOM'yi fiziksel olarak taşımaması gibi kavramsal olarak tuhaf olan nokta. Ana makinenin düğümleri değişmeden kalır. Ekleme noktaları, düğümleri yalnızca ana makineden gölge ağacına yeniden yansıtır. Bu bir sunu/oluşturma öğesidir: "Bu düğümleri buraya taşı" "Bu düğümleri bu konumda oluştur."

Örneğin:

<div><h2>Light DOM</h2></div>
<script>
var root = document.querySelector('div').createShadowRoot();
root.innerHTML = '<content select="h2"></content>';

var h2 = document.querySelector('h2');
console.log(root.querySelector('content[select="h2"] h2')); // null;
console.log(root.querySelector('content').contains(h2)); // false
</script>

Ve işte! h2, gölge DOM'un alt öğesi değil. Bu da bizi bir başka gelişmeye götürüyor:

Element.getDistributedNodes()

Bir <content> hücresine geçiş yapamıyoruz ancak .getDistributedNodes() API, bir ekleme noktasında dağıtılmış düğümleri sorgulamamıza imkan tanır:

<div id="example4">
  <h2>Eric</h2>
  <h2>Bidelman</h2>
  <div>Digital Jedi</div>
  <h4>footer text</h4>
</div>

<template id="sdom">
  <header>
    <content select="h2"></content>
  </header>
  <section>
    <content select="div"></content>
  </section>
  <footer>
    <content select="h4:first-of-type"></content>
  </footer>
</template>

<script>
var container = document.querySelector('#example4');

var root = container.createShadowRoot();

var t = document.querySelector('#sdom');
var clone = document.importNode(t.content, true);
root.appendChild(clone);

var html = [];
[].forEach.call(root.querySelectorAll('content'), function(el) {
  html.push(el.outerHTML + ': ');
  var nodes = el.getDistributedNodes();
  [].forEach.call(nodes, function(node) {
    html.push(node.outerHTML);
  });
  html.push('\n');
});
</script>

Element.getDestinationInsertionPoints()

.getDistributedNodes() ile benzer şekilde, bir düğümün .getDestinationInsertionPoints() çağrısını yaparak bir düğümün hangi ekleme noktalarına dağıtıldığını kontrol edebilirsiniz:

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

<script>
  var container = document.querySelector('div');

  var root1 = container.createShadowRoot();
  var root2 = container.createShadowRoot();
  root1.innerHTML = '<content select="h2"></content>';
  root2.innerHTML = '<shadow></shadow>';

  var h2 = document.querySelector('#host h2');
  var insertionPoints = h2.getDestinationInsertionPoints();
  [].forEach.call(insertionPoints, function(contentEl) {
    console.log(contentEl);
  });
</script>

Araç: Gölge DOM Görselleştirici

Gölge DOM olan kara büyüyü anlamak zordur. İlk defa başımı tabletin etrafına sarmaya çalıştığımı hatırlıyorum.

Gölge DOM oluşturmanın nasıl çalıştığını görselleştirmeye yardımcı olmak için d3.js kullanan bir araç geliştirdim. Sol taraftaki her iki işaretleme kutusu da düzenlenebilir. Kendi işaretlemenizi yapıştırabilir ve öğelerin nasıl çalıştığını görmek için denemeler yapabilirsiniz.

Gölge DOM Görselleştirici
Gölge DOM Görselleştirici'yi Başlat

Deneyin ve düşüncelerinizi bizimle paylaşın.

Etkinlik Modeli

Bazı etkinlikler gölge sınırını geçer, bazıları geçmez. Etkinliklerin sınırı geçtiği durumlarda, gölge kökünün üst sınırının sağladığı kapsüllemeyi korumak için etkinlik hedefi ayarlanır. Yani etkinlikler, Gölge DOM'a giden dahili öğelerden değil, barındıran öğeden gelmiş gibi görünecek şekilde yeniden hedeflenir.

İşlem 1'i oynat

  • Bu soru ilginç. Barındırma öğesinden (<div data-host>) mavi düğüme bir mouseout görmeniz gerekir. Dağıtılmış bir düğüm olsa da ShadowDOM'de değil, ana makinededir. Fareyi tekrar sarıya doğru daha aşağı taşımak mavi düğümde mouseout olmasına neden olur.

İşlem 2'yi oynat

  • Ana makinede görünen bir mouseout var (en sonda). Normalde mouseout etkinliklerinin tüm sarı bloklar için tetiklendiğini görürsünüz. Ancak bu durumda, bu öğeler Gölge DOM'un içindedir ve etkinlik, üst sınırından baloncuk olarak çıkmaz.

İşlem 3'ü oynat

  • Girişi tıkladığınızda focusin öğesinin girişte değil, ana makine düğümünün kendisinde göründüğüne dikkat edin. Yeniden hedeflendi!

Her zaman durdurulan etkinlikler

Aşağıdaki etkinlikler hiçbir zaman gölge sınırını geçmez:

  • bırak yapma
  • hata
  • seç
  • değiştir
  • yük
  • reset
  • resize
  • scroll
  • başlangıcı seç

Sonuç

Gölge DOM'un son derece güçlü olduğuna katılacağınızı umuyoruz. İlk kez, fazladan <iframe> veya diğer eski teknikler olmadan uygun bir kapsülleme işlemi gerçekleştirdik.

Gölge DOM kesinlikle karmaşık bir canavar ancak web platformuna eklemeye değer bir canavar. Biraz vakit ayırın. Öğrenin. Soru sorun.

Daha fazla bilgi edinmek isterseniz Dominic'in Gölge DOM 101 giriş makalesine ve Gölge DOM 201: CSS ve Stil başlıklı makalemize göz atın.