كيف يعمل JS: العناصر المخصصة

[نصح القراءة] الأجزاء الـ 19 الأخرى من الدورة
الجزء 1: نظرة عامة على المحرك وآليات وقت التشغيل ومكدس المكالمة
الجزء 2: حول V8 الداخلية وتحسين التعليمات البرمجية
الجزء الثالث: إدارة الذاكرة وأربعة أنواع من تسرب الذاكرة والتعامل معها
الجزء 4: حلقة الأحداث ، عدم التزامن ، وخمس طرق لتحسين التعليمات البرمجية الخاصة بك مع عدم التزامن / انتظار
الجزء 5: WebSocket و HTTP / 2 + SSE. ماذا تختار؟
الجزء 6: ميزات ومجال WebAssembly
الجزء 7: عمال الويب وخمسة سيناريوهات الاستخدام
الجزء الثامن: عمال الخدمة
الجزء 9: دفع الإخطارات على شبكة الإنترنت
الجزء 10: تتبع التغييرات في DOM باستخدام MutationObserver
الجزء 11: محركات تقديم صفحات الويب ونصائح لتحسين أدائها
الجزء 12: نظام الشبكة الفرعي للمتصفحات ، مما يحسن من أدائها وأمانها
الجزء 12: نظام الشبكة الفرعي للمتصفحات ، مما يحسن من أدائها وأمانها
الجزء 13: الرسوم المتحركة مع CSS وجافا سكريبت
الجزء 14: كيف يعمل JS: أشجار الجملة المجردة ، التحليل والتحسين
الجزء 15: كيف يعمل JS: الطبقات والميراث ، transpilation في بابل و TypeScript
الجزء 16: كيف يعمل JS: التخزين
الجزء 17: كيف يعمل JS: تقنية Shadow DOM ومكونات الويب
الجزء 18: كيف يعمل JS: آليات الاتصال WebRTC و P2P
الجزء 19: كيف يعمل JS: العناصر المخصصة

نقدم انتباهكم إلى ترجمة 19 مقالة من سلسلة مواد SessionStack حول ميزات الآليات المختلفة لنظام جافا سكريبت. سنتحدث اليوم عن معيار العناصر المخصصة - ما يسمى ب "العناصر المخصصة". سنتحدث عن المهام التي يسمحون بحلها ، وكيفية إنشائها واستخدامها.

الصورة


مراجعة


في إحدى المقالات السابقة في هذه السلسلة ، تحدثنا عن Shadow DOM وبعض التقنيات الأخرى التي تشكل جزءًا من ظاهرة أكبر - مكونات الويب. تم تصميم مكونات الويب لتمكين المطورين من توسيع الميزات القياسية لـ HTML من خلال إنشاء عناصر مضغوطة ونموذجية وقابلة لإعادة الاستخدام. هذا هو معيار W3C الجديد نسبيًا الذي لاحظه بالفعل مصنعو جميع المتصفحات الرائدة. يمكن العثور عليه في الإنتاج ، على الرغم ، بالطبع ، بينما يتم توفير عمله من خلال polyphiles (سنتحدث عنها لاحقًا).

كما تعلم بالفعل ، توفر لنا المتصفحات بعض الأدوات الأساسية لتطوير مواقع الويب وتطبيقات الويب. حول HTML و CSS و JavaScript. يتم استخدام HTML لتنظيم صفحات الويب ، وذلك بفضل CSS التي يتم منحها مظهرًا جميلًا ، وجافا سكريبت مسؤولة عن الميزات التفاعلية. ومع ذلك ، قبل ظهور مكونات الويب ، لم يكن من السهل جدًا ربط الإجراءات التي يتم تنفيذها باستخدام جافا سكريبت ببنية HTML.

في الواقع ، سننظر هنا في أساس مكونات الويب - العناصر المخصصة. باختصار ، تسمح واجهة برمجة التطبيقات المصممة للعمل معها للمبرمج بإنشاء عناصر HTML الخاصة بهم باستخدام منطق جافا سكريبت وأنماط موصوفة بواسطة CSS. يخلط العديد من العناصر المخصصة مع تقنية Shadow DOM. ومع ذلك ، فهذان شيئان مختلفان تمامًا ، في الواقع ، يكملان بعضهما البعض ، لكنهما غير قابلين للتبادل.

تحاول بعض الأطر (مثل Angular أو React) حل نفس المشكلة التي تحلها العناصر المخصصة عن طريق تقديم مفاهيمها الخاصة. يمكن مقارنة العناصر المخصصة مع التوجيهات الزاوية أو مع مكونات التفاعل. ومع ذلك ، فإن العناصر المخصصة هي ميزة قياسية للمتصفح ؛ ولا تحتاج إلى استخدام أي شيء بخلاف JavaScript و HTML و CSS العادية للعمل معها. بالطبع ، هذا لا يسمح لنا أن نقول أنها استبدال لأطر شبيبة عادية. تمنحنا الأطر الحديثة أكثر من مجرد القدرة على محاكاة سلوك العناصر المخصصة. ونتيجة لذلك ، يمكننا القول أن كل من الأطر وعناصر المستخدم هي تقنيات يمكن استخدامها معًا لحل مهام تطوير الويب.

API


قبل المتابعة ، دعنا نرى الفرص التي تتيحها لنا واجهة برمجة التطبيقات للعمل مع العناصر المخصصة. وبالتحديد ، نحن نتحدث عن كائن customElements عالمي له عدة طرق:

  • تتيح لك طريقة التعريف define(tagName, constructor, options) تحديد (إنشاء ، تسجيل) عنصر مستخدم جديد. يستغرق الأمر ثلاث وسائط - اسم العلامة لعنصر المستخدم ، المطابق لقواعد التسمية لهذه العناصر ، وإعلان فئة ، وكائن بمعلمات. حاليًا ، يتم دعم معلمة واحدة فقط - وهي extends ، وهو عبارة عن سلسلة تحدد اسم العنصر المضمن المراد توسيعه. يتم استخدام هذه الميزة لإنشاء إصدارات خاصة من العناصر القياسية.
  • تُرجع طريقة get(tagName) مُنشئ عنصر المستخدم ، شريطة أن يكون هذا العنصر مُعرَّفًا بالفعل ، وإلا فإنه يُرجع undefined . يستغرق الأمر وسيطة واحدة - علامة اسم عنصر المستخدم.
  • تُرجع طريقة whenDefined(tagName) الوعد الذي يتم حله بعد إنشاء عنصر المستخدم. إذا تم تحديد عنصر بالفعل ، يتم حل هذا الوعد على الفور. يتم رفض الوعد إذا كان اسم العلامة الذي تم تمريره إليه ليس اسم علامة صالح لعنصر المستخدم. تقبل هذه الطريقة اسم العلامة لعنصر المستخدم.

إنشاء عناصر مخصصة


إنشاء عناصر مخصصة أمر بسيط للغاية. للقيام بذلك ، يجب عمل شيئين: إنشاء تعريف فئة للعنصر الذي يجب أن يوسع فئة HTMLElement وتسجيل هذا العنصر تحت الاسم المحدد. إليك ما يبدو عليه:

 class MyCustomElement extends HTMLElement { constructor() {   super();   // … } // … } customElements.define('my-custom-element', MyCustomElement); 

إذا كنت لا تريد تلويث النطاق الحالي ، يمكنك استخدام فئة مجهولة:

 customElements.define('my-custom-element', class extends HTMLElement { constructor() {   super();   // … } // … }); 

كما ترى من الأمثلة ، يتم تسجيل عنصر المستخدم باستخدام طريقة customElements.define(...) المألوفة لك بالفعل.

المشاكل التي تحلها العناصر المخصصة


لنتحدث عن المشاكل التي تسمح لنا بحل العناصر المخصصة. أحدها هو تحسين بنية الكود وإلغاء ما يسمى "حساء div tag" (حساء div). هذه الظاهرة هي بنية شفرة شائعة جدًا في تطبيقات الويب الحديثة ، حيث يوجد العديد من عناصر div المضمنة في بعضها البعض. إليك ما قد يبدو عليه:

 <div class="top-container"> <div class="middle-container">   <div class="inside-container">     <div class="inside-inside-container">       <div class="are-we-really-doing-this">         <div class="mariana-trench">           …         </div>       </div>     </div>   </div> </div> </div> 

يتم استخدام كود HTML هذا لأسباب مبررة - فهو يصف تخطيط الصفحة ويضمن عرضها الصحيح على الشاشة. ومع ذلك ، فإن هذا يضعف من سهولة قراءة كود HTML ويعقد صيانتها.

لنفترض أن لدينا مكونًا يشبه الشكل التالي.


مظهر المكون

باستخدام النهج التقليدي لوصف هذه الأشياء ، فإن الرمز التالي يتوافق مع هذا المكون:

 <div class="primary-toolbar toolbar"> <div class="toolbar">   <div class="toolbar-button">     <div class="toolbar-button-outer-box">       <div class="toolbar-button-inner-box">         <div class="icon">           <div class="icon-undo"> </div>         </div>       </div>     </div>   </div>   <div class="toolbar-button">     <div class="toolbar-button-outer-box">       <div class="toolbar-button-inner-box">         <div class="icon">           <div class="icon-redo"> </div>         </div>       </div>     </div>   </div>   <div class="toolbar-button">     <div class="toolbar-button-outer-box">       <div class="toolbar-button-inner-box">         <div class="icon">           <div class="icon-print"> </div>         </div>       </div>     </div>   </div>   <div class="toolbar-toggle-button toolbar-button">     <div class="toolbar-button-outer-box">       <div class="toolbar-button-inner-box">         <div class="icon">           <div class="icon-paint-format"> </div>         </div>       </div>     </div>   </div> </div> </div> 

تخيل الآن أنه يمكننا ، بدلاً من هذا الرمز ، استخدام هذا الوصف للمكون:

 <primary-toolbar> <toolbar-group>   <toolbar-button class="icon-undo"></toolbar-button>   <toolbar-button class="icon-redo"></toolbar-button>   <toolbar-button class="icon-print"></toolbar-button>   <toolbar-toggle-button class="icon-paint-format"></toolbar-toggle-button> </toolbar-group> </primary-toolbar> 

أنا متأكد من أن الجميع سيوافق على أن الجزء الثاني من الكود يبدو أفضل بكثير. هذه الشفرة أسهل في القراءة ، وأسهل في الصيانة ، ومفهومة لكل من المطور والمتصفح. كل هذا يرجع إلى حقيقة أنه أبسط من ذلك الذي يوجد به العديد من علامات div المتداخلة.

المشكلة التالية التي يمكن حلها باستخدام العناصر المخصصة هي إعادة استخدام التعليمات البرمجية. يجب ألا يعمل الرمز الذي يكتبه المطورون فحسب ، بل يجب دعمه أيضًا. إعادة استخدام التعليمات البرمجية ، بدلاً من كتابة نفس التركيبات باستمرار ، يحسن قدرات دعم المشروع.
إليك مثال بسيط سيساعدك على فهم هذه الفكرة بشكل أفضل. لنفترض أن لدينا العنصر التالي:

 <div class="my-custom-element"> <input type="text" class="email" /> <button class="submit"></button> </div> 

إذا كنت في حاجة إليها باستمرار ، فعند اتباع الطريقة المعتادة ، سيتعين علينا كتابة شفرة HTML نفسها مرارًا وتكرارًا. تخيل الآن أنك بحاجة إلى إجراء تغيير على هذا الرمز يجب أن ينعكس في أي مكان يتم استخدامه. هذا يعني أننا بحاجة إلى العثور على جميع الأماكن التي يتم فيها استخدام هذا الجزء ، ثم إجراء نفس التغييرات في كل مكان. إنه طويل وصعب ومشحون بالأخطاء.

سيكون من الأفضل بكثير إذا تمكنا من هذا العنصر المطلوب ، فقط اكتب ما يلي:

 <my-custom-element></my-custom-element> 

ومع ذلك ، فإن تطبيقات الويب الحديثة أكثر بكثير من HTML الثابت. إنها تفاعلية. مصدر تفاعلهم هو JavaScript. عادة ، لتوفير مثل هذه القدرات ، يتم إنشاء بعض العناصر ، ثم يتم ربط مستمعي الأحداث بها ، مما يسمح لهم بالاستجابة لتأثيرات المستخدم. على سبيل المثال ، يمكنهم الاستجابة للنقرات ، و "تحريك" مؤشر الماوس فوقها ، وسحبها حول الشاشة ، وما إلى ذلك. إليك كيفية توصيل مستمع الأحداث بعنصر يحدث عند النقر فوقه بالماوس:

 var myDiv = document.querySelector('.my-custom-element'); myDiv.addEventListener('click', _ => { myDiv.innerHTML = '<b> I have been clicked </b>'; }); 

وإليك رمز HTML لهذا العنصر:

 <div class="my-custom-element"> I have not been clicked yet. </div> 

باستخدام API للعمل مع العناصر المخصصة ، يمكن تضمين كل هذا المنطق في العنصر نفسه. للمقارنة - فيما يلي رمز الإعلان عن عنصر مخصص يتضمن معالج حدث:

 class MyCustomElement extends HTMLElement { constructor() {   super();   var self = this;   self.addEventListener('click', _ => {     self.innerHTML = '<b> I have been clicked </b>';   }); } } customElements.define('my-custom-element', MyCustomElement); 

وإليك كيف تبدو في كود HTML للصفحة:

 <my-custom-element> I have not been clicked yet </my-custom-element> 

للوهلة الأولى ، قد يبدو أن هناك حاجة إلى المزيد من أسطر رمز JS لإنشاء عنصر مخصص. ومع ذلك ، في التطبيقات الواقعية ، نادرًا ما يحدث أن يتم إنشاء هذه العناصر لاستخدامها مرة واحدة فقط. ظاهرة نموذجية أخرى في تطبيقات الويب الحديثة هي أن معظم العناصر الموجودة فيها يتم إنشاؤها ديناميكيًا. يؤدي هذا إلى الحاجة إلى دعم سيناريوهين مختلفين للعمل مع العناصر - المواقف عندما تتم إضافتها إلى الصفحة ديناميكيًا باستخدام جافا سكريبت ، والمواقف عندما يتم وصفها في بنية HTML الأصلية للصفحة. بفضل استخدام العناصر المخصصة ، تم تبسيط العمل في هاتين الحالتين.

ونتيجة لذلك ، إذا قمنا بتلخيص نتائج هذا القسم ، فيمكننا أن نقول أن عناصر المستخدم تجعل الشفرة أكثر وضوحًا ، وتبسط دعمها ، وتساعد على تقسيمها إلى وحدات صغيرة ، والتي تشمل جميع الوظائف اللازمة ومناسبة لإعادة الاستخدام.

الآن بعد أن ناقشنا القضايا العامة للعمل مع العناصر المخصصة ، دعنا نتحدث عن ميزاتها.

المتطلبات


قبل البدء في تطوير العناصر المخصصة الخاصة بك ، يجب أن تعرف بعض القواعد التي يجب عليك اتباعها عند إنشائها. ها هم:

  • يجب أن يشتمل اسم المكون على واصلة ( - رمز). بفضل هذا ، يمكن أن يميز محلل HTML بين العناصر المضمنة وعناصر المستخدم. بالإضافة إلى ذلك ، يضمن هذا النهج عدم وجود تصادمات في الأسماء مع عناصر مضمنة (مع العناصر الموجودة الآن ومع العناصر التي ستظهر في المستقبل). على سبيل المثال ، الاسم الفعلي للعنصر المخصص هو >my-custom-element< ، والأسماء >myCustomElement< و <my_custom_element> غير مناسبة.
  • يحظر تسجيل العلامة نفسها أكثر من مرة. ستؤدي محاولة القيام بذلك إلى قيام المتصفح DOMException خطأ DOMException . لا يمكن إعادة تعريف العناصر المخصصة.
  • لا يمكن أن تكون العلامات المخصصة ذاتية الإغلاق. يدعم محلل HTML مجموعة محدودة فقط من علامات الإغلاق الذاتي القياسية (على سبيل المثال ، <img> ، <link> ، <br> ).

الاحتمالات


لنتحدث عما يمكنك القيام به باستخدام العناصر المخصصة. إذا أجبت على هذا السؤال باختصار ، اتضح أنه يمكنك القيام بالكثير من الأشياء المثيرة للاهتمام معهم.

واحدة من أبرز الميزات للعناصر المخصصة هي أن تعريف فئة العنصر يشير إلى عنصر DOM نفسه. وهذا يعني أنه يمكنك استخدام هذه الكلمة الرئيسية في إعلان لربط مستمعي الأحداث ، والوصول إلى الخصائص ، والعقد الفرعية ، وما إلى ذلك.

 class MyCustomElement extends HTMLElement { // ... constructor() {   super();   this.addEventListener('mouseover', _ => {     console.log('I have been hovered');   }); } // ... } 

هذا ، بالطبع ، يجعل من الممكن كتابة بيانات جديدة إلى العقد التابعة للعنصر. ومع ذلك ، لا ينصح بذلك ، لأن هذا يمكن أن يؤدي إلى سلوك غير متوقع للعناصر. إذا كنت تتخيل أنك تستخدم عناصر تم تصميمها بواسطة شخص آخر ، فربما تفاجأ إذا تم استبدال الترميز الخاص بك في العنصر بشيء آخر.

هناك العديد من الطرق التي تسمح لك بتنفيذ التعليمات البرمجية عند نقاط معينة في دورة حياة العنصر.

  • يتم استدعاء طريقة constructor مرة واحدة ، عند إنشاء أو "ترقية" العنصر (سنتحدث عن هذا أدناه). غالبًا ما يتم استخدامه لتهيئة حالة عنصر ، لربط مستمعي الأحداث ، وإنشاء Shadow DOM ، وما إلى ذلك. لا تنس أنك تحتاج دائمًا إلى استدعاء super() في المُنشئ.
  • يتم استدعاء طريقة connectCallback في كل مرة يتم فيها إضافة عنصر إلى DOM. يمكن استخدامه (وهذه هي الطريقة التي يوصى باستخدامها بها) من أجل تأجيل تنفيذ أي إجراءات حتى لحظة ظهور العنصر على الصفحة (على سبيل المثال ، بهذه الطريقة يمكنك تأخير تحميل بعض البيانات).
  • يتم استدعاء طريقة disconnectedCallback عند إزالة عنصر من DOM. يتم استخدامه عادة لتحرير الموارد. لاحظ أنه لا يتم استدعاء هذه الطريقة إذا قام المستخدم بإغلاق علامة تبويب المتصفح بالصفحة. لذلك ، لا تعتمد عليه عند الضرورة للقيام ببعض الإجراءات ذات الأهمية الخاصة.
  • يتم استدعاء طريقة attribChangedCallback عند إضافة attributeChangedCallback عنصر أو إزالتها أو تحديثها أو استبدالها. بالإضافة إلى ذلك ، يتم استدعاؤه عندما يتم إنشاء العنصر بواسطة المحلل اللغوي. ومع ذلك ، لاحظ أن هذه الطريقة تنطبق فقط على السمات المسردة في خاصية الخصائص المميزة.
  • يتم adoptedCallback الأسلوب adoptedCallback عند استخدام طريقة document.adoptNode(...) ، والتي يتم استخدامها لنقل العقدة إلى مستند آخر.

يرجى ملاحظة أن جميع الطرق المذكورة أعلاه متزامنة. على سبيل المثال ، يتم استدعاء طريقة connectCallback مباشرة بعد إضافة العنصر إلى DOM ، وينتظر بقية البرنامج إتمام هذه الطريقة.

انعكاس الملكية


تحتوي عناصر HTML المضمنة على ميزة واحدة مريحة للغاية: انعكاس الملكية. بفضل هذه الآلية ، تنعكس قيم بعض الخصائص مباشرة في DOM كسمات. لنفترض أن هذه هي خاصية خاصية id . على سبيل المثال ، نقوم بتنفيذ العملية التالية:

 myDiv.id = 'new-id'; 

ستؤثر التغييرات ذات الصلة على DOM:

 <div id="new-id"> ... </div> 

تعمل هذه الآلية في الاتجاه المعاكس. إنه مفيد للغاية لأنه يسمح لك بتكوين العناصر بشكل معلن.

لا تحتوي العناصر المخصصة على هذه الميزة المضمنة ، ولكن يمكنك تنفيذها بنفسك. لكي تتصرف بعض خصائص عناصر المستخدم بشكل مشابه ، يمكنك تكوين مجموعاتهم والمستوطنين.

 class MyCustomElement extends HTMLElement { // ... get myProperty() {   return this.hasAttribute('my-property'); } set myProperty(newValue) {   if (newValue) {     this.setAttribute('my-property', newValue);   } else {     this.removeAttribute('my-property');   } } // ... } 

تمديد البنود الموجودة


لا تسمح لك واجهة برمجة التطبيقات للعناصر المخصصة فقط بإنشاء عناصر HTML جديدة ، ولكن أيضًا توسيع العناصر الموجودة. علاوة على ذلك ، نحن نتحدث عن كل من العناصر القياسية والعناصر المخصصة. يتم ذلك باستخدام extends عند الإعلان عن فئة:

 class MyAwesomeButton extends MyButton { // ... } customElements.define('my-awesome-button', MyAwesomeButton);</cosourcede>      ,  , ,    <code>customElements.define(...)</code>,    <code>extends</code>   ,      .     ,        ,        DOM-.   ,          ,      ,       . <source>class MyButton extends HTMLButtonElement { // ... } customElements.define('my-button', MyButton, {extends: 'button'}); 

تسمى العناصر القياسية الموسعة أيضًا "عنصر مدمج مخصص".

من المستحسن جعلها قاعدة لتوسيع العناصر الموجودة دائمًا ، والقيام بها بشكل تدريجي. سيسمح لك هذا بحفظ العناصر الجديدة التي تم تنفيذها في العناصر التي تم إنشاؤها مسبقًا (أي الخصائص والسمات والوظائف) في عناصر جديدة.

يرجى ملاحظة أن العناصر المضمنة المخصصة الآن مدعومة فقط في Chrome 67+. سيظهر هذا في متصفحات أخرى ، ومع ذلك ، من المعروف أن مطوري Safari قرروا عدم تنفيذ هذه الفرصة.

تحديث العناصر


كما ذكرنا من قبل ، يتم استخدام طريقة customElements.define(...) لتسجيل العناصر المخصصة. ومع ذلك ، لا يمكن استدعاء التسجيل الإجراء الذي يجب القيام به في المقام الأول. يمكن تأجيل تسجيل عنصر المستخدم لفترة ، علاوة على ذلك ، قد يأتي هذا الوقت حتى عندما تتم إضافة العنصر بالفعل إلى DOM. هذه العملية تسمى الترقية. لمعرفة متى سيتم تسجيل عنصر ما ، يوفر المستعرض طريقة customElements.whenDefined(...) . يتم إعطاؤه اسم علامة العنصر ، ويعيد الوعد الذي تم حله بعد تسجيل العنصر.

 customElements.whenDefined('my-custom-element').then(_ => { console.log('My custom element is defined'); }); 

على سبيل المثال ، قد تحتاج إلى تأخير تسجيل عنصر حتى يتم الإعلان عن أبنائه. يمكن أن يكون هذا الخط من السلوك مفيدًا للغاية إذا كان المشروع يحتوي على عناصر مستخدم متداخلة. في بعض الأحيان يمكن للوالد الاعتماد على تنفيذ العناصر التابعة. في هذه الحالة ، تحتاج إلى التأكد من تسجيل الأطفال قبل الوالدين.

دوم الظل


كما ذكرنا سابقًا ، تعد العناصر المخصصة و Shadow DOM تقنيات تكميلية. الأول يسمح لك بتضمين منطق JS في عناصر المستخدم ، والثاني يسمح لك بإنشاء بيئات معزولة لأجزاء DOM التي لا تتأثر بما هو خارجها. إذا كنت تشعر أنك بحاجة إلى فهم أفضل لمفهوم Shadow DOM ، فألق نظرة على أحد منشوراتنا السابقة .

فيما يلي كيفية استخدام Shadow DOM لعنصر مخصص:

 class MyCustomElement extends HTMLElement { // ... constructor() {   super();   let shadowRoot = this.attachShadow({mode: 'open'});   let elementContent = document.createElement('div');   shadowRoot.appendChild(elementContent); } // ... }); 

كما ترون ، فإن استدعاء this.attachShadow يلعب دورًا رئيسيًا this.attachShadow .

الأنماط


في إحدى مقالاتنا السابقة ، تحدثنا قليلاً عن النماذج ، على الرغم من أنها تستحق في الواقع مقالة منفصلة. سنلقي نظرة هنا على مثال بسيط لكيفية تضمين النماذج في العناصر المخصصة عند إنشائها. لذلك ، باستخدام علامة <template> ، يمكنك وصف جزء DOM الذي ستتم معالجته بواسطة المحلل اللغوي ، ولكن لن يتم عرضه على الصفحة:

 <template id="my-custom-element-template"> <div class="my-custom-element">   <input type="text" class="email" />   <button class="submit"></button> </div> </template> 

إليك كيفية تطبيق قالب في عنصر مخصص:

 let myCustomElementTemplate = document.querySelector('#my-custom-element-template'); class MyCustomElement extends HTMLElement { // ... constructor() {   super();   let shadowRoot = this.attachShadow({mode: 'open'});   shadowRoot.appendChild(myCustomElementTemplate.content.cloneNode(true)); } // ... }); 

كما ترى ، هناك مزيج من عنصر مخصص و Shadow DOM والقوالب. سمح لنا هذا بإنشاء عنصر معزول في مساحته الخاصة ، حيث يتم فصل بنية HTML عن منطق JS.

الأسلوب


حتى الآن ، تحدثنا فقط عن JavaScript و HTML ، متجاهلين CSS. لذلك ، نتطرق الآن إلى موضوع الأنماط. من الواضح أننا بحاجة إلى طريقة ما لتصميم نمط العناصر المخصصة. يمكن إضافة الأنماط داخل Shadow DOM ، ولكن بعد ذلك يطرح السؤال حول كيفية تصميم هذه العناصر من الخارج ، على سبيل المثال - إذا لم يتم استخدامها من قبل الشخص الذي أنشأها. الإجابة على هذا السؤال بسيطة للغاية - يتم تصميم العناصر المخصصة بنفس طريقة العناصر المضمنة.

 my-custom-element { border-radius: 5px; width: 30%; height: 50%; // ... } 

لاحظ أن الأنماط الخارجية لها الأسبقية على الأنماط المعلنة داخل عنصر ما ، وتجاوزها.

ربما تكون قد رأيت كيف ، عندما تم عرض صفحة على الشاشة ، في مرحلة ما ، يمكنك ملاحظة محتوى غير مبسط عليها (وهذا ما يسمى FOUC - Flash Of Unstyled Content). يمكنك تجنب هذه الظاهرة من خلال تعيين أنماط للمكونات غير المسجلة ، واستخدام بعض التأثيرات المرئية عند تسجيلها. للقيام بذلك ، يمكنك استخدام المحدد :defined . يمكنك القيام بذلك ، على سبيل المثال ، مثل:

 my-button:not(:defined) { height: 20px; width: 50px; opacity: 0; } 

عناصر غير معروفة وعناصر مستخدم غير محددة


مواصفات HTML مرنة للغاية ، فهي تسمح لك بالإعلان عن أي علامات تحتاجها للمطور. وإذا لم يتعرف المستعرض على العلامة ، فسيتم معالجتها بواسطة المحلل اللغوي كـ HTMLUnknownElement :

 var element = document.createElement('thisElementIsUnknown'); if (element instanceof HTMLUnknownElement) { console.log('The selected element is unknown'); } 

ومع ذلك ، عند العمل مع العناصر المخصصة ، لا ينطبق هذا المخطط. , ? , , HTMLElement .

 var element = document.createElement('this-element-is-undefined'); if (element instanceof HTMLElement) { console.log('The selected element is undefined but not unknown'); } 

HTMLElement HTMLUnknownElement , , , , - . , , , . div . .


Chrome 36+. API Custom Components v0, , , , . API, , — . API Custom Elements v1 Chrome 54+ Safari 10.1+ ( ). Mozilla v50, , . , Microsoft Edge API. , , webkit. , , , — IE 11.


, , , customElements
window :

 const supportsCustomElements = 'customElements' in window; if (supportsCustomElements) { // API Custom Elements   } 

:

 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); }); } //    -    . if (supportsCustomElements) { //    ,    . } else { loadScript('path/to/custom-elements.min.js').then(_ => {   //   ,     . }); } 

الملخص


, :

  • HTML- JavaScript-, , CSS-.
  • HTML- ( , ).
  • . , — JavaScript, HTML, CSS, , , .
  • - (Shadow DOM, , , ).
  • , .
  • , .

, Custom Elements v1 , , , , , .

أعزائي القراء! ?

Source: https://habr.com/ru/post/ar419831/


All Articles