صفحات الأنماط المتتالية والتصميم
تتناول هذه المقالة المزيد من الإجراءات الرائعة التي يمكنك اتّخاذها باستخدام Shadow DOM. ويستند إلى المفاهيم التي تمت مناقشتها في Shadow DOM 101. إذا كنت تبحث عن مقدمة، يمكنك الاطّلاع على هذه المقالة.
مقدمة
لنواجه الأمر. لا يُعدّ ترميز HTML غير المنسَّق جذابًا. لحسن الحظ، توقع فريق Web Components هذا الأمر ولم يتركنا في حيرة. تحدِّد وحدة تحديد نطاق CSS العديد من الخيارات لتنسيق المحتوى في شجرة الظل.
تجميع الأنماط
من الميزات الأساسية في Shadow DOM هي حدود الظل. يحتوي على الكثير من السمات الرائعة، وأحد أفضلها هو أنّه يقدّم ميزة تجميع الأنماط مجانًا. بعبارة أخرى:
<div><h3>Light DOM</h3></div>
<script>
var root = document.querySelector('div').createShadowRoot();
root.innerHTML = `
<style>
h3 {
color: red;
}
</style>
<h3>Shadow DOM</h3>
`;
</script>
هناك ملاحظتان مثيرتان للاهتمام حول هذا العرض التوضيحي:
- هناك عناصر h3 أخرى في هذه الصفحة، ولكن العنصر الوحيد الذي يتطابق مع أداة اختيار h3، وبالتالي تم تطبيق النمط الأحمر عليه، هو العنصر الوارد في ShadowRoot. مرة أخرى، يتم استخدام الأنماط على مستوى النطاق تلقائيًا.
- لا تتداخل قواعد الأنماط الأخرى المحدّدة في هذه الصفحة والتي تستهدف علامات h3 مع المحتوى الخاص بي. ويرجع ذلك إلى أنّ أداة الاختيار لا تتجاوز حدود الظل.
ما هو المغزى من هذه القصة؟ لدينا عملية تجميع للنمط من العالم الخارجي. شكرًا Shadow DOM.
تصميم عنصر المضيف
يتيح لك الرمز :host
اختيار العنصر الذي يستضيف شجرة ظلّ وضبط أسلوبه:
<button class="red">My Button</button>
<script>
var button = document.querySelector('button');
var root = button.createShadowRoot();
root.innerHTML = `
<style>
:host {
text-transform: uppercase;
}
</style>
<content></content>
`;
</script>
من النقاط التي يجب الانتباه إليها أنّ القواعد في الصفحة الرئيسية تكون أكثر تحديدًا من قواعد :host
المحدّدة في العنصر، ولكنّها أقل تحديدًا من سمة style
المحدّدة في العنصر المضيف. يتيح ذلك للمستخدمين إلغاء التنسيق من الخارج.
لا تعمل :host
أيضًا إلا في سياق ShadowRoot، لذا لا يمكنك استخدامها خارج Shadow DOM.
يسمح لك الشكل الوظيفي :host(<selector>)
باستهداف العنصر المضيف إذا كان يتطابق مع <selector>
.
مثال: لا تتم المطابقة إلا إذا كان العنصر نفسه يحتوي على الفئة .different
(مثل <x-foo class="different"></x-foo>
):
:host(.different) {
...
}
التفاعل مع حالات المستخدم
من حالات الاستخدام الشائعة لـ :host
هي عند إنشاء عنصر مخصّص وتريد التفاعل مع حالات المستخدِم المختلفة (:hover و:focus و:active وما إلى ذلك).
<style>
:host {
opacity: 0.4;
transition: opacity 420ms ease-in-out;
}
:host(:hover) {
opacity: 1;
}
:host(:active) {
position: relative;
top: 3px;
left: 3px;
}
</style>
تخصيص مظهر عنصر
تتطابق الفئة الزائفة :host-context(<selector>)
مع العنصر المضيف إذا كان هو أو أي من أسلافه يتطابق مع <selector>
.
من الاستخدامات الشائعة لعنصر :host-context()
هي إضفاء مظهر على عنصر استنادًا إلى العناصر المحيطة به. على سبيل المثال،
يطبّق الكثير من الأشخاص المظهر من خلال تطبيق فئة على <html>
أو <body>
:
<body class="different">
<x-foo></x-foo>
</body>
يمكنك :host-context(.different)
لتصميم <x-foo>
عندما يكون عنصرًا فرعيًا لعنصر يحمل الفئة .different
:
:host-context(.different) {
color: red;
}
يمنحك ذلك إمكانية تضمين قواعد الأنماط في Shadow DOM للعنصر الذي يمنح العنصر أسلوبًا فريدًا استنادًا إلى سياقه.
إتاحة أنواع مضيفين متعددة من داخل جذر ظل واحد
هناك استخدام آخر لعنصر :host
وهو إذا كنت تنشئ مكتبة مخصّصة لتنسيقات المواضيع وتريد
إتاحة إمكانية تنسيق العديد من أنواع عناصر المضيف من داخل Shadow DOM نفسه.
:host(x-foo) {
/* Applies if the host is a <x-foo> element.*/
}
:host(x-foo:host) {
/* Same as above. Applies if the host is a <x-foo> element. */
}
:host(div) {
/* Applies if the host element is a <div>. */
}
تصميم العناصر الداخلية في Shadow DOM من الخارج
إنّ العنصر الاصطناعي ::shadow
وعامل الربط /deep/
هما بمثابة سيف Vorpal لسلطة CSS.
وتسمح هذه العناصر بالمرور عبر حدود Shadow DOM لتنسيق العناصر ضمن أشجار الظل.
العنصر الصوري ::shadow
إذا كان العنصر يتضمّن شجرة ظل واحدة على الأقل، يتطابق العنصر النائب ::shadow
مع الجذر المطابق للظل نفسه.
ويسمح لك بكتابة محدّدات تنشئ أنماطًا للعقد الداخلية في عنصر نموذج الظل.
على سبيل المثال، إذا كان العنصر يستضيف جذر ظل، يمكنك كتابة #host::shadow span {}
لتنسيق جميع النطاقات ضمن شجرة الظل.
<style>
#host::shadow span {
color: red;
}
</style>
<div id="host">
<span>Light DOM</span>
</div>
<script>
var host = document.querySelector('div');
var root = host.createShadowRoot();
root.innerHTML = `
<span>Shadow DOM</span>
<content></content>
`;
</script>
مثال (العناصر المخصّصة): يحتوي <x-tabs>
على <x-panel>
عنصر ثانوي في Shadow DOM. تستضيف كل لوحة شجرة ظلّ خاصة بها تحتوي على h2
عنوان. لتنسيق هذه العناوين من الصفحة الرئيسية، يمكن كتابة ما يلي:
x-tabs::shadow x-panel::shadow h2 {
...
}
المُركّب /deep/
يشبه تركيب /deep/
تركيب ::shadow
، ولكنه أكثر فعالية. ويتجاهل هذا الإجراء تمامًا جميع حدود الظلال وينتقل إلى أي عدد من أشجار الظلال. ببساطة، تتيح لك /deep/
التوغّل في عنصر واستهداف أي عقدة.
يكون المُركّب /deep/
مفيدًا بشكل خاص في عالم العناصر المخصّصة حيث يكون من الشائع توفُّر مستويات متعدّدة من Shadow DOM. ومن الأمثلة الرئيسية على ذلك تداخل مجموعة من العناصر المخصّصة (يستضيف كلّ منها شجرة ظلّ خاصة به) أو إنشاء عنصر يكتسِب سمات من عنصر آخر باستخدام <shadow>
.
مثال (عناصر مخصّصة) - اختَر جميع عناصر <x-panel>
التي تنحدر من
<x-tabs>
، في أيّ مكان في الشجرة:
x-tabs /deep/ x-panel {
...
}
مثال: يمكنك تطبيق نمط على جميع العناصر التي تحمل الفئة .library-theme
في أي مكان في شجرة الظل:
body /deep/ .library-theme {
...
}
العمل مع querySelector()
تمامًا مثلما يفتح .shadowRoot
الأشجار الظلّية للتنقّل في نموذج DOM، تفتح العناصر المجمّعة الأشجار الظلّية للتنقّل في أداة الاختيار.
بدلاً من كتابة سلسلة متداخلة من الأوامر، يمكنك كتابة عبارة واحدة:
// No fun.
document.querySelector('x-tabs').shadowRoot
.querySelector('x-panel').shadowRoot
.querySelector('#foo');
// Fun.
document.querySelector('x-tabs::shadow x-panel::shadow #foo');
تصميم العناصر المدمجة مع المحتوى
إنّ عناصر التحكّم الأصلية بتنسيق HTML صعبة التصميم. يتخلّى الكثير من الأشخاص عن استخدام هذه الميزة وينشئون خططهم الخاصة. ومع ذلك، باستخدام ::shadow
و/deep/
، يمكن تنسيق أي عنصر في منصة الويب الذي
يستخدم Shadow DOM. من الأمثلة الرائعة أنواع <input>
و<video>
:
video /deep/ input[type="range"] {
background: hotpink;
}
إنشاء عناصر ربط الأنماط
التخصيص جيد. في بعض الحالات، قد تحتاج إلى إجراء تعديلات على درع تنسيق الظل وإنشاء عناصر ربط ليتمكّن الآخرون من تنسيق الظل.
استخدام ::shadow و /deep/
هناك الكثير من الإمكانات في /deep/
. وتوفّر هذه السمة لمؤلفي المكوّنات طريقة لتحديد
العناصر الفردية على أنّها قابلة للتنسيق أو مجموعة من العناصر على أنّها قابلة للتنسيق حسب المظهر.
مثال: يمكنك تطبيق نمط على جميع العناصر التي تحتوي على الفئة .library-theme
، مع تجاهل جميع الأشجار الظلّية:
body /deep/ .library-theme {
...
}
استخدام العناصر الزائفة المخصّصة
يحدِّد كلّ من WebKit و
Firefox عناصر زائفة لتصميم الأجزاء الداخلية لعناصر المتصفّح الأصلية. ومن الأمثلة الجيدة على ذلك input[type=range]
. يمكنك تصميم مؤشر شريط التمرير <span style="color:blue">blue</span>
من خلال استهداف ::-webkit-slider-thumb
:
input[type=range].custom::-webkit-slider-thumb {
-webkit-appearance: none;
background-color: blue;
width: 10px;
height: 40px;
}
على غرار الطريقة التي توفّر بها المتصفّحات عناصر ربط التنسيق في بعض العناصر الداخلية، يمكن لمؤلفي محتوى Shadow DOM تحديد عناصر معيّنة يمكن لعناصر خارجية تطبيق تنسيق عليها. ويتم ذلك من خلال العناصر الزائفة المخصّصة.
يمكنك تحديد عنصر على أنّه عنصر وهمي مخصّص باستخدام السمة pseudo
.
يجب إضافة البادئة "x-" إلى قيمته أو اسمه. يؤدي ذلك إلى إنشاء
ارتباط بهذا العنصر في شجرة الظلّ ومنح الجهات الخارجية
ممرًا محدّدًا لعبور حدود الظلّ.
في ما يلي مثال على إنشاء أداة مخصّصة للشرائح التمرير والسماح لأحد المستخدمين بتنسيق مؤشر الشريحة التمرير باللون الأزرق:
<style>
#host::x-slider-thumb {
background-color: blue;
}
</style>
<div id="host"></div>
<script>
var root = document.querySelector('#host').createShadowRoot();
root.innerHTML = `
<div>
<div pseudo="x-slider-thumb"></div>' +
</div>
`;
</script>
استخدام متغيّرات CSS
يمكنك استخدام متغيّرات CSS لإنشاء عناصر ربط فعّالة لتنسيقات التطبيق. في الأساس، يتم إنشاء "عناصر نائبة للأسلوب" ليملؤها المستخدمون الآخرون.
تخيل مؤلف عنصر مخصّص يحدّد العناصر النائبة للمتغيّرات في Shadow DOM. أحدهما لتنسيق خط الزر الداخلي والآخر للون:
button {
color: var(--button-text-color, pink); /* default color will be pink */
font-family: var(--button-font);
}
بعد ذلك، يحدّد مُضمِّن العنصر هذه القيم حسب رغبته. ربما لمطابقة مظهر Comic Sans الرائع في صفحته:
#host {
--button-text-color: green;
--button-font: "Comic Sans MS", "Comic Sans", cursive;
}
نظرًا لطريقة اكتساب متغيّرات CSS للقيم، كلّ شيء على ما يرام ويعمل بشكل رائع. تظهر الصورة الكاملة على النحو التالي:
<style>
#host {
--button-text-color: green;
--button-font: "Comic Sans MS", "Comic Sans", cursive;
}
</style>
<div id="host">Host node</div>
<script>
var root = document.querySelector('#host').createShadowRoot();
root.innerHTML = `
<style>
button {
color: var(--button-text-color, pink);
font-family: var(--button-font);
}
</style>
<content></content>
`;
</script>
إعادة ضبط الأنماط
تستمر الأنماط القابلة للتوريث، مثل الخطوط والألوان وارتفاعات السطور، في التأثير على العناصر
في Shadow DOM. ومع ذلك، للحصول على أقصى قدر من المرونة، يمنحنا Shadow DOM سمة
resetStyleInheritance
للتحكّم في ما يحدث عند حدود الظل.
يمكنك اعتبارها طريقة لبدء تصميم جديد عند إنشاء مكوّن جديد.
resetStyleInheritance
false
- الإعداد التلقائي. تستمر خصائص CSS القابلة للتوريث في اكتساب القيمة.true
- تُعيد ضبط السمات القابلة للتوريث علىinitial
عند الحدود.
في ما يلي عرض توضيحي يوضّح مدى تأثُّر شجرة الظلال بتغيير resetStyleInheritance
:
<div>
<h3>Light DOM</h3>
</div>
<script>
var root = document.querySelector('div').createShadowRoot();
root.resetStyleInheritance = <span id="code-resetStyleInheritance">false</span>;
root.innerHTML = `
<style>
h3 {
color: red;
}
</style>
<h3>Shadow DOM</h3>
<content select="h3"></content>
`;
</script>
<div class="demoarea" style="width:225px;">
<div id="style-ex-inheritance"><h3 class="border">Light DOM</div>
</div>
<div id="inherit-buttons">
<button id="demo-resetStyleInheritance">resetStyleInheritance=false</button>
</div>
<script>
var container = document.querySelector('#style-ex-inheritance');
var root = container.createShadowRoot();
//root.resetStyleInheritance = false;
root.innerHTML = '<style>h3{ color: red; }</style><h3>Shadow DOM<content select="h3"></content>';
document.querySelector('#demo-resetStyleInheritance').addEventListener('click', function(e) {
root.resetStyleInheritance = !root.resetStyleInheritance;
e.target.textContent = 'resetStyleInheritance=' + root.resetStyleInheritance;
document.querySelector('#code-resetStyleInheritance').textContent = root.resetStyleInheritance;
});
</script>
إنّ فهم .resetStyleInheritance
أكثر تعقيدًا، ويرجع ذلك أساسًا إلى أنّه
لا يؤثر إلا في خصائص CSS القابلة للتوريث. تشير العبارة إلى أنّه عند
البحث عن خاصية للتوريث، عند الحدّ بين الصفحة
وShadowRoot، لا ترث القيم من المضيف، ولكن استخدِم القيمة initial
بدلاً من ذلك (وفقًا لمواصفات CSS).
إذا لم تكن متأكّدًا من السمات التي يتم اكتسابها في CSS، يمكنك الاطّلاع على هذه القائمة المفيدة أو تفعيل مربّع الاختيار "عرض السمات المكتسَبة" في لوحة "العناصر".
تصميم العقد الموزّعة
العقد الموزّعة هي عناصر يتم عرضها في نقطة إدراج (عنصر <content>
). يتيح لك عنصر <content>
اختيار عقد من Light DOM وعرضها في مواضع محدّدة مسبقًا في Shadow DOM. ولا تكون هذه العناصر منطقيًا في Shadow DOM، بل تظلّ عناصر ثانوية للعنصر المضيف. نقاط الإدراج هي مجرد عملية عرض.
تحتفظ العقد الموزَّعة بالأنماط من المستند الرئيسي. وهذا يعني أنّ قواعد الأنماط من الصفحة الرئيسية تستمر في تطبيقها على العناصر، حتى عند عرضها في نقطة إدراج. مرة أخرى، تظلّ العقد الموزّعة منطقيًا في النطاق الخفيف ولا تتحرّك. ويتم عرض الإعلانات في مكان آخر. ومع ذلك، عند توزيع العقد في Shadow DOM، يمكن أن تتّخذ أنماطًا إضافية يتم تحديدها داخل شجرة الظل.
العنصر النائب ::content
العناصر الموزّعة هي عناصر ثانوية للعنصر المضيف، فكيف يمكننا استهدافها
من داخل Shadow DOM؟ الإجابة هي العنصر الصوري ::content
في CSS.
وهي طريقة لاستهداف عقد Light DOM التي تمرّ عبر نقطة إدراج. على سبيل المثال:
تصفِّح ::content > h3
أي علامات h3
تمرّ عبر نقطة إدراج.
لنطّلِع على مثال:
<div>
<h3>Light DOM</h3>
<section>
<div>I'm not underlined</div>
<p>I'm underlined in Shadow DOM!</p>
</section>
</div>
<script>
var div = document.querySelector('div');
var root = div.createShadowRoot();
root.innerHTML = `
<style>
h3 { color: red; }
content[select="h3"]::content > h3 {
color: green;
}
::content section p {
text-decoration: underline;
}
</style>
<h3>Shadow DOM</h3>
<content select="h3"></content>
<content select="section"></content>
`;
</script>
إعادة ضبط الأنماط عند نقاط الإدراج
عند إنشاء ShadowRoot، يمكنك إعادة ضبط الأنماط المُكتسَبة.
تتوفّر نقاط الإدراج <content>
و<shadow>
أيضًا لهذا الخيار. عند استخدام
هذه العناصر، يمكنك ضبط .resetStyleInheritance
في JavaScript أو استخدام السمة الحقيقية/الكاذبة
reset-style-inheritance
على العنصر نفسه.
بالنسبة إلى نقاط إدراج ShadowRoot أو
<shadow>
:reset-style-inheritance
يعني ذلك أنّه يتم ضبط خصائص CSS القابلة للتوريث علىinitial
في المضيف، قبل أن تصل بالمحتوى المطابق. ويُعرف هذا الموقع الجغرافي باسم الحدّ الأقصى.بالنسبة إلى نقاط الإدراج
<content>
: يشير الرمزreset-style-inheritance
إلى أنّه تم ضبط سمات CSS القابلة للتوريث علىinitial
قبل توزيع عناصر المستضيف في نقطة الإدراج. ويُعرف هذا الموقع الجغرافي باسم الحدّ الأدنى.
الخاتمة
بصفتنا مؤلفين للعناصر المخصّصة، تتوفّر لدينا الكثير من الخيارات للتحكّم في مظهر المحتوى وأسلوبه. تشكّل Shadow DOM الأساس لهذا العالم الجديد.
يوفّر لنا Shadow DOM طريقة لتغليف الأنماط على مستوى النطاق ووسيلة للسماح بقدر ما نختاره (أو القليل) من العالم الخارجي. من خلال تحديد عناصر وهمية مخصّصة أو تضمين عناصر نائبة متغيّرة في CSS، يمكن للمؤلفين توفير عناصر ربط مناسبة لجهات خارجية لمزيد من تخصيص المحتوى. بوجهٍ عام، يتحكّم مؤلفو الويب بشكل كامل في طريقة عرض المحتوى الخاص بهم.