कस्टम एलिमेंट की मदद से, वेब डेवलपर नए एचटीएमएल टैग तय कर सकते हैं, मौजूदा टैग को बड़ा कर सकते हैं, और फिर से इस्तेमाल किए जा सकने वाले वेब कॉम्पोनेंट बना सकते हैं.
कस्टम एलिमेंट की मदद से, वेब डेवलपर नए एचटीएमएल टैग बना सकते हैं, मौजूदा एचटीएमएल टैग को बेहतर बना सकते हैं या दूसरे डेवलपर के बनाए गए कॉम्पोनेंट को बढ़ा सकते हैं. एपीआई, वेब कॉम्पोनेंट का आधार है. यह वेब स्टैंडर्ड के आधार पर, फिर से इस्तेमाल किए जा सकने वाले कॉम्पोनेंट बनाने का तरीका उपलब्ध कराता है. इसके लिए, सिर्फ़ वैनिला JS/एचटीएमएल/सीएसएस का इस्तेमाल किया जाता है. इसकी वजह से, हमारे ऐप्लिकेशन में कम कोड, मॉड्यूलर कोड, और ज़्यादा बार इस्तेमाल किया जाने वाला कोड होता है.
परिचय
ब्राउज़र, वेब ऐप्लिकेशन को व्यवस्थित करने के लिए एक बेहतरीन टूल देता है. इसे एचटीएमएल कहा जाता है. शायद आपने इसके बारे में सुना हो! यह डेक्लेरेटिव, पोर्टेबल, और अच्छी तरह से काम करने वाला है. साथ ही, इसे इस्तेमाल करना आसान है. HTML भले ही बेहतर हो, लेकिन इसकी शब्दावली और इसे बड़ा करने की सुविधा सीमित है. HTML के लाइव स्टैंडर्ड में, मार्कअप के साथ JS के व्यवहार को अपने-आप जोड़ने का कोई तरीका नहीं था. हालांकि, अब यह सुविधा उपलब्ध है.
कस्टम एलिमेंट, एचटीएमएल को आधुनिक बनाने, उसमें मौजूद गड़बड़ियों को ठीक करने, और व्यवहार के साथ स्ट्रक्चर को बंडल करने का जवाब हैं. अगर एचटीएमएल से किसी समस्या का समाधान नहीं मिलता है, तो हम ऐसा कस्टम एलिमेंट बना सकते हैं जिससे समस्या हल हो जाए. कस्टम एलिमेंट, एचटीएमएल के फ़ायदों को बनाए रखते हुए ब्राउज़र को नई तरकीबें सिखाते हैं.
नया एलिमेंट तय करना
नया एचटीएमएल एलिमेंट तय करने के लिए, हमें JavaScript की मदद चाहिए!
customElements
ग्लोबल का इस्तेमाल, कस्टम एलिमेंट तय करने और ब्राउज़र को नए टैग के बारे में बताने के लिए किया जाता है. आपको जिस टैग का नाम बनाना है उसके साथ customElements.define()
को कॉल करें. साथ ही, उस JavaScript class
को कॉल करें जो बेस HTMLElement
को बढ़ाता है.
उदाहरण - मोबाइल ड्रॉअर पैनल, <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 {...});
इस्तेमाल का उदाहरण:
<app-drawer></app-drawer>
यह याद रखना ज़रूरी है कि कस्टम एलिमेंट का इस्तेमाल करने में, <div>
या किसी अन्य एलिमेंट का इस्तेमाल करने में कोई अंतर नहीं होता. इंस्टेंस को पेज पर एलान किया जा सकता है, JavaScript में डाइनैमिक तौर पर बनाया जा सकता है, इवेंट लिसनर अटैच किए जा सकते हैं वगैरह. ज़्यादा उदाहरणों के लिए पढ़ते रहें.
किसी एलिमेंट के JavaScript API को तय करना
कस्टम एलिमेंट की सुविधा, ES2015 के class
का इस्तेमाल करके तय की जाती है, जो HTMLElement
को बढ़ाता है. HTMLElement
एट्रिब्यूट को एक्सटेंड करने से यह पक्का होता है कि कस्टम एलिमेंट को पूरा DOM API इनहेरिट हो. इसका मतलब है कि क्लास में जोड़ी गई कोई भी प्रॉपर्टी/विधि, एलिमेंट के DOM इंटरफ़ेस का हिस्सा बन जाती है. अपने टैग के लिए सार्वजनिक JavaScript API बनाने के लिए, इस क्लास का इस्तेमाल करें.
उदाहरण - <app-drawer>
के डीओएम इंटरफ़ेस को तय करना:
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);
इस उदाहरण में, हम एक ऐसा ड्रॉअर बना रहे हैं जिसमें open
प्रॉपर्टी, disabled
प्रॉपर्टी, और toggleDrawer()
तरीका है. यह प्रॉपर्टी को एचटीएमएल एट्रिब्यूट के तौर पर दिखाता है.
कस्टम एलिमेंट की एक खास सुविधा यह है कि क्लास की परिभाषा में मौजूद this
, डीओएम एलिमेंट को रेफ़र करता है. इसका मतलब है कि क्लास का इंस्टेंस. हमारे उदाहरण में, this
का मतलब <app-drawer>
से है. इस तरह (😉) एलिमेंट, click
लिसनर को खुद से अटैच कर सकता है! इसके अलावा, इवेंट लिसनर के अलावा भी कई अन्य तरीके हैं.
पूरा DOM API, एलिमेंट कोड में उपलब्ध होता है. एलिमेंट की प्रॉपर्टी ऐक्सेस करने, उसके चाइल्ड (this.children
), क्वेरी नोड (this.querySelectorAll('.items')
) वगैरह की जांच करने के लिए, this
का इस्तेमाल करें.
कस्टम एलिमेंट बनाने के नियम
- कस्टम एलिमेंट के नाम में डैश (-) होना चाहिए. इसलिए,
<x-tags>
,<my-element>
, और<my-awesome-app>
मान्य नाम हैं, जबकि<tabs>
और<foo_bar>
अमान्य हैं. यह ज़रूरी है, ताकि एचटीएमएल पार्सर, कस्टम एलिमेंट को सामान्य एलिमेंट से अलग कर सके. इससे यह भी पक्का होता है कि एचटीएमएल में नए टैग जोड़े जाने पर, वे आगे भी काम करते रहें. - एक ही टैग को एक से ज़्यादा बार रजिस्टर नहीं किया जा सकता. ऐसा करने पर,
DOMException
गड़बड़ी का मैसेज दिखेगा. ब्राउज़र को किसी नए टैग के बारे में बताने के बाद, वह टैग लागू हो जाता है. सामान वापस नहीं लिया जा सकता. - कस्टम एलिमेंट अपने-आप बंद नहीं हो सकते, क्योंकि एचटीएमएल में सिर्फ़ कुछ एलिमेंट के अपने-आप बंद होने की अनुमति है. हमेशा क्लोज़िंग टैग लिखें
(
<app-drawer></app-drawer>
).
कस्टम एलिमेंट पर प्रतिक्रिया देना
कस्टम एलिमेंट, अपने अस्तित्व के अहम समय के दौरान कोड चलाने के लिए, लाइफ़साइकल के खास हुक तय कर सकता है. इन्हें कस्टम एलिमेंट के तौर पर प्रतिक्रियाएं कहा जाता है.
नाम | जब कॉल किया जाता है |
---|---|
constructor |
एलिमेंट का एक इंस्टेंस
बनाया गया है या अपग्रेड किया गया है. यह शैडो डॉम बनाने, इवेंट लिसनर सेट अप करने या स्थिति को शुरू करने के लिए मददगार है.
constructor में क्या-क्या किया जा सकता है, इसकी पाबंदियों के बारे में जानने के लिए,
स्पेसिफ़िकेशन
देखें.
|
connectedCallback |
जब भी एलिमेंट को DOM में डाला जाता है, तब इसे कॉल किया जाता है. सेटअप कोड चलाने के लिए काम का है. जैसे, संसाधनों को फ़ेच करना या रेंडर करना. आम तौर पर, आपको इस समय तक काम शुरू करने से बचना चाहिए. |
disconnectedCallback |
जब भी एलिमेंट को डीओएम से हटाया जाता है, तब यह फ़ंक्शन कॉल होता है. यह क्लीन अप कोड चलाने के लिए मददगार है. |
attributeChangedCallback(attrName, oldVal, newVal) |
निगरानी में रखे गए एट्रिब्यूट को जोड़ने, हटाने, अपडेट करने या बदलने पर, यह ट्रिगर होता है. जब कोई एलिमेंट पार्स करने वाले टूल से बनाया जाता है या अपग्रेड किया जाता है, तब शुरुआती वैल्यू के लिए भी इसे कॉल किया जाता है. ध्यान दें: सिर्फ़ observedAttributes प्रॉपर्टी में मौजूद एट्रिब्यूट को यह कॉलबैक मिलेगा.
|
adoptedCallback |
कस्टम एलिमेंट को किसी नए document में ले जाया गया है (उदाहरण के लिए, document.adoptNode(el) ).
|
प्रतिक्रिया वाले कॉलबैक सिंक होते हैं. अगर कोई व्यक्ति आपके एलिमेंट पर el.setAttribute()
कॉल करता है, तो ब्राउज़र तुरंत attributeChangedCallback()
को कॉल करेगा.
इसी तरह, जब आपका एलिमेंट डीओएम से हटाया जाएगा, तब आपको disconnectedCallback()
दिखेगा. उदाहरण के लिए, जब उपयोगकर्ता el.remove()
को कॉल करेगा.
उदाहरण: <app-drawer>
में कस्टम इमोजी जोड़ना:
class AppDrawer extends HTMLElement {
constructor() {
super(); // always call super() first in the constructor.
// ...
}
connectedCallback() {
// ...
}
disconnectedCallback() {
// ...
}
attributeChangedCallback(attrName, oldVal, newVal) {
// ...
}
}
अगर ज़रूरी हो, तो प्रतिक्रियाओं को तय करें. अगर आपका एलिमेंट काफ़ी जटिल है और connectedCallback()
में IndexedDB से कनेक्शन खोलता है, तो disconnectedCallback()
में ज़रूरी क्लीनअप का काम करें. हालांकि, सावधान रहें! यह भरोसा नहीं किया जा सकता कि आपके एलिमेंट को सभी मामलों में डीओएम से हटा दिया जाएगा. उदाहरण के लिए, अगर उपयोगकर्ता टैब बंद कर देता है, तो disconnectedCallback()
को कभी कॉल नहीं किया जाएगा.
प्रॉपर्टी और एट्रिब्यूट
प्रॉपर्टी को एट्रिब्यूट में दिखाना
आम तौर पर, एचटीएमएल प्रॉपर्टी अपनी वैल्यू को एचटीएमएल एट्रिब्यूट के तौर पर डीओएम में दिखाती हैं. उदाहरण के लिए, जब JS में hidden
या id
की वैल्यू बदली जाती हैं, तो:
div.id = 'my-id';
div.hidden = true;
वैल्यू, लाइव डीओएम पर एट्रिब्यूट के तौर पर लागू की जाती हैं:
<div id="my-id" hidden>
इसे "एट्रिब्यूट में प्रॉपर्टी दिखाना" कहा जाता है. एचटीएमएल में मौजूद तकरीबन हर प्रॉपर्टी ऐसा करती है. क्यों? एलिमेंट को एलान के तौर पर कॉन्फ़िगर करने के लिए भी एट्रिब्यूट काम के होते हैं. साथ ही, सुलभता और सीएसएस सिलेक्टर जैसे कुछ एपीआई, काम करने के लिए एट्रिब्यूट पर निर्भर करते हैं.
किसी प्रॉपर्टी को दिखाना तब काम का होता है, जब आपको एलिमेंट के डीओएम को उसकी JavaScript स्थिति के साथ सिंक रखना हो. किसी प्रॉपर्टी को दिखाने की एक वजह यह हो सकती है कि JS की स्थिति बदलने पर, उपयोगकर्ता की तय की गई स्टाइल लागू हो.
हमारा <app-drawer>
याद करें. इस कॉम्पोनेंट का इस्तेमाल करने वाला व्यक्ति, इसे फ़ेड आउट कर सकता है और/या बंद होने पर उपयोगकर्ता के इंटरैक्शन को रोक सकता है:
app-drawer[disabled] {
opacity: 0.5;
pointer-events: none;
}
जब JS में disabled
प्रॉपर्टी बदली जाती है, तो हम चाहते हैं कि उस एट्रिब्यूट को डीओएम में जोड़ दिया जाए, ताकि उपयोगकर्ता का सिलेक्टर मैच हो सके. एलिमेंट, उसी नाम के एट्रिब्यूट की वैल्यू दिखाकर, ऐसा व्यवहार कर सकता है:
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();
}
एट्रिब्यूट में हुए बदलावों को देखना
एचटीएमएल एट्रिब्यूट, उपयोगकर्ताओं के लिए शुरुआती स्थिति की जानकारी देने का एक आसान तरीका है:
<app-drawer open disabled></app-drawer>
एलिमेंट, एट्रिब्यूट में हुए बदलावों पर प्रतिक्रिया दे सकते हैं. इसके लिए, attributeChangedCallback
तय करना होगा. ब्राउज़र, observedAttributes
कलेक्शन में मौजूद एट्रिब्यूट में होने वाले हर बदलाव के लिए, इस तरीके को कॉल करेगा.
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.
}
}
उदाहरण में, हम disabled
एट्रिब्यूट में बदलाव होने पर, <app-drawer>
पर अन्य एट्रिब्यूट सेट कर रहे हैं. हालांकि, हम यहां ऐसा नहीं कर रहे हैं, लेकिन किसी JS प्रॉपर्टी को उसके एट्रिब्यूट के साथ सिंक रखने के लिए, attributeChangedCallback
का इस्तेमाल भी किया जा सकता है.
एलिमेंट के अपग्रेड
प्रोग्रेसिव एन्हैंसमेंट वाला एचटीएमएल
हमने पहले ही जान लिया है कि कस्टम एलिमेंट को customElements.define()
को कॉल करके तय किया जाता है. हालांकि, इसका मतलब यह नहीं है कि आपको एक ही बार में कस्टम एलिमेंट तय करके रजिस्टर करना होगा.
कस्टम एलिमेंट का इस्तेमाल, उनकी परिभाषा रजिस्टर होने से पहले किया जा सकता है.
प्रोग्रेसिव एन्हैंसमेंट, कस्टम एलिमेंट की एक सुविधा है. दूसरे शब्दों में, पेज पर कई <app-drawer>
एलिमेंट का एलान किया जा सकता है और customElements.define('app-drawer', ...)
को बाद में कभी भी लागू नहीं किया जा सकता. ऐसा इसलिए होता है, क्योंकि अनजान टैग की वजह से, ब्राउज़र संभावित कस्टम एलिमेंट को अलग तरह से हैंडल करता है. define()
को कॉल करने और किसी मौजूदा एलिमेंट को क्लास की परिभाषा देने की प्रोसेस को "एलिमेंट अपग्रेड" कहा जाता है.
टैग का नाम कब तय होता है, यह जानने के लिए window.customElements.whenDefined()
का इस्तेमाल किया जा सकता है. यह एक प्रॉमिस दिखाता है, जो एलिमेंट के तय होने पर रिज़ॉल्व हो जाता है.
customElements.whenDefined('app-drawer').then(() => {
console.log('app-drawer defined');
});
उदाहरण - जब तक चाइल्ड एलिमेंट का कोई सेट अपग्रेड नहीं हो जाता, तब तक काम में देरी करना
<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.
});
एलिमेंट से तय किया गया कॉन्टेंट
कस्टम एलिमेंट, एलिमेंट कोड में मौजूद डीओएम एपीआई का इस्तेमाल करके, अपना कॉन्टेंट मैनेज कर सकते हैं. इसके लिए, प्रतिक्रियाएं भेजने की सुविधा का इस्तेमाल किया जा सकता है.
उदाहरण - कुछ डिफ़ॉल्ट एचटीएमएल के साथ एलिमेंट बनाएं:
customElements.define('x-foo-with-markup', class extends HTMLElement {
connectedCallback() {
this.innerHTML = "<b>I'm an x-foo-with-markup!</b>";
}
// ...
});
इस टैग का एलान करने पर, ये चीज़ें दिखेंगी:
<x-foo-with-markup>
<b>I'm an x-foo-with-markup!</b>
</x-foo-with-markup>
// TODO: DevSite - कोड सैंपल हटा दिया गया है, क्योंकि इसमें इनलाइन इवेंट हैंडलर का इस्तेमाल किया गया था
शैडो डीओएम का इस्तेमाल करने वाला एलिमेंट बनाना
शैडो डीओएम की मदद से, किसी एलिमेंट को पेज के बाकी हिस्से से अलग, डीओएम के किसी हिस्से को मालिकाना हक, रेंडर, और स्टाइल करने की सुविधा मिलती है. यहां तक कि, एक ही टैग में पूरे ऐप्लिकेशन को छिपाया जा सकता है:
<!-- chat-app's implementation details are hidden away in Shadow DOM. -->
<chat-app></chat-app>
कस्टम एलिमेंट में शैडो डीओएम का इस्तेमाल करने के लिए, अपने constructor
में this.attachShadow
को कॉल करें:
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));
}
// ...
});
इस्तेमाल का उदाहरण:
<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>
उपयोगकर्ता का कस्टम टेक्स्ट
// TODO: DevSite - कोड सैंपल हटा दिया गया है, क्योंकि इसमें इनलाइन इवेंट हैंडलर का इस्तेमाल किया गया था
<template>
से एलिमेंट बनाना
जिन लोगों को इस बारे में जानकारी नहीं है उनके लिए बता दें कि <template>
एलिमेंट की मदद से, पेज लोड होने पर पार्स किए गए और काम न करने वाले DOM के फ़्रैगमेंट के बारे में बताया जा सकता है. साथ ही, इन्हें रनटाइम के दौरान चालू किया जा सकता है. यह वेब कॉम्पोनेंट फ़ैमिली में मौजूद एक और एपीआई प्रिमिटिव है. टेंप्लेट, कस्टम एलिमेंट के स्ट्रक्चर को बताने के लिए सबसे सही प्लेसहोल्डर हैं.
उदाहरण: किसी <template>
से बनाए गए शैडो DOM कॉन्टेंट के साथ एलिमेंट को रजिस्टर करना:
<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>
कोड की ये कुछ लाइनें काफ़ी असरदार हैं. आइए, इन अहम बातों को समझते हैं:
- हम एचटीएमएल में एक नया एलिमेंट तय कर रहे हैं:
<x-foo-from-template>
- एलिमेंट का शैडो डीओएम,
<template>
से बनाया जाता है - शैडो डीओएम की मदद से, एलिमेंट का डीओएम एलिमेंट के लिए स्थानीय होता है
- शैडो डीओएम की मदद से, एलिमेंट की इंटरनल सीएसएस का दायरा एलिमेंट तक सीमित होता है
मैं Shadow DOM में हूं. मेरे मार्कअप को <template> से स्टैंप किया गया था.
// TODO: DevSite - कोड सैंपल हटा दिया गया है, क्योंकि इसमें इनलाइन इवेंट हैंडलर का इस्तेमाल किया गया था
कस्टम एलिमेंट को स्टाइल करना
भले ही आपका एलिमेंट, शैडो डीओएम का इस्तेमाल करके अपनी स्टाइल तय करता हो, फिर भी उपयोगकर्ता अपने पेज से आपके कस्टम एलिमेंट को स्टाइल कर सकते हैं. इन्हें "उपयोगकर्ता की तय की गई स्टाइल" कहा जाता है.
<!-- 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>
शायद आप खुद से पूछ रहे हों कि अगर एलिमेंट में शैडो डीओएम में स्टाइल तय की गई हैं, तो सीएसएस की खास जानकारी कैसे काम करती है. उपयोगकर्ता स्टाइल, खास जानकारी देने के मामले में बेहतर हैं. ये हमेशा, एलिमेंट के लिए तय की गई स्टाइल को बदल देंगे. शैडो DOM का इस्तेमाल करने वाला एलिमेंट बनाना सेक्शन देखें.
बिना रजिस्टर किए गए एलिमेंट को पहले से स्टाइल करना
किसी एलिमेंट को अपग्रेड करने से पहले, :defined
स्यूडो-क्लास का इस्तेमाल करके, उसे सीएसएस में टारगेट किया जा सकता है. यह किसी कॉम्पोनेंट को पहले से स्टाइल करने के लिए काम आता है. उदाहरण के लिए, हो सकता है कि आप लेआउट या अन्य विज़ुअल फ़ोकस ऑन यूज़र (एफ़ओयूसी) को रोकना चाहें. इसके लिए, बिना जानकारी वाले कॉम्पोनेंट को छिपाएं और जानकारी दिखने पर उन्हें धीरे-धीरे दिखाएं.
उदाहरण - <app-drawer>
को तय किए जाने से पहले छिपाएं:
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>
तय होने के बाद, सिलेक्टर (app-drawer:not(:defined)
) अब मैच नहीं होता.
एलिमेंट एक्सटेंड करना
कस्टम एलिमेंट एपीआई, नए एचटीएमएल एलिमेंट बनाने के लिए मददगार है. हालांकि, यह अन्य कस्टम एलिमेंट या ब्राउज़र में पहले से मौजूद एचटीएमएल को बढ़ाने के लिए भी मददगार है.
कस्टम एलिमेंट को बड़ा करना
किसी दूसरे कस्टम एलिमेंट को बड़ा करने के लिए, उसकी क्लास की परिभाषा को बड़ा किया जाता है.
उदाहरण - <app-drawer>
को एक्सटेंड करने वाला <fancy-app-drawer>
बनाएं:
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);
नेटिव एचटीएमएल एलिमेंट को एक्सटेंशन देना
मान लें कि आपको <button>
का एक शानदार वर्शन बनाना है. <button>
के व्यवहार और फ़ंक्शन को दोहराने के बजाय, कस्टम एलिमेंट का इस्तेमाल करके मौजूदा एलिमेंट को बेहतर बनाना बेहतर विकल्प है.
पसंद के मुताबिक बनाया गया बिल्ट-इन एलिमेंट एक कस्टम एलिमेंट है, जो ब्राउज़र के किसी एक बिल्ट-इन एचटीएमएल टैग को बढ़ाता है. किसी मौजूदा एलिमेंट को एक्सटेंड करने का मुख्य फ़ायदा यह है कि आपको उसकी सभी सुविधाएं (डीओएम प्रॉपर्टी, तरीके, सुलभता) मिलती हैं. प्रोग्रेसिव वेब ऐप्लिकेशन लिखने का इससे बेहतर तरीका नहीं है कि मौजूदा एचटीएमएल एलिमेंट को धीरे-धीरे बेहतर बनाया जाए.
किसी एलिमेंट को एक्सटेंड करने के लिए, आपको एक क्लास की परिभाषा बनानी होगी, जो सही डीओएम इंटरफ़ेस से इनहेरिट होती हो. उदाहरण के लिए, <button>
को एक्सटेंड करने वाले कस्टम एलिमेंट को HTMLElement
के बजाय HTMLButtonElement
से इनहेरिट करना होगा.
इसी तरह, <img>
को बड़ा करने वाले एलिमेंट को HTMLImageElement
को भी बड़ा करना होगा.
उदाहरण - <button>
को बड़ा करना:
// 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'});
ध्यान दें कि किसी नेटिव एलिमेंट को बड़ा करने पर, define()
को कॉल करने का तरीका थोड़ा बदल जाता है. ज़रूरी तीसरा पैरामीटर, ब्राउज़र को बताता है कि आपको किस टैग को बड़ा करना है. ऐसा करना ज़रूरी है, क्योंकि कई एचटीएमएल टैग एक ही डीओएम इंटरफ़ेस का इस्तेमाल करते हैं. <section>
, <address>
, और <em>
(अन्य के साथ) सभी HTMLElement
शेयर करते हैं; <q>
और <blockquote>
, दोनों HTMLQuoteElement
शेयर करते हैं; वगैरह…
{extends: 'blockquote'}
की वैल्यू सेट करने पर, ब्राउज़र को पता चलता है कि <q>
के बजाय, बेहतर <blockquote>
बनाया जा रहा है. एचटीएमएल के डीओएम इंटरफ़ेस की पूरी सूची के लिए, एचटीएमएल स्पेसिफ़िकेशन देखें.
कस्टमाइज़ किए गए पहले से मौजूद एलिमेंट का इस्तेमाल, कई तरीकों से किया जा सकता है. वे नेटिव टैग में is=""
एट्रिब्यूट जोड़कर, इसकी जानकारी दे सकते हैं:
<!-- This <button> is a fancy button. -->
<button is="fancy-button" disabled>Fancy button!</button>
JavaScript में इंस्टेंस बनाएं:
// 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);
इसके अलावा, new
ऑपरेटर का इस्तेमाल करके भी ऐसा किया जा सकता है:
let button = new FancyButton();
button.textContent = 'Fancy button!';
button.disabled = true;
यहां <img>
को बड़ा करने का एक और उदाहरण दिया गया है.
उदाहरण - <img>
को बड़ा करना:
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'});
उपयोगकर्ता इस कॉम्पोनेंट को इस तरह से बताते हैं:
<!-- This <img> is a bigger img. -->
<img is="bigger-img" width="15" height="20">
या JavaScript में इंस्टेंस बनाएं:
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);
अन्य जानकारी
अनजान एलिमेंट बनाम तय नहीं किए गए कस्टम एलिमेंट
एचटीएमएल का इस्तेमाल करना आसान और सुविधाजनक है. उदाहरण के लिए, किसी पेज पर <randomtagthatdoesntexist>
का एलान करें और ब्राउज़र उसे आसानी से स्वीकार कर ले. स्टैंडर्ड टैग के बजाय, दूसरे टैग क्यों काम करते हैं? इसका जवाब है कि एचटीएमएल के लिए तय की गई शर्तों के मुताबिक ऐसा किया जा सकता है. खास जानकारी में जिन एलिमेंट के बारे में नहीं बताया गया है उन्हें HTMLUnknownElement
के तौर पर पार्स किया जाता है.
हालांकि, कस्टम एलिमेंट के लिए ऐसा नहीं है. संभावित कस्टम एलिमेंट को HTMLElement
के तौर पर पार्स किया जाता है. ऐसा तब होता है, जब उन्हें मान्य नाम (इसमें "-" शामिल होता है) के साथ बनाया जाता है. इसकी जांच, कस्टम एलिमेंट के साथ काम करने वाले ब्राउज़र में की जा सकती है. कंसोल चालू करें:
Ctrl+Shift+J (या Mac पर Cmd+Opt+J) दबाएं और कोड की ये पंक्तियां चिपकाएं:
// "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
एपीआई का संदर्भ
customElements
ग्लोबल, कस्टम एलिमेंट के साथ काम करने के लिए काम के तरीके तय करता है.
define(tagName, constructor, options)
ब्राउज़र में नया कस्टम एलिमेंट तय करता है.
उदाहरण
customElements.define('my-app', class extends HTMLElement { ... });
customElements.define(
'fancy-button', class extends HTMLButtonElement { ... }, {extends: 'button'});
get(tagName)
किसी मान्य कस्टम एलिमेंट टैग के नाम के आधार पर, एलिमेंट का कन्स्ट्रक्टर दिखाता है.
अगर कोई एलिमेंट डेफ़िनिशन रजिस्टर नहीं की गई है, तो undefined
दिखाता है.
उदाहरण
let Drawer = customElements.get('app-drawer');
let drawer = new Drawer();
whenDefined(tagName)
एक प्रॉमिस दिखाता है, जो कस्टम एलिमेंट तय होने पर रिज़ॉल्व हो जाता है. अगर एलिमेंट पहले से तय है, तो उसे तुरंत ठीक करें. अगर टैग का नाम, पसंद के मुताबिक बनाए गए एलिमेंट का मान्य नाम नहीं है, तो इसे अस्वीकार कर दिया जाता है.
उदाहरण
customElements.whenDefined('app-drawer').then(() => {
console.log('ready!');
});
इतिहास और ब्राउज़र के लिए सहायता
अगर आपने पिछले कुछ सालों से वेब कॉम्पोनेंट का इस्तेमाल किया है, तो आपको पता होगा कि Chrome 36 और उसके बाद के वर्शन में, कस्टम एलिमेंट एपीआई का एक ऐसा वर्शन लागू किया गया है जो customElements.define()
के बजाय document.registerElement()
का इस्तेमाल करता है. इसे अब स्टैंडर्ड का पुराना वर्शन माना जाता है, जिसे v0 कहा जाता है.
customElements.define()
एक नया और लोकप्रिय तरीका है. इसे ब्राउज़र वेंडर लागू करना शुरू कर रहे हैं. इसे कस्टम एलिमेंट v1 कहा जाता है.
अगर आपको पुराने वर्शन 0 के स्पेसिफ़िकेशन के बारे में जानना है, तो html5rocks का लेख पढ़ें.
ब्राउज़र समर्थन
Chrome 54 (स्थिति), Safari 10.1 (स्थिति), और Firefox 63 (स्थिति) में, कस्टम एलिमेंट v1 है. Edge के लिए, डेवलपमेंट शुरू हो गया है.
कस्टम एलिमेंट का पता लगाने के लिए, window.customElements
की मौजूदगी की जांच करें:
const supportsCustomElementsV1 = 'customElements' in window;
Polyfill
जब तक ब्राउज़र के लिए सहायता सभी के लिए उपलब्ध नहीं हो जाती, तब तक कस्टम एलिमेंट वर्शन 1 के लिए, स्टैंडअलोन पॉलीफ़िल उपलब्ध है. हालांकि, हमारा सुझाव है कि वेब कॉम्पोनेंट के पॉलीफ़िल को बेहतर तरीके से लोड करने के लिए, webcomponents.js loader का इस्तेमाल करें. लोडर, ब्राउज़र के लिए ज़रूरी पॉलीफ़िल को असिंक्रोनस तरीके से लोड करने के लिए, सुविधा की पहचान का इस्तेमाल करता है.
इसे इंस्टॉल करने के लिए:
npm install --save @webcomponents/webcomponentsjs
इस्तेमाल का तरीका:
<!-- 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>
नतीजा
कस्टम एलिमेंट की मदद से, ब्राउज़र में नए एचटीएमएल टैग तय किए जा सकते हैं और फिर से इस्तेमाल किए जा सकने वाले कॉम्पोनेंट बनाए जा सकते हैं. इन्हें Shadow DOM और <template>
जैसे नए प्लैटफ़ॉर्म प्राइमिटिव के साथ जोड़ें, तो हमें Web Components की पूरी तस्वीर दिखने लगती है:
- फिर से इस्तेमाल किए जा सकने वाले कॉम्पोनेंट बनाने और उन्हें बड़ा करने के लिए, क्रॉस-ब्राउज़र (वेब स्टैंडर्ड).
- शुरू करने के लिए, किसी लाइब्रेरी या फ़्रेमवर्क की ज़रूरत नहीं होती. Vanilla JS/HTML FTW!
- यह एक जाना-पहचाना प्रोग्रामिंग मॉडल उपलब्ध कराता है. यह सिर्फ़ डीओएम/सीएसएस/एचटीएमएल है.
- वेब प्लैटफ़ॉर्म की अन्य नई सुविधाओं (शैडो DOM,
<template>
, सीएसएस की कस्टम प्रॉपर्टी वगैरह) के साथ अच्छी तरह से काम करता है - ब्राउज़र के DevTools के साथ पूरी तरह इंटिग्रेट किया गया है.
- सुलभता की मौजूदा सुविधाओं का फ़ायदा लें.