Özel öğeler, web geliştiricilerinin yeni HTML etiketleri tanımlamasına, mevcut etiketleri genişletmesine ve yeniden kullanılabilir web bileşenleri oluşturmasına olanak tanır.
Özel Öğeler sayesinde web geliştiricileri yeni HTML etiketleri oluşturabilir, mevcut HTML etiketlerini güçlendirebilir veya diğer geliştiricilerin yazdığı bileşenleri genişletebilir. API, web bileşenlerinin temelini oluşturur. Vanilla JS/HTML/CSS'den başka bir şey kullanmadan yeniden kullanılabilir bileşenler oluşturmak için web standartlarına dayalı bir yol sunar. Sonuç olarak daha az kod, modüler kod ve uygulamalarımızda daha fazla yeniden kullanım elde edilir.
Giriş
Tarayıcı, web uygulamalarını yapılandırmak için bize mükemmel bir araç sunuyor. Buna HTML denir. Bunu duymuş olabilirsiniz. Bildirim temelli, taşınabilir, iyi desteklenir ve kolayca kullanılabilir. HTML harika olsa da kelime hazinesi ve genişletilebilirliği sınırlıdır. HTML yaşam standardı şimdiye kadar JS davranışını işaretlemenizle otomatik olarak ilişkilendirecek bir yönteme sahipti.
Özel öğeler HTML'yi modernleştirme, eksik parçaları doldurma ve yapıyı davranışla gruplandırma çözümleridir. HTML bir sorunun çözümünü sağlamıyorsa, bu çözümü sağlayan özel bir öğe oluşturabiliriz. Özel öğeler, HTML'nin avantajlarını korurken tarayıcıya yeni püf noktalarını öğretir.
Yeni öğe tanımlama
Yeni bir HTML öğesi tanımlamak için JavaScript'in gücüne ihtiyacımız vardır!
customElements
global, özel bir öğe tanımlamak ve tarayıcıya yeni bir etiketi öğretmek için kullanılır. Oluşturmak istediğiniz etiket adıyla ve temel HTMLElement
öğesini genişleten bir JavaScript class
ile customElements.define()
çağırın.
Örnek - bir mobil çekmece paneli tanımlama, <app-drawer>
:
class AppDrawer extends HTMLElement {...}
window.customElements.define('app-drawer', AppDrawer);
// Or use an anonymous class if you don't want a named constructor in current scope.
window.customElements.define('app-drawer', class extends HTMLElement {...});
Örnek kullanım:
<app-drawer></app-drawer>
Özel öğe kullanmanın, <div>
veya başka bir öğe kullanmaktan farklı olmadığını unutmayın. Örnekler sayfada bildirilebilir, JavaScript'te dinamik olarak oluşturulabilir, etkinlik işleyiciler eklenebilir vb. Daha fazla örnek için okumaya devam edin.
Bir öğenin JavaScript API'sini tanımlama
Özel öğelerin işlevi, HTMLElement
ile başlayan bir ES2015 class
kullanılarak tanımlanır. HTMLElement
genişletildiğinde özel öğe, tüm DOM API'yi devralır ve sınıfa eklediğiniz tüm özellikler/yöntemler öğenin DOM arayüzünün parçası haline gelir. Esas olarak, bu sınıfı etiketiniz için genel bir JavaScript API oluşturmak amacıyla kullanın.
Örnek - <app-drawer>
öğesinin DOM arayüzünü tanımlama:
class AppDrawer extends HTMLElement {
// A getter/setter for an open property.
get open() {
return this.hasAttribute('open');
}
set open(val) {
// Reflect the value of the open property as an HTML attribute.
if (val) {
this.setAttribute('open', '');
} else {
this.removeAttribute('open');
}
this.toggleDrawer();
}
// A getter/setter for a disabled property.
get disabled() {
return this.hasAttribute('disabled');
}
set disabled(val) {
// Reflect the value of the disabled property as an HTML attribute.
if (val) {
this.setAttribute('disabled', '');
} else {
this.removeAttribute('disabled');
}
}
// Can define constructor arguments if you wish.
constructor() {
// If you define a constructor, always call super() first!
// This is specific to CE and required by the spec.
super();
// Setup a click listener on <app-drawer> itself.
this.addEventListener('click', e => {
// Don't toggle the drawer if it's disabled.
if (this.disabled) {
return;
}
this.toggleDrawer();
});
}
toggleDrawer() {
// ...
}
}
customElements.define('app-drawer', AppDrawer);
Bu örnekte, open
özelliği, disabled
özelliği ve toggleDrawer()
yöntemi bulunan bir çekmece oluşturuyoruz. Ayrıca, özellikleri HTML özellikleri olarak yansıtır.
Özel öğelerin bir özelliği, sınıf tanımı içindeki this
öğesinin DOM öğesinin kendisine başvuruda bulunmasıdır, yani sınıfın örneğine. Örneğimizde this
, <app-drawer>
anlamına gelir. Öğe bu şekilde bir click
işleyicisi ekleyebilir. Ayrıca etkinlik işleyicilerle sınırlı değilsiniz.
DOM API'sinin tamamı öğe kodunun içinde bulunur. Öğenin özelliklerine erişmek, alt öğelerini (this.children
), sorgu düğümlerini (this.querySelectorAll('.items')
) vb. incelemek için this
öğesini kullanın.
Özel öğe oluşturma kuralları
- Özel öğe adı kısa çizgi (-) içermelidir. Dolayısıyla
<x-tags>
,<my-element>
ve<my-awesome-app>
adları geçerliyken<tabs>
ve<foo_bar>
geçerli değildir. Bu şart, HTML ayrıştırıcısının özel öğeleri normal öğelerden ayırt edebilmesidir. Ayrıca, HTML'ye yeni etiketler eklendiğinde yönlendirme uyumluluğu da sağlanır. - Aynı etiketi bir defadan fazla kaydedemezsiniz. Bunu yapmaya çalışırsanız
DOMException
döndürülür. Tarayıcıya yeni bir etiketi bildirdikten sonra, ilgili işlem biter. Geri alma yok. - HTML yalnızca birkaç öğenin kendi kendine kapanmasına izin verdiği için özel öğeler kendiliğinden kapanamaz. Her zaman bir kapanış etiketi
(
<app-drawer></app-drawer>
) yazın.
Özel öğe tepkileri
Özel öğeler, ilginç zamanlarda kod çalıştırmak için özel yaşam döngüsü kancaları tanımlayabilir. Bunlara özel öğe reaksiyonları adı verilir.
Ad | Şu durumda aranır: |
---|---|
constructor |
Öğenin bir örneği oluşturulur veya yükseltilir. Durumu başlatmak, etkinlik işleyicileri ayarlamak veya gölge alanı oluşturmak için kullanışlıdır.
constructor içinde yapabileceklerinizle ilgili kısıtlamalar için
spesifikasyonları
inceleyin.
|
connectedCallback |
Öğe DOM'ye her eklendiğinde çağrılır. Kaynak getirme veya oluşturma gibi kurulum kodlarını çalıştırmak için kullanışlıdır. Genellikle bu zamana kadar işi ertelemeye çalışmalısınız. |
disconnectedCallback |
Öğe DOM'den her kaldırıldığında çağrılır. Temizleme kodu çalıştırmak için faydalıdır. |
attributeChangedCallback(attrName, oldVal, newVal) |
Gözlemlenen özellik eklendiğinde, kaldırıldığında, güncellendiğinde veya değiştirildiğinde çağrılır. Ayrıştırıcı tarafından bir öğe oluşturulduğunda ilk değerler için de çağrılır veya yeni sürüme geçirilir. Not: Bu geri çağırmayı yalnızca observedAttributes özelliğinde listelenen özellikler alır.
|
adoptedCallback |
Özel öğe yeni bir document öğesine (ör. document.adoptNode(el) adlı bir kullanıcı) taşındı.
|
Tepki geri çağırmaları eşzamanlıdır. Birisi öğenizde el.setAttribute()
çağrısı yaparsa tarayıcı hemen attributeChangedCallback()
yöntemini çağırır.
Benzer şekilde, öğeniz DOM'den kaldırıldıktan hemen sonra bir disconnectedCallback()
alırsınız (ör. kullanıcı el.remove()
çağrısı yapar).
Örnek: <app-drawer>
öğesine özel öğe tepkileri ekleme:
class AppDrawer extends HTMLElement {
constructor() {
super(); // always call super() first in the constructor.
// ...
}
connectedCallback() {
// ...
}
disconnectedCallback() {
// ...
}
attributeChangedCallback(attrName, oldVal, newVal) {
// ...
}
}
Gerektiğinde/mantıklı olduğunda tepkileri tanımlayın. Öğeniz yeterince karmaşıksa ve connectedCallback()
içinde IndexedDB'ye bağlantı açarsa disconnectedCallback()
içinde gerekli temizleme işlemini yapın. Ancak dikkatli olun! Öğenizin her durumda DOM'dan kaldırılmasına güvenemezsiniz. Örneğin, kullanıcı sekmeyi kapatırsa disconnectedCallback()
hiçbir zaman çağrılmaz.
Özellikler ve özellikler
Özellikleri özelliklere yansıtma
HTML özelliklerinin değerlerini HTML özelliği olarak DOM'a geri yansıtması yaygın bir durumdur. Örneğin, JS'de hidden
veya id
değerleri değiştirildiğinde:
div.id = 'my-id';
div.hidden = true;
değerler, canlı DOM'ye özellik olarak uygulanır:
<div id="my-id" hidden>
Buna "özellikleri özelliklere yansıtma" denir. HTML'deki neredeyse her özellik bunu yapar. Neden? Özellikler, bir öğeyi bildirimli şekilde yapılandırmak için de faydalıdır. Erişilebilirlik ve CSS seçiciler gibi belirli API'ler, çalışmak için özelliklere ihtiyaç duyar.
Öğenin DOM gösterimini JavaScript durumuyla senkronize etmek istediğiniz her yerde bir özelliği yansıtmak işinize yarayabilir. Bir özelliği yansıtmak istemenizin nedenlerinden biri, JS durumu değiştiğinde kullanıcı tanımlı stilin uygulanmasıdır.
<app-drawer>
ürünümüzü tekrar gözden geçirin. Bu bileşenin bir tüketicisi, devre dışı bırakıldığında karartmak ve/veya kullanıcı etkileşimini engellemek isteyebilir:
app-drawer[disabled] {
opacity: 0.5;
pointer-events: none;
}
JS'de disabled
özelliği değiştirildiğinde, kullanıcının seçicisinin eşleşmesi için bu özelliğin DOM'ye eklenmesini isteriz. Öğe, değeri aynı ada sahip bir özelliğe yansıtarak bu davranışı sağlayabilir:
get disabled() {
return this.hasAttribute('disabled');
}
set disabled(val) {
// Reflect the value of `disabled` as an attribute.
if (val) {
this.setAttribute('disabled', '');
} else {
this.removeAttribute('disabled');
}
this.toggleDrawer();
}
Özelliklerde yapılan değişiklikleri gözlemleme
HTML özellikleri, kullanıcıların başlangıç durumunu bildirmesinin kolay bir yoludur:
<app-drawer open disabled></app-drawer>
Öğeler, bir attributeChangedCallback
tanımlayarak özellik değişikliklerine tepki verebilir. Tarayıcı, observedAttributes
dizisinde listelenen özelliklerde yapılan her değişiklik için bu yöntemi çağırır.
class AppDrawer extends HTMLElement {
// ...
static get observedAttributes() {
return ['disabled', 'open'];
}
get disabled() {
return this.hasAttribute('disabled');
}
set disabled(val) {
if (val) {
this.setAttribute('disabled', '');
} else {
this.removeAttribute('disabled');
}
}
// Only called for the disabled and open attributes due to observedAttributes
attributeChangedCallback(name, oldValue, newValue) {
// When the drawer is disabled, update keyboard/screen reader behavior.
if (this.disabled) {
this.setAttribute('tabindex', '-1');
this.setAttribute('aria-disabled', 'true');
} else {
this.setAttribute('tabindex', '0');
this.setAttribute('aria-disabled', 'false');
}
// TODO: also react to the open attribute changing.
}
}
Örnekte, bir disabled
özelliği değiştirildiğinde <app-drawer>
için ek özellikler ayarlıyoruz. Bunu burada yapmasak da bir JS mülkünün özelliğiyle senkronize bir durumda tutmak için attributeChangedCallback
öğesini de kullanabilirsiniz.
Öğe yükseltmeleri
Kademeli olarak geliştirilmiş HTML
Özel öğelerin customElements.define()
çağrısıyla tanımlandığını daha önce öğrendik. Ancak bu, özel bir öğeyi tek seferde tanımlamanız ve kaydetmeniz gerektiği anlamına gelmez.
Özel öğeler, tanımları kaydedilmeden önce kullanılabilir.
Progresif geliştirme, özel öğelerin bir özelliğidir. Diğer bir deyişle, sayfada birçok <app-drawer>
öğesi tanımlayabilir ve customElements.define('app-drawer', ...)
öğesini çok ileri bir zamana kadar asla çağırmayabilirsiniz. Bunun nedeni, tarayıcının potansiyel özel öğeleri bilinmeyen etiketler sayesinde farklı şekilde işlemesidir. define()
yöntemini çağırma ve mevcut bir öğeyi sınıf tanımıyla ekleme işlemine "öğe yükseltmeleri" denir.
Bir etiket adının ne zaman tanımlandığını öğrenmek için window.customElements.whenDefined()
kullanabilirsiniz. Öğe tanımlı hale geldiğinde çözümlenen bir Promise döndürür.
customElements.whenDefined('app-drawer').then(() => {
console.log('app-drawer defined');
});
Örnek - bir alt öğe grubu yeni sürüme geçirilene kadar çalışmayı erteleme
<share-buttons>
<social-button type="twitter"><a href="...">Twitter</a></social-button>
<social-button type="fb"><a href="...">Facebook</a></social-button>
<social-button type="plus"><a href="...">G+</a></social-button>
</share-buttons>
// Fetch all the children of <share-buttons> that are not defined yet.
let undefinedButtons = buttons.querySelectorAll(':not(:defined)');
let promises = [...undefinedButtons].map((socialButton) => {
return customElements.whenDefined(socialButton.localName);
});
// Wait for all the social-buttons to be upgraded.
Promise.all(promises).then(() => {
// All social-button children are ready.
});
Öğe tanımlı içerik
Özel öğeler, öğe kodu içindeki DOM API'lerini kullanarak kendi içeriklerini yönetebilir. Tepkiler bu açıdan faydalıdır.
Örnek: Varsayılan HTML ile bir öğe oluşturma:
customElements.define('x-foo-with-markup', class extends HTMLElement {
connectedCallback() {
this.innerHTML = "<b>I'm an x-foo-with-markup!</b>";
}
// ...
});
Bu etiketi bildirdiğinizde şunlar olur:
<x-foo-with-markup>
<b>I'm an x-foo-with-markup!</b>
</x-foo-with-markup>
// YAPILACAKLAR: DevSite - Kod örneği, satır içi etkinlik işleyicileri kullandığı için kaldırıldı
Gölge DOM kullanan bir öğe oluşturma
Gölge DOM, bir öğenin sayfanın geri kalanından ayrı bir şekilde bir DOM parçasına sahip olması, bu parçayı oluşturması ve biçimlendirmesi için bir yol sunar. Hatta bir uygulamanın tamamını tek bir etiket içinde gizleyebilirsiniz:
<!-- chat-app's implementation details are hidden away in Shadow DOM. -->
<chat-app></chat-app>
Gölge DOM'u özel bir öğede kullanmak için constructor
içinde this.attachShadow
çağrısı yapın:
let tmpl = document.createElement('template');
tmpl.innerHTML = `
<style>:host { ... }</style> <!-- look ma, scoped styles -->
<b>I'm in shadow dom!</b>
<slot></slot>
`;
customElements.define('x-foo-shadowdom', class extends HTMLElement {
constructor() {
super(); // always call super() first in the constructor.
// Attach a shadow root to the element.
let shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.appendChild(tmpl.content.cloneNode(true));
}
// ...
});
Örnek kullanım:
<x-foo-shadowdom>
<p><b>User's</b> custom text</p>
</x-foo-shadowdom>
<!-- renders as -->
<x-foo-shadowdom>
#shadow-root
<b>I'm in shadow dom!</b>
<slot></slot> <!-- slotted content appears here -->
</x-foo-shadowdom>
Kullanıcının özel metni
// YAPILACAKLAR: DevSite - Kod örneği, satır içi etkinlik işleyicileri kullandığı için kaldırıldı
<template>
öğesinden öğe oluşturma
Tanımadığınız kişiler için <template>
öğesi; ayrıştırılan, sayfa yüklenirken durağan ve çalışma zamanında daha sonra etkinleştirilebilen DOM parçalarını bildirmenizi sağlar. Web bileşenleri ailesindeki başka bir
temel API öğesi. Şablonlar, özel bir öğenin yapısını belirtmek için ideal bir yer tutucudur.
Örnek: <template>
'den oluşturulmuş Gölge DOM içeriğine sahip bir öğe kaydetme:
<template id="x-foo-from-template">
<style>
p { color: green; }
</style>
<p>I'm in Shadow DOM. My markup was stamped from a <template>.</p>
</template>
<script>
let tmpl = document.querySelector('#x-foo-from-template');
// If your code is inside of an HTML Import you'll need to change the above line to:
// let tmpl = document.currentScript.ownerDocument.querySelector('#x-foo-from-template');
customElements.define('x-foo-from-template', class extends HTMLElement {
constructor() {
super(); // always call super() first in the constructor.
let shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.appendChild(tmpl.content.cloneNode(true));
}
// ...
});
</script>
Bu birkaç satırlık kod çok etkili. Neler olduğunu anlamaya çalışalım:
- HTML'de yeni bir öğe tanımlıyoruz:
<x-foo-from-template>
- Öğenin Gölge DOM'u, bir
<template>
- Gölge DOM sayesinde öğenin DOM'u öğenin yereldir
- Öğenin dahili CSS'si, Gölge DOM sayesinde öğeye ayarlanır
Gölge DOM'deyim. İşaretlemem bir <template> ile damgalandı.
// YAPILACAKLAR: DevSite - Kod örneği, satır içi etkinlik işleyicileri kullandığı için kaldırıldı
Özel öğelerin stilini belirleme
Öğeniz Gölge DOM kullanarak kendi stilini tanımlasa bile kullanıcılar, özel öğenizin stilini kendi sayfalarından belirleyebilir. Bunlara "kullanıcı tanımlı stiller" denir.
<!-- user-defined styling -->
<style>
app-drawer {
display: flex;
}
panel-item {
transition: opacity 400ms ease-in-out;
opacity: 0.3;
flex: 1;
text-align: center;
border-radius: 50%;
}
panel-item:hover {
opacity: 1.0;
background: rgb(255, 0, 255);
color: white;
}
app-panel > panel-item {
padding: 5px;
list-style: none;
margin: 0 7px;
}
</style>
<app-drawer>
<panel-item>Do</panel-item>
<panel-item>Re</panel-item>
<panel-item>Mi</panel-item>
</app-drawer>
Öğenin Gölge DOM içinde tanımlanmış stilleri varsa CSS özgünlüğünün nasıl çalıştığını kendinize soruyor olabilirsiniz. Belirginlik açısından, kullanıcı stilleri kazanır. Bunlar her zaman öğe tanımlı stilleri geçersiz kılar. Gölge DOM kullanan bir öğe oluşturma bölümüne bakın.
Kayıtlı olmayan öğelerin önceden stilini ayarlama
Bir öğe yeni sürüme geçirilmeden önce :defined
sözde sınıfını kullanarak CSS'de hedefleyebilirsiniz. Bu, bir bileşenin önceden stilini belirlemek için yararlı olur. Örneğin, tanımlanmamış bileşenleri gizleyerek ve tanımlandıklarında gizlenerek düzen veya diğer görsel FOUC'leri önlemek isteyebilirsiniz.
Örnek - tanımlamadan önce <app-drawer>
öğesini gizleyin:
app-drawer:not(:defined) {
/* Pre-style, give layout, replicate app-drawer's eventual styles, etc. */
display: inline-block;
height: 100vh;
opacity: 0;
transition: opacity 0.3s ease-in-out;
}
<app-drawer>
tanımlandıktan sonra seçici (app-drawer:not(:defined)
) eşleşmez.
Öğe uzatma
Custom Elements API, yeni HTML öğeleri oluşturmak için kullanışlı olduğu gibi, diğer özel öğelerin, hatta tarayıcının yerleşik HTML'sinin genişletilmesinde de işe yarar.
Özel öğe genişletme
Başka bir özel öğe, sınıf tanımı genişletilerek genişletilir.
Örnek - <app-drawer>
aralığını kapsayan <fancy-app-drawer>
oluşturma:
class FancyDrawer extends AppDrawer {
constructor() {
super(); // always call super() first in the constructor. This also calls the extended class' constructor.
// ...
}
toggleDrawer() {
// Possibly different toggle implementation?
// Use ES2015 if you need to call the parent method.
// super.toggleDrawer()
}
anotherMethod() {
// ...
}
}
customElements.define('fancy-app-drawer', FancyDrawer);
Yerel HTML öğelerini genişletme
Daha ilgi çekici bir <button>
oluşturmak istediğinizi varsayalım. <button>
davranışını ve işlevini tekrarlamak yerine, özel öğeler kullanarak mevcut öğeyi kademeli olarak geliştirmek daha iyi bir seçenektir.
Özelleştirilmiş yerleşik öğe, tarayıcının yerleşik HTML etiketlerinden birini genişleten özel bir öğedir. Mevcut bir öğeyi genişletmenin birincil avantajı, tüm özelliklerini (DOM özellikleri, yöntemler, erişilebilirlik) elde etmektir. Progresif web uygulaması yazmanın mevcut HTML öğelerini kademeli olarak geliştirmekten daha iyi bir yolu yoktur.
Bir öğeyi genişletmek için, doğru DOM arayüzünden devralan bir sınıf tanımı oluşturmanız gerekir. Örneğin, <button>
öğesini genişleten özel bir öğenin HTMLElement
yerine HTMLButtonElement
öğesinden devralması gerekir.
Benzer şekilde, <img>
öğesini genişleten bir öğenin HTMLImageElement
öğesini genişletmesi gerekir.
Örnek - <button>
kapsamının genişletilmesi:
// See https://html.spec.whatwg.org/multipage/indices.html#element-interfaces
// for the list of other DOM interfaces.
class FancyButton extends HTMLButtonElement {
constructor() {
super(); // always call super() first in the constructor.
this.addEventListener('click', e => this.drawRipple(e.offsetX, e.offsetY));
}
// Material design ripple animation.
drawRipple(x, y) {
let div = document.createElement('div');
div.classList.add('ripple');
this.appendChild(div);
div.style.top = `${y - div.clientHeight/2}px`;
div.style.left = `${x - div.clientWidth/2}px`;
div.style.backgroundColor = 'currentColor';
div.classList.add('run');
div.addEventListener('transitionend', (e) => div.remove());
}
}
customElements.define('fancy-button', FancyButton, {extends: 'button'});
Bir yerel öğeyi genişletirken define()
çağrısının biraz değiştiğine dikkat edin. Zorunlu üçüncü parametre, tarayıcıya hangi etiketi genişlettiğinizi bildirir. Birçok HTML etiketi aynı DOM arayüzünü paylaştığından bu gereklidir. <section>
, <address>
ve <em>
(diğerleriyle birlikte)
HTMLElement
paylaşır; hem <q>
hem de <blockquote>
HTMLQuoteElement
paylaşır; vb.
{extends: 'blockquote'}
belirtildiğinde tarayıcı, <q>
yerine bu tür bir <blockquote>
oluşturduğunuzu anlar. HTML'nin DOM arayüzlerinin tam listesi için HTML spesifikasyonuna bakın.
Özelleştirilmiş bir yerleşik öğeyi kullananlar bunu birkaç şekilde kullanabilir. Yerel etikete is=""
özelliğini ekleyerek bunu bildirebilir:
<!-- This <button> is a fancy button. -->
<button is="fancy-button" disabled>Fancy button!</button>
JavaScript'te bir örnek oluşturun:
// Custom elements overload createElement() to support the is="" attribute.
let button = document.createElement('button', {is: 'fancy-button'});
button.textContent = 'Fancy button!';
button.disabled = true;
document.body.appendChild(button);
veya new
operatörünü kullanın:
let button = new FancyButton();
button.textContent = 'Fancy button!';
button.disabled = true;
Burada, <img>
tarihini kapsayan başka bir örneği inceleyebilirsiniz.
Örnek - <img>
kapsamının genişletilmesi:
customElements.define('bigger-img', class extends Image {
// Give img default size if users don't specify.
constructor(width=50, height=50) {
super(width * 10, height * 10);
}
}, {extends: 'img'});
Kullanıcılar bu bileşeni şu şekilde tanımlar:
<!-- This <img> is a bigger img. -->
<img is="bigger-img" width="15" height="20">
veya JavaScript'te bir örnek oluşturun:
const BiggerImage = customElements.get('bigger-img');
const image = new BiggerImage(15, 20); // pass constructor values like so.
console.assert(image.width === 150);
console.assert(image.height === 200);
Çeşitli ayrıntılar
Bilinmeyen öğeler ile tanımlanmamış özel öğelerin karşılaştırması
HTML üzerinde çalışmak esnek ve esnektir. Örneğin, bir sayfada <randomtagthatdoesntexist>
kodunu belirttiğinizde tarayıcı bunu kabul eder. Standart olmayan etiketler neden çalışır? Cevap, HTML spesifikasyonunun buna izin vermesidir. Spesifikasyon tarafından tanımlanmayan öğeler HTMLUnknownElement
olarak ayrıştırılır.
Aynı durum özel öğeler için geçerli değildir. Potansiyel özel öğeler, geçerli bir adla ("-" içerir) oluşturulurlarsa HTMLElement
olarak ayrıştırılır. Bunu, özel öğeleri destekleyen bir tarayıcıda kontrol edebilirsiniz. Konsolu çalıştırın:
Ctrl+Üst Karakter+J (veya Mac'te Cmd+Opt+J) ve aşağıdaki
kod satırlarını yapıştırın:
// "tabs" is not a valid custom element name
document.createElement('tabs') instanceof HTMLUnknownElement === true
// "x-tabs" is a valid custom element name
document.createElement('x-tabs') instanceof HTMLElement === true
API referansı
customElements
global, özel öğelerle çalışmak için faydalı yöntemleri tanımlar.
define(tagName, constructor, options)
Tarayıcıda yeni bir özel öğe tanımlar.
Örnek
customElements.define('my-app', class extends HTMLElement { ... });
customElements.define(
'fancy-button', class extends HTMLButtonElement { ... }, {extends: 'button'});
get(tagName)
Geçerli bir özel öğe etiketi adı verildiğinde, öğe oluşturucuyu döndürür.
Hiçbir öğe tanımı kaydedilmemişse undefined
değerini döndürür.
Örnek
let Drawer = customElements.get('app-drawer');
let drawer = new Drawer();
whenDefined(tagName)
Özel öğe tanımlandığında çözümlenen bir Promise döndürür. Öğe zaten tanımlanmışsa hemen çözün. Etiket adı geçerli bir özel öğe adı değilse reddeder.
Örnek
customElements.whenDefined('app-drawer').then(() => {
console.log('ready!');
});
Geçmiş ve tarayıcı desteği
Son birkaç yıldır web bileşenlerini takip ediyorsanız Chrome 36 ve sonraki sürümlerde, Custom Elements API'nin customElements.define()
yerine document.registerElement()
kullanan bir sürümünün kullanıldığını anlayacaksınız. Bu artık standardın v0 adı verilen kullanımdan kaldırılmış bir sürümü olarak kabul edilir.
customElements.define()
yeni ve tarayıcı tedarikçilerinin uygulamaya başladığı yeni bir özelliktir. Adı, Custom Elements v1.
Eski v0 spesifikasyonuyla ilgileniyorsanız html5rocks makalesine göz atın.
Tarayıcı desteği
Chrome 54 (durum), Safari 10.1 (durum) ve Firefox 63 (durum) Custom Elements v1'e sahiptir. Edge geliştirmeye başladı.
Özel öğe algılama özelliğini kullanmak için window.customElements
öğesinin mevcut olup olmadığını kontrol edin:
const supportsCustomElementsV1 = 'customElements' in window;
Polyester Lifi
Tarayıcı desteği yaygın bir şekilde kullanıma sunulana kadar, Custom Elements v1 için bağımsız bir çoklu dolgu olacak. Ancak, web bileşenleri çoklu dolgularını en iyi şekilde yüklemek için webcomponents.js loader'ını kullanmanızı öneririz. Yükleyici, yalnızca tarayıcının ihtiyaç duyduğu pozitif dolguları eşzamansız olarak yüklemek için özellik algılamayı kullanır.
Yükleyin:
npm install --save @webcomponents/webcomponentsjs
Kullanım:
<!-- Use the custom element on the page. -->
<my-element></my-element>
<!-- Load polyfills; note that "loader" will load these async -->
<script src="node_modules/@webcomponents/webcomponentsjs/webcomponents-loader.js" defer></script>
<!-- Load a custom element definitions in `waitFor` and return a promise -->
<script type="module">
function loadScript(src) {
return new Promise(function(resolve, reject) {
const script = document.createElement('script');
script.src = src;
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
}
WebComponents.waitFor(() => {
// At this point we are guaranteed that all required polyfills have
// loaded, and can use web components APIs.
// Next, load element definitions that call `customElements.define`.
// Note: returning a promise causes the custom elements
// polyfill to wait until all definitions are loaded and then upgrade
// the document in one batch, for better performance.
return loadScript('my-element.js');
});
</script>
Sonuç
Özel öğeler, tarayıcıda yeni HTML etiketleri tanımlamak ve yeniden kullanılabilir bileşenler oluşturmak için bize yeni bir araç sunar. Bunları Gölge DOM ve <template>
gibi diğer yeni platform temel öğeleriyle birleştirdiğinizde Web Bileşenlerinin büyük resmini anlamaya başlıyoruz:
- Yeniden kullanılabilir bileşenler oluşturmak ve genişletmek için tarayıcılar arası (web standardı).
- Başlamak için kitaplık veya çerçeve gerekmez. Vanilla JS/HTML FTW!
- Tanıdık bir programlama modeli sunar. Yalnızca DOM/CSS/HTML'dir.
- Diğer yeni web platformu özellikleriyle (Gölge DOM,
<template>
, CSS özel özellikleri vb.) birlikte iyi çalışır - Tarayıcının Geliştirici Araçları ile güçlü bir şekilde entegre edilmiştir.
- Mevcut erişilebilirlik özelliklerinden yararlanın.