عندما يُحمِّل المتصفّح الصّفحة، فهو يقرأ/يحلّل شيفرة HTML ويُنشئ من خلالها كائنات DOM. وحين يتعلّق الأمر بالعُقد العناصرية (element nodes)، فإن غالبية سمات HTML المعيارية تصبح خاصيات للكائنات DOM.
فعلى سبيل المثال، إذا كانت السمة كالتالي: <"body id="page>
، يكتسب كائن DOM الخاصية "body.id="page
. لكن عملية الربط بين السمات والخاصيات لا تتم تقابليا (كلّ سمة لا يقابلها بالضرورة خاصية). فيما يلي ستتعرف على كيفية التمييز بين الْمفهومَيْن، لتتعلم طريقة استخدامهما حين يتطابقان وحين يختلفان.
خاصيات DOM
سبق وأن تعرفت على خاصيات نموذج تمثيل المستند ككائن (أي خاصيات DOM) المبنيّة مسبقًا. هناك العديد منها ولكن ذلك لا يمنع المبرمج من إضافة/إنشاء خاصيات أخرى إذا لم تكن الخاصيات المبنيّة مسبقًا كافية لتلبية حاجاته. تُعدّ عُقد DOM كائنات جافاسكربت عادية يمكنك التعديل عليها.
فلننشئ على سبيل المثال خاصيّة جديدة للكائن document.body
:
document.body.myData = { name: 'Caesar', title: 'Imperator' }; alert(document.body.myData.title); // Imperator
كما يمكن إضافة دالّة أيضًا:
document.body.sayTagName = function() { alert(this.tagName); }; document.body.sayTagName(); // BODY (قيمة this في الدالة هي document.body)
يمكن أيضًا تعديل النماذج المبنيّة مسبقًا مثل Element.prototype
وإضافة دوالّ جديدة تُطبّق على كلّ العناصر.
Element.prototype.sayHi = function() { alert(`Hello, I'm ${this.tagName}`); }; document.documentElement.sayHi(); // Hello, I'm HTML document.body.sayHi(); // Hello, I'm BODY
تسلُك خاصيات ودوالّ DOM إذا سلوكًا مماثلًا لسلوك كائنات جافاسكربت العاديّة ويمكنها استقبال أيّ قيمةٍ كانت. فهي تخضع لشرط التمييز بين الحروف الكبيرة والحروف الصغيرة (فنكتب مثلا elem.nodeType
، ولا نكتب elem.NodeType
).
سمات HTML
يمكن أن تملك وسوم HTML سمات، ويتعرف المتصفّح على السمات المعيارية عندما يقرأ الوسوم ويحلّلها لإنشاء خاصيات لكائنات DOM المقابلة لها.
وبذلك، عندما يكون للعنصر السمة id
أوأيّ سمة معيارية أخرى، تُنشَأُ الخاصية الموافقة لها. ولكن ذلك لن يحدث إذا كانت الخاصية غير معيارية.
انظر المثال التالي:
<body id="test" something="non-standard"> <script> alert(document.body.id); // test // السمة غير المعيارية لا تولّد خاصية مقابلة لها alert(document.body.something); // undefined </script> </body>
لاحظ أن سمةً ما قد تكون معياريةً لعناصر دون غيرها وغير مُعرّفةٍ إذا تعلّق الأمر بعناصر أخرى. فمثلًا تُعدّ السمة "type"
سمةً معياريةً للعنصر <input>
(HTMLInputElement) وغير معيارية للعنصر <body>
(HTMLBodyElement). وستجد وصفًا للسمات المعيارية في مواصفات صنف كل عنصر.
والمثال التالي يوضّح ذلك:
<body id="body" type="..."> <input id="input" type="text"> <script> alert(input.type); // text alert(body.type); // undefined لم تٌنشأ خاصية DOM لأنها غير معيارية // </script> </body>
وبالتالي إذا كانت السمة غير معيارية، لا تٌنشأ خاصية DOM الموافقة لها. فهل من طريقة تسمح بالوصول إلى هذه السمات؟ بالتأكيد، يمكن الوصول إلى جميع السمات باستعمال الدوالّ التالية:
-
(elem.hasAttribute(name
: للتحقّق من وجود السمة. -
(elem.getAttribute(name
: للحصول على قيمة السمة. -
(elem.setAttribute(name, value
: إسناد القيمةvalue
للسمةname
. -
(elem.removeAttribute(name
: حذف السمة.
وتُستخدم هذه الدوالّ مع السمات غير المعيارية كما هي مكتوبة بالضبط في شيفرة HTML. يمكن أيضًا قراءة كافة السمات باستعمال elem.attributes
؛ وهو مجموعة من الكائنات التي تنتمي إلى صنف Attr
المبني مسبقًا، تملك خاصيتي الاسم name
والقيمة value
.
فيما يلي عرض لكيفية قراءة سمة غير معيارية:
<body something="non-standard"> <script> alert(document.body.getAttribute('something')); // non-standard </script> </body>
تملك سمات HTML الميزات التالية:
-
لا تخضع لشرط التمييز بين الحروف الصغيرة والحروف الكبيرة (
id
هو نفسهID
). - تكون قيم هذه السمات دائما من نوع سلاسل نصّية (أي من النوع string).
فيما يلي عرضٌ موسّعٌ يوضّح طريقة التعامل مع السمات:
<body> <div id="elem" about="Elephant"></div> <script> alert( elem.getAttribute('About') ); // (1) 'Elephant', جلب elem.setAttribute('Test', 123); // (2), ضبط alert( elem.outerHTML ); // (3), التحقق من وجود السمة في شيفرة HTML (نعم) for (let attr of elem.attributes) { // (4) عرض القائمة كاملة alert( `${attr.name} = ${attr.value}` ); } </script> </body>
لاحظ ما يلي: (1).('getAttribute('About
: الحرف الأول من كلمة About هو حرف كبير مع أنه صغير في شيفرة HTML. هذا لا يُسبّب أيّ مشكلةٍ طالما أن أسماء السمات لا تخضع لشرط التمييز بين الحروف الكبيرة والحروف الصغيرة. (2). يمكن إسناد قيمة من أيّ نوعٍ كان للسمة، ولكن هذه القيمة تصبح سلسلةً نصّيةً. قيمة السمة Test
اذًا هنا هي "123"
. (3). تظهر كافة السمات بما فيها تلك التي أضفناها (أي غير المعيارية مثل تلك التي كانت موجودة في الشيفرة كالسمة 'About'، وتلك التي أضفناها كالسمة 'Test') في outerHTML
. (4). تُعدّ مجموعة السمات attributes
قابلة للتمرير ضمن حلقة تكرار، وتضمّ كافة سمات العنصر، المعيارية منها وغير المعيارية، في شكل كائنات تملك خاصيتي الاسم name
والقيمة value
.
المزامنة (synchronization) بين الخاصية والسمة
حين تتغيّر سمة معيارية ما، يجري تحديث الخاصية الموافقة لها آليًا والعكس كذلك صحيح (ما عدا في بعض الحالات الاستثنائية). عُدّلت في المثال الموالي السمة "id"
، فلاحظ أن ذلك أدّى الى تعديل الخاصية أيضًا. وحدث الشّيء نفسه في المثال العكسي الذي جاء بعده، حيث عُدّلت الخاصية وهو ما أدّى إلى تعديل السمة آليًا.
<input> <script> let input = document.querySelector('input'); // سمة => خاصّية input.setAttribute('id', 'id'); alert(input.id); // id (تحديث) // خاصية => سمة input.id = 'newId'; alert(input.getAttribute('id')); // newId (تحديث) </script>
غير أن هناك بعض الاستثناءات، فعلى سبيل المثال، لا يمكن مزامنة input.value
سوى في اتجاه واحد، من السمة إلى الخاصية، وليس العكس.
<input> <script> let input = document.querySelector('input'); // سمة ⇐ خاصية input.setAttribute('value', 'text'); alert(input.value); // text //خاصية ⇍ سمة input.value = 'newValue'; alert(input.getAttribute('value')); // text (لم يتم التحديث) </script>
في المثال أعلاه:
-
يُؤدّي تعديل قيمة السمة
value
إلى تعديل الخاصية آليًا. - ولكن التعديل على الخاصية مباشرةً لا يُؤدّي إلى أيّ أثر على السمة.
هذه "الميزة" يمكن أن تكون فعلا مفيدة، حيث يمكن للمستخدم أن يغيّر القيمة value
، وبعدها، إذا أردت استعادة القيمة الأولية من HTML، تجدها في السمة.
خاصيات DOM لها أنواع
لا تكون خاصيات DOM دائمًا عبارة عن سلاسل نصّية (أي من النوع "string"
)، فمثلا خاصية input.checked
(في مربعات الاختيار (checkbox)) تكون عبارة عن قيمة بوليانية/منطقية.
<input id="input" type="checkbox" checked> checkbox <script> alert(input.getAttribute('checked')); // قيمة السمة من نوع سلسلة نصّية فارغة alert(input.checked); // قيمة الخاصية هي “true” </script>
وهناك أمثلة أخرى؛ تكون السمة style
عبارة عن سلسلة نصّية ولكن الخاصية style
هي كائن.
<div id="div" style="color:red;font-size:120%">Hello</div> <script> // سلسلة نصّية alert(div.getAttribute('style')); // color:red;font-size:120% // كائن alert(div.style); // [object CSSStyleDeclaration] alert(div.style.color); // red </script>
غير أن غالبية الخاصيات تكون من نوع سلاسل نصّية.
وفي حالات نادرة جدًا، حتى وإن كانت خاصية DOM من نوع سلسلة نصّية، يمكن أن تختلف عن السمة. فمثلا، خاصية href
في DOM تكون دائما عبارة عن عنوان URL مطلق كامل، حتى وإن تضمّنت السمة عنوان URL نسبي أو إشارةً إلى أحد العناصر في الصفحة عبر الصياغة hash#
. وفيما يلي مثالٌ على ذلك.
<a id="a" href="#hello">link</a> <script> // attribute alert(a.getAttribute('href')); // #hello // property alert(a.href ); // URL كامل على شكل http://site.com/page#hello </script>
إذا ما أردت الوصول إلى قيمة href
، أو أيّ سمةٍ غيرها، كما هي مدّونة بالضبط في شيفرة HTML، استعمل الخاصية getAttribute
.
السمات غير المعيارية والخاصية dataset
نستخدم الكثير من السمات المعيارية عند الكتابة بلغة جافاسكربت ولكن ماذا عن السمات غير المعيارية وهل هي مفيدة؟ فلنرى أولا إن كانت مفيدة أم لا وما هو دورها.
تُستخدم أحيانا السمات غير المعيارية في تمرير البيانات من شيفرة HTML إلى جافاسكربت أو لـ : "وسم" عناصر HTML حتى تتعرّف عليها جافاسكربت.
<!-- وسم العنصر div لإظهار الاسم هنا--> <div show-info="name"></div> <!-- والعمر هنا --> <div show-info="age"></div> <script> // تجد الشيفرة العنصر الموسوم وتقوم بإظهار المطلوب let user = { name: "Pete", age: 25 }; for(let div of document.querySelectorAll('[show-info]')) { // ملئ الحقل بالمعلومات الموافقة له let field = div.getAttribute('show-info'); div.innerHTML = user[field]; //(*) } </script>
(*): إظهارالقيمة "Pete" داخل العنصر div
الموسوم بـ name
وإظهار القيمة 25
داخل العنصر div
الموسوم بـage
. كما يمكن استعمالها لتنسيق عنصر ما. فمثلا، تُستخدم هنا السمة order-state
للتعبير عن حالة الطلب (إن كان جديدًا، معلّقًا أو ملغى).
<style> /* تعتمد الأنماط على السمة غير المعيارية 'order-state' */ .order[order-state="new"] { color: green; } .order[order-state="pending"] { color: blue; } .order[order-state="canceled"] { color: red; } </style> <div class="order" order-state="new"> A new order. </div> <div class="order" order-state="pending"> A pending order. </div> <div class="order" order-state="canceled"> A canceled order. </div>
لماذا يُستحسن استخدام السمة بدل استخدام الأصناف order-state-canceled.
وorder-state-pending.
و order-state-new.
؟ ذلك لأنه يَسهُل استعمال الخاصيات موازنةً مع الأصناف حيث يمكن تعديل الحالة بمنتهى السهولة كما في المثال التالي:
// أسهل من حذف/إضافة صنف جديد div.setAttribute('order-state', 'canceled');
ولكن السمات غير المعيارية قد تتسبّب في المشكل التالي: ماذا لو استُخدمت سمةٌ غير معياريةٍ لغرضٍ ما وبعدها اِستُحدِثت كسمةٍ معياريةٍ تقوم بوظيفةٍ ما؟ تُعد لغة HTML حيّةً، ومتطورةً، حيث تَستحدِث باستمرار سماتٍ جديدةٍ تلبّي حاجات المطوّرين. قد ينجرّ عن هذا الأمر انعكاساتٍ غير متوقعة. ولتفادي حصول مثل هذا التضارب وُجدت السمات *-data
.
كلّ السمات التي تبدأ بـ "-data" هي سمات محجوزة يستعملها المطوّرون فقط. ويمكن الوصول إليها عبر الخاصية dataset
. فمثلا، إذا كانت السمة المسمّاة "data-about"
تابعة للعنصر elem
، يمكن الوصول إليها باستعمال elem.dataset.about
كالآتي:
<body data-about="Elephants"> <script> alert(document.body.dataset.about); // Elephants </script>
تُصبح أسماء السمات التي تتكوّن من عدّة كلمات مثل data-order-state
على الشكل: dataset.orderState
، حيث تُكتب -عند استعمالها كخاصيات- بنمط سنام الجمل أي بجعل أول حرف من كلّ كلمة كبيرًا عدا الكلمة الأولى. نعيد فيما يلي كتابة المثال السابق (المثال الخاص بـ "حالة الطلب") بطريقة أخرى.
<style> .order[data-order-state="new"] { color: green; } .order[data-order-state="pending"] { color: blue; } .order[data-order-state="canceled"] { color: red; } </style> <div id="order" class="order" data-order-state="new"> A new order. </div> <script> // قراءة alert(order.dataset.orderState); // new // تعديل order.dataset.orderState = "pending"; // (*) </script>
يُعدّ استعمال السمات التي تبدأ بـ *-data
طريقةً فعالةً وآمنةً لتمرير البيانات غير المعيارية. لاحظ هنا أنه لا يمكنك قراءة السمات *-data
فحسب، بل حتى تعديلها، ومن ثمّ تُطبَّق تنسيقات CSS بناءً عليها. في المثال أعلاه، غيّرت تعليمة السطر الأخير (الموسوم بالرمز (*)
) اللّون ليصبح أزرقًا.
الخلاصة
- السمة هي ما يدوّن على شيفرة HTML
- الخاصية هي ما يندرج ضمن الكائن DOM
موازنة بسيطة
الخاصيات | السمات | |
---|---|---|
النوع | أيّ قيمةٍ كانت، للخاصيات أنواعٌ موضّحةٌ في المواصفة التقنية | سلسلة نصّية |
الاسم | يخضع لشرط التمييز بين الحروف الكبيرة والحروف الصغيرة | لا يخضع لشرط التمييز بين الحروف الكبيرة والحروف الصغيرة |
دوالّ العمليات على السمات هي:
-
(elem.hasAttribute(name
: للتحقق من وجود السمة. -
(elem.getAttribute(name
: للحصول على قيمة السمة. -
(elem.setAttribute(name, value
: إسناد القيمةvalue
للسمةname
. -
(elem.removeAttribute(name
: حذف السمة. -
elem.attributes
: يمثّل مجموعة السمات الخاصة بالعنصر المحدد.
ويُستحسن استعمال خاصيات DOM في أغلب الحالات، وعليك اللجوء إلى استعمال السمات فقط حين تحتاج إلى ذلك وإذا كان استعمال خاصيات DOM لا يفي بالغرض. مثلا: *تحتاج إلى سمةٍ غير معيارية، ولكن إن كانت تبدأ بـ -data
عليك استعمالdataset
. *تُريد قراءة القيمة كما هي مكتوبة بالضبط في شيفرة HTML. قد تكون قيمة الخاصية DOM مختلفةً عنها. فمثلا، تكون الخاصية href
دائما عبارة عن عنوان URL مطلق كامل، وقد تحتاج أنت الوصول إلى القيمة الأصلية/الأولى.
تمارين
الوصول إلى سمةٍ ما
درجة الأهمية: 5
اُكتب الشيفرة التي تسمح باختيار العنصر من المستند باستعمال السمة data-widget-name
وقراءة قيمتها.
<!DOCTYPE html> <html> <body> <div data-widget-name="menu">Choose the genre</div> <script> /*أكتب الشيفرة هنا */ </script> </body> </html>
الحل
<!DOCTYPE html> <html> <body> <div data-widget-name="menu">Choose the genre</div> <script> // الحصول عليه let elem = document.querySelector('[data-widget-name]'); // قراءة القيمة alert(elem.dataset.widgetName); // أو alert(elem.getAttribute('data-widget-name')); </script> </body> </html>
تغيير لون الروابط الخارجية إلى البرتقالي
درجة الأهمية: 3
غيّر لون جميع الروابط الخارجية إلى البرتقالي من خلال التعديل على خاصية style
. يُعدّ الرابط خارجيًا إذا:
-
كانت السمة
href
الخاصة به تضمّ السلسلة النصّية '//:' - لم تكن تبدأ بـالسلسلة النصّية 'http://internal.com'
مثال:
<a name="list">the list</a> <ul> <li><a href="http://google.com">http://google.com</a></li> <li><a href="/tutorial">/tutorial.html</a></li> <li><a href="local/path">local/path</a></li> <li><a href="ftp://ftp.com/my.zip">ftp://ftp.com/my.zip</a></li> <li><a href="http://nodejs.org">http://nodejs.org</a></li> <li><a href="http://internal.com/test">http://internal.com/test</a></li> </ul> <script> // تنسيق رابط واحد let link = document.querySelector('a'); link.style.color = 'orange'; </script>
ينبغي أن تكون النتيجة كالآتي:
الحل
عليك أولا إيجاد كلّ الروابط الخارجية. وهناك طريقتان: الأولى من خلال إيجاد كافة الروابط باستعمال ('document.querySelectorAll('a
وبعدها أخذ ما تحتاج إليه فقط.
let links = document.querySelectorAll('a'); for (let link of links) { let href = link.getAttribute('href'); if (!href) continue; // لا وجود للسمة if (!href.includes('://')) continue; // لا وجود للسلسلة النصية '://' if (href.startsWith('http://internal.com')) continue; // رابط داخلي link.style.color = 'orange'; }
لاحظ هنا أنّنا نستعمل ('link.getAttribute('href
وليس link.href
لأنّنا نريد استخراج القيمة من شيفرة HTML . والطريقة الثانية، أبسط من الأولى، تكون بإضافة عمليات التحقّق لمحدّد CSS:
البحث في href عن كلّ الروابط التي تضمّ السلسلة النصّية '//:' // // ولا تبدأ بـالسلسلة النصّية 'http://internal.com' let selector = 'a[href*="://"]:not([href^="http://internal.com"])'; let links = document.querySelectorAll(selector); links.forEach(link => link.style.color = 'orange');
ترجمة -وبتصرف- للفصل Attributes and properties من كتاب Browser: Document, Events, Interfaces
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.