Advertisement
  1. Web Design
  2. HTML & CSS

مقدمة الىShadow DOM

Scroll to top
Read Time: 11 min

() translation by (you can also view the original English article)

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

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

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


Shadow DOM الى الإنقاذ

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

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

تمكين Shadow DOM

في وقت كتابة هذه السطور ، يدعم الإصدار الحالي من Chrome (الإصدار 29) فحص Shadow DOM باستخدام Chrome DevTools. افتح Devtools وانقر على زر cog في الجزء السفلي الأيسر من الشاشة لفتح لوحة Settings ، ثم مرر لأسفل قليلاً وستظهر مربع اختيار لعرض Shadow DOM.

Turn on Shadow DOMTurn on Shadow DOMTurn on Shadow DOM

الآن بعد أن قمنا بتمكين متصفحك ، دعنا نتحقق من الأجزاء الداخلية لمشغل الصوت الافتراضي. فقط اكتب:

1
<audio width="300" height="32" src="https://developer.mozilla.org/@api/deki/files/2926/=AudioTest_(1).ogg" autoplay="autoplay" controls="controls">
2
 Your browser does not support the HTML5 Audio.
3
 </audio>

في ترميز HTML الخاص بك. يعرض مشغل الصوت الأصلي التالي في المستعرضات المدعومة:

audio_player

الآن المضي قدما وتفقد القطعة مشغل الصوت التي قمت بإنشائها فقط.

Shadow DOM of Native Date WidgetShadow DOM of Native Date WidgetShadow DOM of Native Date Widget

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

Shadow Host & Shadow Root

  • Shadow Host: هو عنصر DOM الذي يستضيف المجموعة الفرعية Shadow DOM أو هي عقدة DOM التي تحتوي علىShadow Root ".
  • Shadow Root: هو جذر الشجرة الفرعية لـ DOM التي تحتوي على عقد ال Shadow DOM. إنها عقدة خاصة ، والتي تنشئ الحدود بين عقد ال DOM العادية وعقد ال ShadowDOM . إنها هذه الحدود ، التي تقوم بتغليف عقد Shadow DOM من أي شفرة JavaScript أو CSS على الصفحة المستهلكة.
  • Shadow DOM: يسمح بتكوين نطاقات فرعية DOM متعددة في شجرة واحدة أكبر. تشرح الصور التالية المأخوذة من مسودة العمل الخاصة بـ W3C مفهوم تراكب العقد. هذه هي الطريقة التي تظهر بها قبل أن يتم إرفاق محتوى Shadow Root الى عنصر ال Shadow Host:
    Normal Document Tree Shadow DOM SubtreesNormal Document Tree Shadow DOM SubtreesNormal Document Tree Shadow DOM Subtrees

    عندما يتم تقديمها ، فإن Shadow tree تأخذ مكان محتوى الShadow Host.

    Composition CompleteComposition CompleteComposition Complete

    وغالبًا ما يشار إلى عملية التراكب هذه على أنها "تكوين".

  • Shadow Boundary: يتم ترميزها بواسطة الخط المنقط في الصورة أعلاه. هذا يدل على الفصل بين عالم DOM العادي وعالم Shadow DOM. لا يمكن للنصوص من كلا الجانبين عبور هذه الحدود وخلق الفوضى على الجانب الآخر.

مرحبا بك في عالم Shadow DOM

كفى chit-chat أقول ،هيا نجعل أيدينا تقوم بكتابة بعض الاكواد. لنفترض أن لدينا الترميز التالي ، والذي يعرض رسالة ترحيب بسيطة.

1
<div id="welcomeMessage">Welcome to My World</div>

إضافة التعليمة البرمجية JavaScript التالية أو استخدام هذه الحيلة:

1
var shadowHost = document.querySelector("#welcomeMessage");
2
var shadowRoot = shadowHost.webkitCreateShadowRoot();
3
shadowRoot.textContent = "Hello Shadow DOM World";

هنا نقوم بإنشاء Shadow Root باستخدام وظيفة webkitCreateShadowRoot () ، ونعلقها على Shadow Host ، ثم ببساطة نغيير المحتوى.

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

إذا كنت تمضي قدمًا وقمت بتشغيل هذا المثال في متصفح مدعوم ، فسترى "Hello Shadow DOM World" بدلاً من "Welcome to My World" حيث غطت عُقد Shadow DOM الدمج العادي.

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


احترام حدود الShadow

إذا حاولت الوصول إلى محتوى الشجرة التي تم عرضها باستخدام JavaScript ، مثل:

1
var shadowHost = document.querySelector("#welcomeMessage");
2
var shadowRoot = shadowHost.webkitCreateShadowRoot();
3
shadowRoot.textContent = "Hello Shadow DOM World";
4
5
console.log(shadowHost.textContent);
6
 // Prints "Welcome to My World" as the shadow DOM nodes are encapsulated and cannot be accessed by JavaScript

 ستحصل على المحتوى الأصلي "Welcome to My World" وليس المحتوى الذي يتم تقديمه فعليًا على الصفحة ، حيث يتم تغليف شجرة Shadow DOM الأساسية من أي نصوص برمجية. وهذا يعني أيضًا أن الأداة التي تقوم بإنشائها باستخدام Shadow DOM آمنة من أي نصوص برمجية غير مرغوبة / متعارضة موجودة بالفعل في الصفحة.

تغليف الأنماط

وبالمثل ، يحظر على أي محدد CSS عبور shadow boundary.  تحقق من التعليمة البرمجية التالية حيث قمنا بتطبيق اللون الأحمر على عناصر القائمة ، ولكن هذا النمط مطبق فقط على العقد التي تعد جزءًا من الصفحة الرئيسية ، ولا تتأثر عناصر القائمة التي تعد جزءًا من Shadow Root بهذا النمط.

1
<div class="outer">
2
  <div id="welcomeMessage">Welcome to My World</div>
3
  <div class="normalTree">Sample List
4
  <ul>
5
      <li>Item 1</li>
6
      <li>Item 2</li>
7
  </ul>
8
  </div>
9
</div>
10
<style>
11
   div.outer li {  
12
      color: red;  
13
   } 
14
   div.outer{  
15
      border: solid 1px;  padding: 1em; 
16
   }
17
</style>
18
<script type="text/javascript">
19
    var shadowHost = document.querySelector("#welcomeMessage");
20
    var shadowRoot = shadowHost.webkitCreateShadowRoot();
21
    shadowRoot.innerHTML = ["<div class='shadowChild'>",
22
                            "Shadow DOM offers us Encapsulation from",
23
                            "<ul>",
24
                            "<li>Scripts</li>",
25
                            "<li>Styles</li>",
26
                            "</ul>",
27
                            "</div>"
28
                            ].join(',').replace(/,/g,"");
29
</script>

يمكنك رؤية الرمزin action on Fiddle. ينطبق هذا التغليف حتى في حالة عكس اتجاه الاجتياز. لا تؤثر أي أنماط يتم تعريفها داخل DOM Shadow على المستند الأصل ، ولا يزال يتم تحديد نطاقه إلى Shadow Root فقط حدد هذا Fiddle كمثال ، حيث نطبق اللون الأزرق على عناصر القائمة في Shadow DOM ولكن عناصر قائمة المستندات الأصلية لا تتأثر.

هناك استثناء واحد ملحوظ هنا ؛ يعطينا Shadow DOM المرونة في تصميم Shadow Host ، عقدة DOM التي تحتفظ بـ Shadow DOM. من الناحية المثالية أنها تقع خارج حدود Shadow وهي ليست جزءًا من Shadow Root ، ولكن باستخدام قاعدة  @host ، يمكن تحديد الأنماط التي يمكن تطبيقها على Shadow Host حيث قمنا بتصميم رسالة الترحيب في المثال التالي.

1
<div id="welcomeMessage">Welcome to My World</div>
2
<script type="text/javascript">
3
  var shadowHost = document.querySelector("#welcomeMessage");
4
  var shadowRoot = shadowHost.webkitCreateShadowRoot();
5
  shadowRoot.innerHTML = ["<style>",
6
                          "@host{ ",
7
                             "#welcomeMessage{ ",
8
                                "font-size: 28px;",
9
                                "font-family:cursive;",
10
                                "font-weight:bold;",
11
                             "}",
12
                          "}",
13
                          "</style>",
14
                          "<content select=''></content>"
15
                          ].join(',').replace(/,/g,"");
16
</script>

تحقق من هذا  أثناء تصميم رسالة الترحيب في Shadow Host باستخدام الأنماط المحددة في Shadow DOM.

إنشاء نمط Hooks

باعتباري مطورًا لأداة التطبيقات ، قد أرغب في أن يتمكن مستخدم أداة التطبيق من تصميم عناصر معينة. هذا يمكن تحقيقه عن طريق توصيل ثقب في shadow boundary باستخدام عناصر زائفة مخصصة. يشبه ذلك كيفية إنشاء بعض المستعرضات لخطافات للمطوِّر لتصميم بعض العناصر الداخلية لعنصر واجهة أصلي. على سبيل المثال ، لتصميم الإبهام ومسار شريط التمرير الأصلي ، يمكنك استخدام :: - webkit-slider-thumb و :: webkit-slider-runnable-track على النحو التالي:

1
input[type=range]{
2
    -webkit-appearance:none;
3
 }
4
 input[type=range]::-webkit-slider-thumb {
5
    -webkit-appearance:none;
6
    height:12px;
7
    width:12px;
8
    border-radius:6px;
9
    background:yellow;
10
    position:relative;
11
    top:-5px;
12
 }
13
 input[type=range]::-webkit-slider-runnable-track {
14
    background:red;
15
    height:2px;
16
 }

شغل هذا ال Fiddle وطبق الأنماط الخاصة بك لذلك!

إعادة توجيه الحدث

إذا كان الحدث الذي ينشأ من أحد العقد في Shadow DOM يتقاطع مع Shadow Boundary، فسيتم إعادة استهدافه للإشارة إلى Shadow Host من أجل الحفاظ على التغليف. خذ بعين الاعتبار الكود البرمجي التالي:

1
<input id="normalText" type="text" value="Normal DOM Text Node" />
2
<div id="shadowHost"></div>
3
<input id="shadowText" type="text" value="Shadow DOM Node" />
4
<script type="text/javascript">
5
    var shadowHost = document.querySelector('#shadowHost');
6
    var shadowRoot = shadowHost.webkitCreateShadowRoot();
7
    var template = document.querySelector('template');
8
    shadowRoot.appendChild(template.content.cloneNode(true));
9
    template.remove();
10
    document.addEventListener('click', function(e) { 
11
                                 console.log(e.target.id + ' clicked!'); 
12
                              });
13
</script>

يعرض عنصرين إدخال نص ، أحدهما عبر DOM عادي والآخر عبر Shadow DOM ثم يستمع لحدث click في document. الآن ، عندما يتم النقر على إدخال النص الثاني ، يتم إنشاء الحدث من داخل Shadow DOM وعندما يعبرShadow boundary ، يتم تعديل الحدث لتغيير العنصر الهدف إلى عنصر div> Shadow Host بدلاً من إدخال النص< input> . لقد قدمنا أيضًا عنصر  < template> جديد هنا ؛ هذا مشابه من الناحية المفاهيمية لحلول templating client-side مثل Handlebars و Underscore ولكنها ليست متطورة وتفتقر إلى دعم المستعرض. بعد قولي هذا ، استخدام القوالب هو الطريقة المثلى لكتابة Shadow DOM بدلاً من استخدام علامات البرنامج النصي كما تم حتى الآن في جميع أنحاء هذه المقالة.


الفصل بين الاهتمامات

نحن نعلم بالفعل أنه من الأفضل دائمًا فصل المحتوى الفعلي عن العرض ؛ يجب عدم تضمين Shadow DOM لأي محتوى ، والذي سيتم عرضه أخيرًا للمستخدم. بدلاً من ذلك ، يجب أن يكون المحتوى موجودًا دائمًا على الصفحة الأصلية وليس مخفيًا داخل قالب Shadow DOM عند حدوث التكوين ، يجب أن يتم عرض هذا المحتوى في نقاط الإدراج المناسبة المحددة في قالب Shadow DOM. دعونا نعيد كتابة مثال Hello World ، مع مراعاة الفصل السابق - يمكن العثور على مثال حي على Fiddle.

1
<div id="welcomeMessage">Welcome to Shadow DOM World</div>
2
<script type="text/javascript">
3
    var shadowRoot = document.querySelector("#welcomeMessage").webkitCreateShadowRoot();
4
    var template = document.querySelector("template");
5
    shadowRoot.appendChild(template.content); 
6
    template.remove();
7
</script>

عندما يتم عرض الصفحة ، يتم عرض محتوى مضيف الظل في المكان الذي يظهر فيه عنصر < content>. هذا مثال بسيط للغاية حيث يلتقط < content> كل شيء داخل مضيف الظل أثناء التكوين. ولكن يمكن أن تكون انتقائية للغاية في اختيار المحتوى من Shadow Host باستخدام السمة select كما هو موضح أدناه

1
<div id="outer">How about some cool demo, eh ?
2
    <div class="cursiveButton">My Awesome Button</div>
3
</div>
4
<button>
5
  Fallback Content
6
</button>
7
<style>
8
button{ 
9
   font-family: cursive;  
10
   font-size: 24px;
11
   color: red; 
12
}
13
</style>
14
<script type="text/javascript">
15
    var shadowRoot = document.querySelector("#outer").webkitCreateShadowRoot(); 
16
    var template = document.querySelector("template"); 
17
    shadowRoot.appendChild(template.content.cloneNode(true));
18
    template.remove();
19
</script>

تحقق من  live demo وجرب معها للوصول الى فهم أفضل لمفهوم نقطة الإدراج والإسقاطات.


مكونات ويب

كما تعلمون بالفعل ، فإن Shadow DOM هو جزء من Web Components Spec ، والذي يقدم ميزات أنيقة أخرى ، مثل:

  1. القوالب - تستخدم للاحتفاظ برمز خامل ، والذي سيتم استخدامه في وقت لاحق. ونعني من خلال الخاملة ، أن جميع الصور الموجودة في الترميز لا يتم تنزيلها ، ولا تكون النصوص المضمنة موجودة حتى يصبح محتوى القالب فعليًا جزءًا من الصفحة.
  2. 2. الزخرفات - يتم استخدامها لتطبيق القوالب استنادًا إلى CSS Selectors ، ومن ثم يمكن اعتباره زخرفة العناصر الموجودة من خلال تحسين العرض التقديمي.
  3. استيراد HTML - يوفر لنا إمكانية إعادة استخدام مستندات HTML الأخرى في المستند دون الحاجة إلى إجراء مكالمات XHR بشكل صريح وكتابة معالجات الأحداث لها.
  4. عناصر مخصصة - تسمح لنا بتحديد أنواع عناصر HTML الجديدة التي يمكن استخدامها بعد ذلك في الترميز. على سبيل المثال ، إذا كنت ترغب في إنشاء أداة تنقل خاصة بك ، فإنك تحدد عنصر التنقل الخاص بك ، وترث من HTMLElement وتوفر بعض الاسترجاعات دورة الحياة التي تنفذ أحداثًا معينة مثل الإنشاء والتغيير وتدمير الأداة واستخدام كل هذه الأدوات في ترميزك كـ < myAwesomeNavigation attr1=" value1" ..>< /myAwesomeNavigation >. لذا تعطينا العناصر المخصصة أساسًا طريقة لتجميع كل سحر Shadow DOM ، وإخفاء التفاصيل الداخلية وحزم كل شيء معًا.

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


الاستنتاج

تعتبر Web Components Spec عبارة عن عمل قيد التقدم وتضمنت الشفرة النموذجية التي تعمل اليوم و التي قد لا تعمل في إصدار لاحق. على سبيل المثال ، تستخدم النصوص السابقة في هذا الموضوع أسلوب webkitShadowRoot () الذي لم يعد يعمل ؛ بدلاً من ذلك ، استخدم createWebkitShadowRoot () لإنشاء "Shadow Root". لذلك إذا كنت ترغب في استخدام هذا لإنشاء بعض العروض التجريبية الرائعة باستخدام Shadow DOM ، فمن الأفضل دائمًا الرجوع إلى المواصفات للحصول على التفاصيل.

في الوقت الحالي ، لا يدعم ذلك سوى Chrome وOpera ، لذلك سأكون حذراً بشأن تضمين أي من Shadow DOM على مثيلتي الإنتاجية ، ولكن مع خروج Google من Polymer الذي تم إنشاؤه على قمة مكونات الويب وملفات Polyfills القادمة لدعم Shadow DOM أصلاً ، بالتأكيد شيء يجب على كل مطور ويب أن يلطمسه.

يمكنك أيضًا متابعة آخر الأحداث على Shadow DOM من خلال اتباع Google+ Channel. تحقق أيضًا من أداة Shadow DOM Visualizer ، والتي تساعدك على تصور كيفية عرض Shadow DOM في المستعرض.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Web Design tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.