تجدر بنا الإشارة إلى قاعدة مهمة قبل الشروع في الحديث عن كيفية تعامل جافاسكربت مع الأنماط والأصناف. القاعدة بديهية جدًا ولكننا سنذكرها للإفادة. هناك طريقتان تُستخدمان لتنسيق عنصرٍ ما:
-
إنشاء صنف في ملف CSS وإضافته للعنصر على الشكل التالي:
<div class="...">
-
كتابة خاصيات السمة
style
مباشرة بين تسلسلات التهريب الخاصة بالعنصر على الشكل التالي:<div style="...">
تستطيع لغة جافاسكربت التعديل على خاصيات الأصناف وخاصيات الخاصية style
. ويُستحسن استعمال أصناف CSS للتنسيق بدلا من السمة style
، حيث نلجأ إلى الطريقة الثانية فقط إذا تعذّرعلينا إضافة التنسيق باستعمال الطريقة الأولى.
فعلى سبيل المثال، يمكن استعمال الخاصية style
إذا كان عليك حساب إحداثيات عنصر ما ديناميكيًا وتحديد قيمها باستعمال جافاسكربت كالآتي:
let top = /* عمليات حسابية معقّدة */; let left = /* عمليات حسابية معقّدة */; elem.style.left = left; // e.g '123px', تُحسب أثناء التنفيذ elem.style.top = top; // e.g '456px'
وفي حالات أخرى مثل تلوين النص بالأحمر، أو إضافة صورة للخلفية، يُستحسن وصف التنسيق باستعمال CSS ثم إضافة الصنف للعنصر (يمكن عمل ذلك باستعمال جافاسكربت)، حيث يمنحك ذلك أكثر مرونة وسهولة في البرمجة.
اسم الصنف (className) وقائمة الأصناف (classList)
يُعدّ تعديل الصنف أكثر عمليةٍ نُصادفها في السكربتات. وكانت جافاسكربت فيما مضى تتّسمُ بالمحدودية حين يتعلّق الأمر بالكلمة المحجوزة "class"
، حيث لم تكن تسمح بأن تحمل خاصيةٌ من خواص الكائن (object) اسم "class"
كالآتي : elem.class
. حينها جاء التفكير في استحداث خاصية مشابهة تسمى "className"
تُطبّق على الأصناف. حيث يمثِّل elem.className
اسم السمة "class"
كما هو مبين في المثال التالي:
<body class="main page"> <script> alert(document.body.className); // main page </script> </body>
في حالة ما إذا أسندنا قيمةً معينةً للخاصية elem.className
، تعوِّض هذه القيمة مُجمل سلسلة الأصناف. هذا ما نحتاج إليه أحيانا، ولكننا نحتاج في غالبية الحالات إلى إضافة أو حذف صنفٍ واحدٍ فقط. ولهذا وُجدت خاصية أخرى؛ إنها خاصية elem.classList
. تُعدّ هذه الخاصية كائنًا خاصًا بحد ذاته له دوالّه الخاصة (methods) لإضافة صنف ما (add
) أو حذفه (remove
) أو إما إضافته إن لم يكن موجودًا أو حذفه إن وُجد (toggle
).كما هو مبين في المثال التالي:
<body class="main page"> <script> // إضافة صنف document.body.classList.add('article'); alert(document.body.className); // main page article </script> </body>
وبذلك يمكننا إجراء عمليات على مجمل سلسلة الأصناف دفعة واحدة باستعمال className
أو على الأصناف، كلٌّ على حدى، باستعمال classList
. اختيارنا لهذا أو ذاك مرتبط بما نحتاج القيام به.
الدوالّ الخاصة بـالخاصية classList
هي: *elem.classList.add/remove("class")
: إضافة الصنف المذكور كوسيط للدلّة/حذف الصنف المذكور كوسيط للدلّة. *elem.classList.toggle("classe")
: إضافة الصنف المذكور كوسيط للدلّة إن لم يكن موجودًا أو حذفه إن وُجد. *elem.classList.contains("class")
: البحث عن الصنف المذكور كوسيط للدلّة، والنتيجة تكون صحيح أو خطأ (true/false).
وتقبل الخاصية classList
الإدماج داخل حلقة التكرار for....of
لإظهار قائمة الأصناف كما في المثال التالي:
<body class="main page"> <script> for (let name of document.body.classList) { alert(name); // main, and then page } </script> </body>
تنسيق العنصر باستعمال الخاصية style
تُعدّ الخاصية elem.style
كائنًا يحمل محتوى السمة style
. ويؤدي إسناد القيمة "100px"
للخاصية elem.style.width
على الشكل التالي: elem.style.width="100px"
إلى النتيجة نفسها لو كانت السمة style
تحمل السلسلة النصية "width:100px"
.
إذا كان اسم السمة يتكون من عدة كلمات، يُشكَّل اسم الخاصية بجعل الحرف الأول من كل كلمة حرفا كبيرًا ماعدا الكلمة الأولى كما في المثال التالي:
background-color => elem.style.backgroundColor z-index => elem.style.zIndex border-left-width => elem.style.borderLeftWidth
ملاحظة: الخاصيات التي تبدأ ببادئة
تتبع الخاصيات التي تبدأ ببادئة تُحدّد المتصفح نفس القاعدة، كالخاصيتين -moz-border-radius
و-webkit-border-radius
، حيث تُترجَم الشرطة إلى حرفٍ كبيرٍ كالآتي:
button.style.MozBorderRadius = '5px'; button.style.WebkitBorderRadius = '5px';
تغيير قيمة خاصية التنسيق style
يحدث أن تَرغب في إسناد قيمةٍ للخاصية style
ثم حذفها لاحقا. يمكننا على سبيل المثال إسناد القيمة "none"
للخاصية elem.style.display
كالأتي elem.style.display = "none"
ثم حذفها وكأننا لم نحدّد لها قيمةً من قبل. هنا، ينبغي إسناد سلسلة نصية فارغة للخاصية elem.style.display
كالآتي elem.style.display = ""
بدلا من حذفها (delete
).
//عند تنفيذ هذا السكربت يختفي العنصر <body> ثمّ يُعاود الظهور document.body.style.display = "none"; // يختفي setTimeout(() => document.body.style.display = "", 1000); // يُعاود الظهور
إذا أسندنا سلسلة نصية فارغة للخاصية style.display
، يُطبِّق المتصفح أصناف CSS والأنماط التنسيقية المتضمَّنة بداخلها بطريقة عاديةٍ جدًا وكأن الخاصية style.display
غير موجودة تماما.
ملاحظة: التعديل على خاصيات الخاصية style
جملةً واحدةً باستعمال الخاصية style.cssStyle
تُستعمل عادة الخاصية *.style
للتعديل على قيم خاصيات التنسيق، كلٌ على حدى، ولا يمكننا التعديل عليها دفعة واحدة كالآتي: div.style="color:red; width:100px"
، لأن div.style
هو كائنٌ لا يمكن التعديل عليه (ِread-only) بهذه الطريقة.
يمكن تغيير التنسيق كاملا دفعة واحدة بإسناد سلسلة نصية (تحمل وصف التنسيق) للخاصية style.cssStyle
كما في المثال التالي:
<div id="div">Button</div> <script> // يمكننا استعمال رايات تنسيقية خاصة مثل الراية “important” div.style.cssText=`color: red !important; background-color: yellow; width: 100px; text-align: center; `; alert(div.style.cssText); </script>
غير أنه من النادر استعمال هذه الخاصية كونها تحذف الأنماط التنسيقية السابقة وتستبدلها بالقيم الجديدة، أي أنها قد تحذف أشياء مازلنا بحاجتها. فيما يمكن أن تُستخدم لتنسيق العناصر الجديدة، فلن يؤدي إسناد القيم بهذه الطريقة إلى أيّ عملية حذف (بما أن العناصر الجديدة لا تملك تنسيقات بعد). ويمكننا عمل ذلك أيضا باستعمال الدالّة div.setAttribute('style', 'color: red...')
.
الوحدات
لا تنس إضافة الوحدات للقيم في شيفرة CSS، فلا يصِحُّ إسناد القيمة "10" للخاصية elem.style.top
بل القيمة 10px
هي الأصح، وإلا فلن يعمل السكربت بالشكل المطلوب.
<body> <script> // لا يعمل document.body.style.margin = 20; alert(document.body.style.margin); // '' (سلسلة نصية فارغة، إهمال عملية الإسناد) // يعمل بعد إضافة الوحدة document.body.style.margin = '20px'; alert(document.body.style.margin); // 20px alert(document.body.style.marginTop); // 20px alert(document.body.style.marginLeft); // 20px </script> </body>
لاحظ في السطرين الأخيرين أن المتصفح يفكّك الخاصية style.margin
إلى خاصيتين وهما: style.marginLeft
وstyle.marginTop
.
الأنماط المحسوبة باستعمال الدالة getComputedStyle
يُعدّ التعديل على الأنماط عمليةً سهلةً ولكن كيف تُقرأ الأنماط؟ نريد مثلا معرفة مقاس، هوامش ولون عنصرٍ ما، كيف نتحصل عليها؟
تعمل الخاصية style
على تعديل قيمة السمة style
فقط دون الوصول إلى الأنماط الموصوفة في الأوراق التنسيقية المتتالية CSS. وبالتالي لا يمكننا قراءة أيّ قيمٍ من أصناف CSS باستعمال الخاصية elem.style
. فعلى سبيل المثال لا يمكن أن تصل الخاصية style
في هذا المثال إلى الهامش.
<head> <style> body { color: red; margin: 5px } </style> </head> <body> The red text <script> alert(document.body.style.color); // فارغة alert(document.body.style.marginTop); // فارغة </script> </body>
ماذا لو أردنا على سبيل المثال إضافة 20px للهامش؟ سيكون علينا أولا الوصول إلى القيمة الحالية له حتى يتسنى لنا تعديلها. وهنا لدينا طريقة أخرى للحصول على ذلك وتكون باستعمال الدالة getComputedStyle
وبنيتها كالآتي:
getComputedStyle(element, [pseudo])
حيث يمثّل العنصر element
العنصر الذي سنحسب قيمه ويمثل pseudo
العنصر الزائف، مثلا before::
. إذا كانت قيمة pseudo
عبارة عن سلسلة نصية فارغة أو غير موجودة أصلا فهذا يعني أننا نقصد العنصر نفسه. وتكون مخرجات الدالّة (output) عبارة عن كائن يحوي أنماط تنسيقية مثله مثل elem.style
ولكن يأخذ في الحسبان هذه المرة كلّ الأصناف الموجودة في ملف CSS. وفيما يلي مثال على ذلك:
<head> <style> body { color: red; margin: 5px } </style> </head> <body> <script> let computedStyle = getComputedStyle(document.body); // يمكننا الآن قراءة اللون والهامش alert( computedStyle.marginTop ); // 5px alert( computedStyle.color ); // rgb(255, 0, 0) </script> </body>
ملاحظة: القيم المحسوبة والقيم النهائية (المُحدَّدة)
هناك مفهومان في لغة CSS هما:
-
قيمة تنسيقية محسوبة وهي القيمة المتحصّل عليها بعد تطبيق مجمل القواعد التنسيقية وقواعد الوراثة المُتضمَّنة في ملف CSS. قد تكون على شكل
height:1em
أوfont-size:125%
. -
قيمة تنسيقية نهائية (مُحدَّدة) وهي القيمة التي يقع عليها الاختيار في آخر المطاف وتُطبَّق على العنصر. القيمتان
1em
و125%
هما قيمتان نسبيتان. يأخذ المتصفح كافة القيم المحسوبة ويجعل كافة الوحدات مطلقة كما في المثال التالي:height:20px
،font-size:16px
. ويمكن للقيم النهائية الخاصة بالخاصيات الهندسية أن تكون عشرية مثل:width:50.5px
.
لقد اُستحدثت الدالّة getComputedStyle
أساسا للحصول على قيم محسوبة ولكن تبيّن فيما بعد أن القيم النهائية (المُحدَّدة) أحسن، فتغيرت المعايير، وأصبحت الدالّة getComputedStyle
تُخرِج القيم النهائية للخاصية والتي تكون غالبا بالبكسل px بالنسبة للخاصيات الهندسية.
ملاحظة: تتطلب الدالة getComputedStyle
ذكر الاسم الكامل للخاصية
علينا البحث على الدوام عن الخاصية التي نحتاج إليها بدقة مثل: padingLeft، أو marginTop أو borderTopWidth وإلا فلن نتمكن من ضمان صحة النتيجة المتحصّل عليها. فمع وُجود، على سبيل المثال، الخاصيتين padingLeft/padingTop، على ماذا سوف نحصل عند تنفيذ الدالّة getComputedStyle(elem, pading)
؟
لن نحصل على شيئ؟ أو ربما سنحصل على قيمة "مستوحاة" من قيم معرّفة مسبقا للحاشية (pading)؟ في الحقيقة لا يوجد أيّ معايير تتحدث عن هذا الموضوع.
وهناك بعض التناقضات الأخرى، حيث تُظهِر بعض المتصفحات (Chrome مثلا) في مثال الموالي القيمة 10px
، ولا تُظهِرها متصفحات أخرى (كالمتصفح Firefox).
مثال:
<style> body { margin: 10px; } </style> <script> let style = getComputedStyle(document.body); alert(style.margin); // نحصل على سلسلة فارغة عند استعمال المتصفح Firefox </script>
ملاحظة: الأنماط التي تُطبَّق على الروابط :visited
تكون مخفية
يمكن تلوين الروابط التي سبق وأن زيرت باستخدام الصنف الزائف :visited
في ملف CSS. لكن الدالّة getComputedStyle
لا يمكنها الوصول إلى هذا اللون، لأن ذلك يمكِّن أيّ صفحة كانت من إنشاء الرابط على الصفحة والإطلاع على الأنماط وبالتالي معرفة ما إذا كان المستخدم قد زار الرابط من قبل.
لا يمكن للغة جافاسكربت الإطلاع على الأنماط المعرّفة باستخدام الصنف الزائف :visited
، كما تمنع لغة CSS تطبيق تنسيقاتِ تغيير الشكل والأبعاد (geometry-changing styles) ضمن الصنف الزائف :visited
وذلك لغلق الطريق أمام أيّ صفحةٍ مشبوهةٍ تسعى لمعرفة ما إذا زار المستخدم الرابط أم لا، وبالتالي التعدي على خصوصيته.
الخلاصة
هناك خاصيتان تُستخدمان للعمل على الأصناف وهما:
-
className
: وهي سلسلة نصية تُستخدم للعمل على كافة الأصناف دفعةً واحدةً. -
classList
: هي عبارة عن كائن له دوالّه الخاصة (add/delete/toggle/contains) وتستخدم للعمل على الأصناف، كلُ على حدى.
ولتغيير التنسيق لدينا:
-
الخاصية
style
؛ وهي عبارة عن كائن تُشكَّل خواصه بجعل الحرف الأول من كل كلمة حرفا كبيرًا ما عدا الكلمة الأولى. تُعدّ قراءته والتعديل عليه تماما كالتعديل على خاصيات السمةstyle
، كلٌ على حدى. وللاطلاع على كيفية إضافة الراية important وغيرها، يمكنك زيارة موقع MDN حيث تجد قائمة من الدوالّ التي تُستخدم لذلك. -
الخاصية
style.cssText
: هي الخاصية التي تقابِل السمة"style"
في مجملها، أي السلسلة النصية التي تحمل كافة الأنماط التنسيقية دفعةً واحدةً.
ولقراءة الأنماط التنسيقية النهائية (التي تأخذ في الحسبان كافة الأصناف بعد تطبيق تنسيقات CSS وحساب القيم النهائية) وُجدَت الدالة getComputedStyle(elem, [pseudo])
والتي تخرِج/تعيد كائنا يحمل التنسيقات وهو قابلٌ للقراءة فقط.
تمرين
إنشاء إشعار
درجة الأهمية: 5
اكتب شيفرة الدالّة showNotification(options)
التي تُنشِئ إشعارًا كالآتي:
// أظهِر عنصرًا يحمل النص "Hello" بالقرب من الركن العلوي الأيمن للنافذة showNotification({ top: 10, // عشرة بكسل بدءًا من أعلى النافذة والذي فاصلته 0 right: 10, // عشرة بكسل بدءًا من الحافة اليمنى للنافذة والتي ترتيبتها 0 html: "Hello!", // شيفرة HTML الخاصة بالإشعار className: "welcome" // صنف إضافي للحاوية ‘div’ (اختياري) });
استعمل تنسيقات CSS لتحديد موضع إظهار العنصر حسب الإحداثيات المعطاة بافتراض أن الصفحة تحتوي مسبقا على الأنماط التنسيقية الضرورية.
الحل
ترجمة -وبتصرف- للفصل Styles and classes من كتاب Browser: Document, Events, Interfaces
أفضل التعليقات
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.