Giriş
Web Bileşenleri, aşağıdakileri sağlayan son teknoloji standartlardan oluşan bir settir:
- Widget oluşturmayı mümkün kılmak
- …güvenilir bir şekilde yeniden kullanılabilir.
- ...ve bileşenin sonraki sürümünde dahili uygulama ayrıntıları değişirse sayfalar bozulmaz.
Bu, HTML/JavaScript'i ve Web Bileşenlerini ne zaman kullanacağınıza karar vermeniz gerektiği anlamına mı geliyor? Hayır HTML ve JavaScript, etkileşimli görsel öğeler oluşturabilir. Widget'lar etkileşimli görsel öğelerdir. Widget geliştirirken HTML ve JavaScript becerilerinizden yararlanmanız faydalı olacaktır. Web Bileşenleri standartları, bunu yapmanıza yardımcı olacak şekilde tasarlanmıştır.
Ancak HTML ve JavaScript'den oluşturulan widget'ların kullanımını zorlaştıran temel bir sorun vardır: Bir widget'ın içindeki DOM ağacı, sayfanın geri kalanından kapsüllenmez. Bu kapsülleme eksikliği, doküman stil sayfanızın widget'ın içindeki bölümlere yanlışlıkla uygulanabileceği, JavaScript'inizin widget'ın içindeki bölümleri yanlışlıkla değiştirebileceği, kimliklerinizin widget'ın içindeki kimliklerle çakışma ihtimalinin olduğu vb. anlamına gelir.
Web bileşenleri üç bölümden oluşur:
Gölge DOM, DOM ağacı kapsülleme sorununu ele alır. Web bileşenlerinin dört bölümü birlikte çalışacak şekilde tasarlanmıştır ancak Web bileşenlerinin hangi bölümlerinin kullanılacağını seçebilirsiniz. Bu eğiticide, Gölge DOM'un nasıl kullanılacağı gösterilmektedir.
Merhaba, Shadow World
Gölge DOM ile öğeler, kendileriyle ilişkilendirilmiş yeni bir tür düğüm alabilir. Bu yeni düğüm türüne gölge kök denir. Kendisiyle ilişkili bir gölge kökü olan öğelere gölge ana makine denir. Gölge ana makinenin içeriği oluşturulmaz, bunun yerine gölge kökün içeriği oluşturulur.
Örneğin, aşağıdaki gibi bir işaretlemeniz varsa:
<button>Hello, world!</button>
<script>
var host = document.querySelector('button');
var root = host.createShadowRoot();
root.textContent = 'こんにちは、影の世界!';
</script>
yerine
<button id="ex1a">Hello, world!</button>
<script>
function remove(selector) {
Array.prototype.forEach.call(
document.querySelectorAll(selector),
function (node) { node.parentNode.removeChild(node); });
}
if (!HTMLElement.prototype.createShadowRoot) {
remove('#ex1a');
document.write('<img src="SS1.png" alt="Screenshot of a button with \'Hello, world!\' on it.">');
}
</script>
Sayfanız aşağıdaki gibi görünüyor
<button id="ex1b">Hello, world!</button>
<script>
(function () {
if (!HTMLElement.prototype.createShadowRoot) {
remove('#ex1b');
document.write('<img src="SS2.png" alt="Screenshot of a button with \'Hello, shadow world!\' in Japanese on it.">');
return;
}
var host = document.querySelector('#ex1b');
var root = host.createShadowRoot();
root.textContent = 'こんにちは、影の世界!';
})();
</script>
Bununla da kalmaz, sayfadaki JavaScript düğmenin textContent
değerini sorarsa "こんにちは、影の世界!" yerine "Hello, world!" değerini alır. Bunun nedeni, gölge kökün altındaki DOM alt ağacının kapsüllenmiş olmasıdır.
İçeriği Sunudan Ayırma
Şimdi içeriği sunumdan ayırmak için Gölge DOM'u kullanmaya göz atacağız. Şu ad etiketine sahip olduğumuzu varsayalım:
<style>
.ex2a.outer {
border: 2px solid brown;
border-radius: 1em;
background: red;
font-size: 20pt;
width: 12em;
height: 7em;
text-align: center;
}
.ex2a .boilerplate {
color: white;
font-family: sans-serif;
padding: 0.5em;
}
.ex2a .name {
color: black;
background: white;
font-family: "Marker Felt", cursive;
font-size: 45pt;
padding-top: 0.2em;
}
</style>
<div class="ex2a outer">
<div class="boilerplate">
Hi! My name is
</div>
<div class="name">
Bob
</div>
</div>
İşaretleme aşağıda verilmiştir. Bugün şunu yazarsınız. Gölge DOM kullanılmaz:
<style>
.outer {
border: 2px solid brown;
border-radius: 1em;
background: red;
font-size: 20pt;
width: 12em;
height: 7em;
text-align: center;
}
.boilerplate {
color: white;
font-family: sans-serif;
padding: 0.5em;
}
.name {
color: black;
background: white;
font-family: "Marker Felt", cursive;
font-size: 45pt;
padding-top: 0.2em;
}
</style>
<div class="outer">
<div class="boilerplate">
Hi! My name is
</div>
<div class="name">
Bob
</div>
</div>
DOM ağacında kapsülleme olmadığı için ad etiketinin tüm yapısı belgeye açıktır. Sayfadaki diğer öğeler, stil veya komut dosyası oluşturma için yanlışlıkla aynı sınıf adlarını kullanırsa çok zamanınız olur.
Böylece kötü zamanlardan kaçınabiliriz.
1. Adım: Sunu Ayrıntılarını Gizleme
Anlamsal olarak muhtemelen yalnızca şunu önemsiyoruz:
- Bu bir isim etiketi.
- Adı "Barış".
Öncelikle, istediğimiz gerçek semantiklere daha yakın bir işaretleme yazarız:
<div id="nameTag">Bob</div>
Ardından, sunu için kullanılan tüm stilleri ve div'leri bir <template>
öğesine yerleştiririz:
<div id="nameTag">Bob</div>
<template id="nameTagTemplate">
<span class="unchanged"><style>
.outer {
border: 2px solid brown;
… same as above …
</style>
<div class="outer">
<div class="boilerplate">
Hi! My name is
</div>
<div class="name">
Bob
</div>
</div></span>
</template>
Bu noktada "Bob" oluşturulan tek şeydir. Sunu amaçlı DOM öğelerini bir <template>
öğesinin içine taşıdığımız için bu öğeler oluşturulmaz ancak JavaScript'den erişilebilir. Gölge kökü doldurmak için bunu şu anda yapıyoruz:
<script>
var shadow = document.querySelector('#nameTag').createShadowRoot();
var template = document.querySelector('#nameTagTemplate');
var clone = document.importNode(template.content, true);
shadow.appendChild(clone);
Gölge kök oluşturduğumuza göre ad etiketi tekrar oluşturulur. Ad etiketini sağ tıklayıp öğeyi incelerseniz bunun semantik işaretleme olduğunu görürsünüz:
<div id="nameTag">Bob</div>
Bu, Gölge DOM'u kullanarak ad etiketinin sunum ayrıntılarını dokümandan gizlediğimizi gösterir. Gösterim ayrıntıları, Gölge DOM'da kapsüllenir.
2. Adım: İçeriği sunudan ayırın
Ad etiketimiz artık sunum ayrıntılarını sayfadan gizliyor ancak aslında sunumu içerikten ayırmıyor. Çünkü içerik ("Barış" adı) sayfadaysa da oluşturulan ad, gölge köke kopyaladığımız addır. Ad etiketindeki adı değiştirmek istediğimizde bunu iki yerde yapmamız gerekecekti ve bu da senkronize olamayabilir.
HTML öğeleri, kompozisyoneldir. Örneğin, bir tablonun içine bir düğme yerleştirebilirsiniz. Burada kompozisyona ihtiyacımız var: Ad etiketi, kırmızı arka plan, "Merhaba" metni ve ad etiketindeki içeriğin bir kompozisyonu olmalıdır.
Bileşenin yazarı olarak, <content>
adlı yeni bir öğeyi kullanarak bileşimin widget'ınızla nasıl çalışacağını tanımlarsınız. Bu işlem, widget'ın sunumunda bir ekleme noktası oluşturur ve ekleme noktası, bu noktada gösterilecek içeriği gölge ana makineden seçer.
Gölge DOM'daki işaretlemeyi şu şekilde değiştirirseniz:
<span class="unchanged"><template id="nameTagTemplate">
<style>
…
</style></span>
<div class="outer">
<div class="boilerplate">
Hi! My name is
</div>
<div class="name">
<content></content>
</div>
</div>
<span class="unchanged"></template></span>
Ad etiketi oluşturulduğunda, gölge ana makinenin içeriği <content>
öğesinin göründüğü noktaya yansıtılır.
Artık ad yalnızca dokümanda bulunduğundan dokümanın yapısı daha basittir. Sayfanızda kullanıcının adını güncellemeniz gerekirse şunları yazmanız yeterlidir:
document.querySelector('#nameTag').textContent = 'Shellie';
Hepsi bu kadar. Ad etiketi içeriğini <content>
ile doğru şekilde yansıttığımız için ad etiketinin oluşturulması tarayıcı tarafından otomatik olarak güncellenir.
<div id="ex2b">
Artık içerik ve sunumu ayırdık. İçerik dokümanda, sunu ise Gölge DOM'dadır. Bir öğe oluşturulacağı zaman tarayıcılar tarafından otomatik olarak senkronize edilirler.
3. adım: Kâr elde edin
İçeriği ve sunumu ayırarak içeriği işleyen kodu basitleştirebiliriz. Ad etiketi örneğinde, bu kodun birkaç yerine yalnızca bir <div>
içeren basit bir yapıyla ilgilenmesi gerekir.
Artık sunumuzu değiştirdiğimizde kodda herhangi bir değişiklik yapmamız gerekmiyor.
Örneğin, ad etiketimizi yerelleştirmek istediğimizi varsayalım. Ad etiketi hâlâ aynı olduğundan dokümandaki anlamsal içerik değişmez:
<div id="nameTag">Bob</div>
Gölge kök kurulum kodu aynı kalır. Gölge köke nelerin yerleştirildiği değişir:
<template id="nameTagTemplate">
<style>
.outer {
border: 2px solid pink;
border-radius: 1em;
background: url(sakura.jpg);
font-size: 20pt;
width: 12em;
height: 7em;
text-align: center;
font-family: sans-serif;
font-weight: bold;
}
.name {
font-size: 45pt;
font-weight: normal;
margin-top: 0.8em;
padding-top: 0.2em;
}
</style>
<div class="outer">
<div class="name">
<content></content>
</div>
と申します。
</div>
</template>
Ad güncelleme kodunuz, basit ve tutarlı olan bileşenin yapısına bağlı olabileceğinden bu, günümüzde web'de mevcut olan duruma kıyasla büyük bir gelişmedir. Ad güncelleme kodunuzun oluşturma için kullanılan yapıyı bilmesi gerekmez. Oluşturulan içeriği göz önünde bulundurursak ad, İngilizce olarak ikinci sırada görünür ("Merhaba! Benim adım") ama ilk olarak Japoncada ( tag申ます kavramından önce). Bu ayrım, gösterilen adın güncellenmesi açısından anlam açısından anlamsızdır. Bu nedenle ad güncelleme kodunun bu ayrıntıdan haberdar olması gerekmez.
Ek Kredi: Gelişmiş Projeksiyon
Yukarıdaki örnekte, <content>
öğesi gölge ana makinedeki tüm içerikleri seçmektedir. select
özelliğini kullanarak bir içerik öğesinin neyi yansıtacağını kontrol edebilirsiniz. Birden fazla içerik öğesi de
kullanabilirsiniz.
Örneğin, şunu içeren bir dokümanınız varsa:
<div id="nameTag">
<div class="first">Bob</div>
<div>B. Love</div>
<div class="email">bob@</div>
</div>
ve belirli içerikleri seçmek için CSS seçicileri kullanan bir gölge kökü:
<div style="background: purple; padding: 1em;">
<div style="color: red;">
<content **select=".first"**></content>
</div>
<div style="color: yellow;">
<content **select="div"**></content>
</div>
<div style="color: blue;">
<content **select=".email">**</content>
</div>
</div>
<div class="email">
öğesi hem <content select="div">
hem de <content
select=".email">
öğeleriyle eşleştirilir. Bora'nın e-posta adresi kaç kez ve hangi renklerde gösteriliyor?
Cevap, Bora'nın e-posta adresinin bir kez ve sarı renkte görünmesidir.
Bunun nedeni, Shadow DOM'ye saldırı yapan kişilerin bildiği gibi ekranda gösterilenin
ağaçını oluşturmak büyük bir parti gibidir. İçerik öğesi, dokümandaki içeriğin sahne arkası Gölge DOM oluşturma partisine girmesine olanak tanıyan davettir. Bu davetiyeler sırayla gönderilir. Davetiyenin kime gönderildiğine (yani select
özelliğine) bağlıdır. Davet edilen içerikler her zaman daveti kabul eder (kim kabul etmez ki?) ve çalışmaya başlar. Bu adrese tekrar bir davetiye gönderilirse
evde kimse olmaz ve o kişi size ulaşmaz.
Yukarıdaki örnekte <div class="email">
, hem div
hem de .email
seçiciyle eşleşir, ancak div
seçiciye sahip içerik öğesi dokümanda daha önce geldiğinden <div class="email">
sarı tarafa gider ve mavi partiye kimse gelmeyecektir. (Bu neden olabilir. Ancak dert paylaştıkça azalır.)
Bir öğe hiçbir partiye davet edilmezse hiç oluşturulmaz. İlk örnekteki "Merhaba dünya" metninde de bu işlem uygulanmıştır. Bu, tamamen farklı bir oluşturma işlemi gerçekleştirmek istediğinizde yararlıdır: Anlamsal modeli belgeye yazın. Bu model, sayfadaki komut dosyalarının erişebileceği modeldir ancak oluşturma amacıyla gizlenir ve JavaScript kullanılarak Shadow DOM'da gerçekten farklı bir oluşturma modeline bağlanır.
Örneğin, HTML'de kullanışlı bir tarih seçici vardır. <input
type="date">
yazarsanız şık bir pop-up takvim görürsünüz. Ancak kullanıcının tatlı ada tatili (Red Vines'den yapılmış hamaklarla) için bir tarih aralığı seçmesine izin vermek istiyorsanız ne olur? Belgenizi şu şekilde oluşturursunuz:
<div class="dateRangePicker">
<label for="start">Start:</label>
<input type="date" name="startDate" id="start">
<br>
<label for="end">End:</label>
<input type="date" name="endDate" id="end">
</div>
Ancak, tarih aralığını vurgulayan şık bir takvim oluşturmak için tablo kullanan Shadow DOM oluşturun. Kullanıcı takvimdeki günleri tıkladığında bileşen, startDate ve endDate girişlerindeki durumu günceller. Kullanıcı formu gönderdiğinde bu giriş öğelerindeki değerler gönderilir.
Oluşturulmayacaksa neden dokümana etiketler ekledim? Bunun nedeni, kullanıcı formu Gölge DOM'u desteklemeyen bir tarayıcıyla görüntülerse, form kadar güzel olmasa da kullanılabilir durumda olmasıdır. Kullanıcı şuna benzer bir sayfa görür:
<div class="dateRangePicker">
<label for="start">Start:</label>
<input type="date" name="startDate" id="start">
<br>
<label for="end">End:</label>
<input type="date" name="endDate" id="end">
</div>
Gölge DOM 101'i Geçirsiniz
Gölge DOM'un temelleri budur. Gölge DOM 101'i iletebilirsiniz. Gölge DOM ile daha fazlasını yapabilirsiniz. Örneğin, bir gölge ana makinesinde birden fazla gölge kullanabilir, kapsayıcı için iç içe gölgeler kullanabilir veya Model Odaklı Görünümler (MDV) ve Gölge DOM'u kullanarak sayfanızı tasarlayabilirsiniz. Web bileşenleri, gölge DOM'dan daha fazlasıdır.
Bu konuları daha sonraki yayınlarda açıklayacağız.