Giriş
Web Bileşenleri, aşağıdaki özellikleri sunan bir dizi ileri düzey standarttır:
- Widget oluşturmayı mümkün kılma
- …ve güvenilir bir şekilde yeniden kullanılabilir.
- …ve bileşenin bir sonraki sürümü dahili uygulama ayrıntılarını değiştirirse sayfaları bozmaz.
Bu, HTML/JavaScript'i ne zaman kullanacağınıza ve Web Bileşenleri'ni 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 yararlanmak mantıklıdır. Web Bileşenleri standartları, bunu yapmanıza yardımcı olmak için tasarlanmıştır.
Ancak HTML ve JavaScript'ten oluşturulan widget'ların kullanımını zorlaştıran temel bir sorun vardır: 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 yanlışlıkla widget'ın içindeki bölümlere uygulanabileceği, JavaScript'inizin yanlışlıkla widget'ın içindeki bölümleri değiştirebileceği, kimliklerinizin widget'ın içindeki kimliklerle çakışabileceği vb. anlamına gelir.
Web Bileşenleri üç bölümden oluşur:
Gölge DOM, DOM ağacı kapsülleme sorununu giderir. Web Bileşenleri'nin dört parçası birlikte çalışacak şekilde tasarlanmıştır ancak Web Bileşenleri'nin hangi parçalarını kullanacağınızı da seçebilirsiniz. Bu eğitimde, gölge DOM'un nasıl kullanılacağı gösterilmektedir.
Merhaba Gölge Dünya
Shadow DOM ile öğeler, kendileriyle ilişkili yeni bir düğüm türü alabilir. Bu yeni düğüm türüne gölge kök adı verilir. Kendisiyle ilişkili bir gölge kökü olan öğeye gölge ana makinesi 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ın görünümü
<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>
Ayrıca, sayfadaki JavaScript, düğmenin textContent değerini sorduğunda "こんにちは、影の世界!" değil, "Hello, world!" yanıtını alır. Bunun nedeni, gölge kökün altındaki DOM alt ağacının kapsüllenmiş olmasıdır.
İçeriği sunumdan ayırma
Şimdi, içeriği sunumdan ayırmak için gölge DOM'un kullanımına bakacağız. Şöyle bir yaka kartımız olduğunu 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 yazacağınız şey bu. Gölge DOM kullanmaz:
<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ığından ad etiketinin tüm yapısı dokümana gösterilir. Sayfadaki diğer öğeler stil veya komut dosyası oluşturma için yanlışlıkla aynı sınıf adlarını kullanırsa sorun yaşarız.
Kötü bir deneyim yaşamaktan kaçınabiliriz.
1. adım: Sunu ayrıntılarını gizleyin
Anlamsal olarak muhtemelen yalnızca şunları önemseriz:
- Bu bir ad etiketi.
- Adı "Bob".
İlk olarak, istediğimiz gerçek semantiğe 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 yalnızca "Bob" oluşturulur. Sunum amaçlı DOM öğelerini bir <template> öğesinin içine taşıdığımız için bunlar oluşturulmaz ancak JavaScript'ten erişilebilir. Şimdi gölge kökü doldurmak için
bunu 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ğumuz için ad etiketi tekrar oluşturulur. Ad etiketini sağ tıklayıp öğeyi incelerseniz hoş ve anlamsal yapı işaretlemesi görürsünüz:
<div id="nameTag">Bob</div>
Bu, gölge DOM kullanarak ad etiketinin sunum ayrıntılarını dokümandan gizlediğimizi gösterir. Sunum ayrıntıları, gölge DOM'da kapsüllenir.
2. adım: İçeriği sunumdan ayırın
Ad etiketimiz artık sunum ayrıntılarını sayfadan gizliyor ancak sunumu içerikten gerçekten ayırmıyor. Çünkü içerik ("Bob" adı) sayfada olsa da oluşturulan ad, gölge köke kopyaladığımız addır. Yaka kartındaki adı değiştirmek istersek bunu iki yerde yapmamız gerekir ve bu iki yer senkronize olmayabilir.
HTML öğeleri birleştirilebilir. Örneğin, bir tabloya düğme ekleyebilirsiniz. Burada ihtiyacımız olan şey oluşturmadır: Ad etiketi; kırmızı arka plan, "Merhaba!" metni ve ad etiketindeki içeriğin bir bileşimi olmalıdır.
Bileşen yazarı olarak siz, <content> adlı yeni bir öğeyi kullanarak bileşimin widget'ınızla nasıl çalıştığını tanımlarsınız. Bu, widget'ın sunumunda bir ekleme noktası oluşturur ve ekleme noktası, o noktada sunulacak gölge ana makineden içerik seçer.
Gölge DOM'daki işaretlemeyi şu şekilde değiştirirsek:
<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.
Ad yalnızca bir yerde (dokümanda) yer aldığı için dokümanın yapısı artık daha basit. Sayfanızın kullanıcının adını güncellemesi gerekirse şunu yazmanız yeterlidir:
document.querySelector('#nameTag').textContent = 'Shellie';
Hepsi bu kadar. Ad etiketinin oluşturulması, tarayıcı tarafından otomatik olarak güncellenir. Bunun nedeni, ad etiketinin içeriğini <content> ile yerine yansıtıyor olmamızdır.
<div id="ex2b">
Artık içerik ve sunumu birbirinden ayırdık. İçerik, dokümanda yer alır. Sunum ise gölge DOM'da bulunur. Bir öğenin oluşturulma zamanı geldiğinde tarayıcı tarafından otomatik olarak senkronize edilirler.
3. adım: Kâr
İçeriği ve sunumu ayırarak içeriği işleyen kodu
basitleştirebiliriz. Ad etiketi örneğinde, bu
kodun birden fazla yerine yalnızca
bir <div> içeren basit bir yapıyla ilgilenmesi yeterlidir.
Artık sunumumuzu değiştirirsek kodun hiçbirini değiştirmemiz gerekmez.
Örneğin, ad etiketimizi yerelleştirmek istediğimizi varsayalım. Bu hâlâ bir ad etiketidir. Bu nedenle, dokümandaki semantik içerik değişmez:
<div id="nameTag">Bob</div>
Gölge kök kurulum kodu aynı kalır. Yalnızca gölge köke eklenenler 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>
Bu, web'deki mevcut duruma kıyasla büyük bir gelişmedir. Ad güncelleme kodunuz, basit ve tutarlı olan bileşenin yapısına bağlı olabilir. Ad güncelleme kodunuzun, oluşturma için kullanılan yapıyı bilmesi gerekmez. Oluşturulan metne baktığımızda, adın İngilizcede ikinci sırada ("Hi! Ancak Japoncada önce ad, sonra "と申します" ifadesi gelir. Bu fark, gösterilen adın güncellenmesi açısından anlamsal olarak önemsizdir. Bu nedenle, ad güncelleme kodunun bu ayrıntıyı bilmesi gerekmez.
Ekstra kredi: Gelişmiş projeksiyon
Yukarıdaki örnekte, <content> öğesi gölge ana makinedeki tüm içeriği seçer. select özelliğini kullanarak bir içerik öğesinin neyi yansıtacağını kontrol edebilirsiniz. Birden fazla içerik öğesi de kullanabilirsiniz.
Örneğin, aşağıdaki metni 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ü vardır:
<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şir. Bob'un e-posta adresi kaç kez ve hangi renklerde görünüyor?
Cevap: Bob'un e-posta adresi bir kez görünür ve sarı renktedir.
Bunun nedeni, Gölge DOM'u hack'leyenlerin bildiği gibi, ekranda gerçekte oluşturulanların ağacını oluşturmanın büyük bir parti gibi olmasıdır. İçerik öğesi, dokümandaki içeriğin sahne arkasındaki gölge DOM oluşturma partisine girmesine izin veren davetiyedir. Bu davetiyeler sırayla gönderilir. Davetiyeyi kimin alacağı, kime gönderildiğine (yani select özelliğine) bağlıdır. İçerik, davet edildikten sonra her zaman daveti kabul eder (kim etmez ki?!) ve gönderilir. Bu adrese tekrar davetiye gönderilirse evde kimse olmadığı için partinize katılamaz.
Yukarıdaki örnekte <div class="email">, hem div seçiciyi hem de .email seçiciyi eşleştirir. Ancak div seçiciye sahip içerik öğesi dokümanda daha önce geldiği için <div class="email"> sarı partiye gider ve mavi partiye kimse katılamaz. (Bu yüzden bu kadar mavi olabilir
. Ancak dertler paylaştıkça azalır, bu yüzden ne olacağı belli
olmaz.)
Bir öğe hiçbir tarafa davet edilmezse hiç oluşturulmaz. İlk örnekteki "Hello, world" metninde de bu durum yaşandı. Bu, tamamen farklı bir oluşturma işlemi gerçekleştirmek istediğinizde kullanışlıdır: Dokümandaki anlamsal modeli yazın (sayfadaki komut dosyalarının erişebileceği model budur) ancak oluşturma amacıyla gizleyin ve JavaScript kullanarak Shadow DOM'da tamamen farklı bir oluşturma modeline bağlayın.
Örneğin, HTML'de güzel bir tarih seçici vardır. <input
type="date"> yazarsanız düzgün bir pop-up takvim görürsünüz. Peki ya kullanıcının tatlı adası tatili için bir tarih aralığı seçmesine izin vermek isterseniz? (Bilirsiniz, Red Vines'dan yapılmış hamaklar falan.) Dokümanınızı şu şekilde ayarlarsınız:
<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 bir gölge DOM oluşturur. 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 dokümana neden etiket ekledim? Bunun nedeni, kullanıcı formu Shadow DOM'u desteklemeyen bir tarayıcıyla görüntülerse formun yine de kullanılabilir olmasıdır, sadece o kadar güzel görünmez. Kullanıcı şuna benzer bir şey 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çtiniz
Gölge DOM ile ilgili temel bilgiler bu kadar. Gölge DOM 101'i başarıyla tamamladınız. Gölge DOM ile daha fazlasını yapabilirsiniz. Örneğin, bir gölge ana makinesinde birden fazla gölge veya kapsülleme için iç içe yerleştirilmiş gölgeler kullanabilir ya da sayfanızı Model Tabanlı Görünümler (MDV) ve Gölge DOM kullanarak tasarlayabilirsiniz. Web Bileşenleri, Gölge DOM'dan daha fazlasıdır.
Bunları sonraki yayınlarda açıklayacağız.