تُفيد خصائص التنقّل في DOM كثيرا عندما تكون العناصر قريبة من بعضها البعض. لكن ماذا لو لم تكن كذلك؟ كيف يمكن تحصيل عنصرٍ ما على الصفحة؟
هناك المزيد من توابع البحث لهذا الغرض.
document.getElementById أو فقط id
إذا كان للعنصر سمة id
، فيمكننا تحصيله باستخدام التابع (document.getElementById(id
، أينما وُجد.
على سبيل المثال:
<div id="elem"> <div id="elem-content">Element</div> </div> <script> // تحصيل العنصر let elem = document.getElementById('elem'); // تلوين خلفيّته باﻷحمر elem.style.background = 'red'; </script>
بالإضافة إلى هذا، يمكن الإشارة إلى العنصر بواسطة متغيّر عامّ (global variable) اسمه نفس قيمة الـ id
.
<div id="elem"> <div id="elem-content">Element</div> </div> <script> // id="elem" الذي سمته DOM إلى عنصر elem يشير المتغيّر elem.style.background = 'red'; // واصلة في وسطه، لذا فلا يمكن أن يكون اسمًا لمتغير id="elem-content"لدى // window['elem-content'] لكن يمكن الوصول إليه بواسطة اﻷقواس المربعة... </script>
… هذا إذا لم نصرّح بمتغيّر جافاسكربت له نفس الاسم، فإنّ اﻷولويّة حينها تكون له:
<div id="elem"></div> <script> let elem = 5; // <div id="elem"> هو 5، وليس إشارة إلى elem يكون الآن alert(elem); // 5 </script>
يُرجى عدم استخدام المتغيّرات العامّة المسمّات على قيم الـ id للوصول إلى العناصر
هذا السلوك مُبيّن في المواصفة، مما يجعله وفق المعايير نوعا ما. لكنّه مدعوم في الغالب بغرض التوافق (compatibility).
يحاول المتصفّح مساعدتنا بالمزج بين مجالات اﻷسماء (namespaces) في جافاسكربت و DOM. لا بأس بذلك في النصوص البرمجيّة البسيطة المضّمنة في HTML، لكن لا يُنصح به في العموم. فقد يؤدي ذلك إلى تناقضات في التسمية. كما أنّه عند قراءة شفرة جافاسكربت دون النظر إلى HTML، قد لا يتضّح من أين يأتي المتغيّر.
نستعمل هنا في هذا المقال id
للإشارة مباشرة إلى عنصر ما بغرض الاختصار، عندما يكون واضحا من أين يأتي العنصر.
في التطبيقات الواقعيّة، تُعدّ document.getElementById
هي الطريقة المفضّلة.
يجب أن يكون الـ id
فريدًا
يجب أن يكون الـ id
فريدًا. لا يمكن أن يحمل أكثر من عنصر في المستند نفس الـ id
.
إذا كانت هناك عدّة عناصر لها نفس الـ id
، فإنّه لا يمكن التنبؤ بسلوك التوابع التي تستخدمها. فقد تُرجع مثلا document.getElementById
أيًّا من العناصر عشوائيًّا. لذا يُرجى الالتزام بالقاعدة وإبقاء الـ id
فريدا.
document.getElementById
فقط، وليس anyElem.getElementById
يمكن استدعاء التابع getElementById
على الكائن document
فقط. يبحث هذا التابع عن الـ id
المراد في المستند بأكمله.
querySelectorAll
يُعدّ (css)elem.querySelectorAll
أكثر التوابع تنوّعا في الاستخدام على الإطلاق. يعيد هذا التابع جميع العناصر التي ينطبق عليها محدّد selector) CSS) معيّن.
نبحث في اﻷسفل مثلا عن جميع العناصر <li>
التي هي آخر اﻷبناء:
<ul> <li>The</li> <li>test</li> </ul> <ul> <li>has</li> <li>passed</li> </ul> <script> let elements = document.querySelectorAll('ul > li:last-child'); for (let elem of elements) { alert(elem.innerHTML); // "test", "passed" } </script>
هذا التابع قويّ بالفعل، ﻷنّه يمكن معه استخدام أيّ محدّد CSS.
يمكن استخدام أشباه اﻷصناف كذلك
يدعم محدّد CSS أيضا أشباه اﻷصناف (pseudo-class) مثل hover:
و active:
. على سبيل المثال، يعيد ('document.querySelectorAll(':hover
مجموعة العناصر التي يوجد عليها المؤشّر حاليّا (حسب ترتيب تفرعها: بداية من <html>
إلى آخر فرع منها).
querySelector
يعيد استدعاء (elem.querySelector(css
أوّل العناصر التي ينطبق عليها محدّد CSS.
بعبارة أخرى، هي نفس نتيجة [elem.querySelectorAll(css)[0
، لكنّ هذه اﻷخيرة تبحث عن جميع العناصر وتختار بعد ذلك أوّلها، بينما تبحث elem.querySelector
عن عنصر واحد فقط. فهي بذلك أسرع في البحث وأقصر في الكتابة.
matches
تقوم التوابع السابقة بالبحث في DOM.
لا يقوم (elem.matches(css بالبحث عن أيّ شيء، بل يتحقّق فقط من أنّ العنصر elem
يطابق محدّد CSS المراد، ويعيد إمّا true
أو false
.
يُفيد هذا التابع عند المرور على عدد من العناصر (في مصفوفة أو شيء من هذا القبيل) ونريد ترشيح العناصر التي تهمّنا فقط.
على سبيل المثال:
<a href="http://example.com/file.zip">...</a> <a href="http://ya.ru">...</a> <script> // document.body.children يمكن تطبيقها على أيّة مجموعة مكان for (let elem of document.body.children) { if (elem.matches('a[href$="zip"]')) { alert("The archive reference: " + elem.href ); } } </script>
closest
يتمثّل أسلاف (ancestors) عنصر ما في أبيه، وأب أبيه، وهكذا. يشكّل جميع اﻷسلاف معًا سلسلة الآباء التي تبتدئ من العنصر وتنتهي عند القمّة.
يبحث تابع (elem.closest(css
عن أقرب سلفٍ ينطبق عليه محدّد CSS. يشمل البحثُ العنصرَ elem
نفسَه.
بعبارة أخرى، ينطلق التابع closest
صعودًا من العنصر المراد ويفحص كلّا من الآباء. فإذا طابق أحد اﻷسلاف المُحدِّد يتوقّف البحث، ويعيدُ السلفَ المطابق.
على سبيل المثال:
<h1>Contents</h1> <div class="contents"> <ul class="book"> <li class="chapter">Chapter 1</li> <li class="chapter">Chapter 1</li> </ul> </div> <script> let chapter = document.querySelector('.chapter'); // LI alert(chapter.closest('.book')); // UL alert(chapter.closest('.contents')); // DIV alert(chapter.closest('h1')); // (ليس من اﻷسلاف h1 ﻷن) null </script>
*getElementsBy
هناك أيضا توابع أخرى للبحث عن العقد حسب الوسم (tag) والصنف (class) وغيرها.
تُعدّ هذه التوابع غالبًا من الماضي، إذ أنّ querySelector
أقوى وأقصر في الكتابة.
إذًا سنذكرها هنا من باب التمام، كما أنّه لا يزال من الممكن إيجادها في النصوص البرمجيّة القديمة.
-
يبحث
(elem.getElementsByTagName(tag
عن العناصر التي لها نفس الوسم المُراد ويعيدهم جميعا على شكل مجموعة. يمكن للمعاملtag
أن يكون نجمة أيضا"*"
ليشمل "جميع الوسوم". -
يعيد
(elem.getElementsByClassName(className
العناصر التي لها نفس صنف CSS المراد. -
يعيد
(document.getElementsByName(name
كل العناصر التي في المستند التي لها نفس السمةname
. يندر استعمال هذا التابع.
على سبيل المثال:
// التي في المستند div حصّل جميع وسوم let divs = document.getElementsByTagName('div');
لنجد جميع وسوم input
التي في المستند:
<table id="table"> <tr> <td>Your age:</td> <td> <label> <input type="radio" name="age" value="young" checked> less than 18 </label> <label> <input type="radio" name="age" value="mature"> from 18 to 50 </label> <label> <input type="radio" name="age" value="senior"> more than 60 </label> </td> </tr> </table> <script> let inputs = table.getElementsByTagName('input'); for (let input of inputs) { alert( input.value + ': ' + input.checked ); } </script>
لا تنسَ حرف "s"
للجمع!
ينسى المطوّرون المبتدئون أحيانا الحرف "s"
، فيعمدون إلى مناداة getElementByTagName
بدل getElement<b>s</b>ByTagName
.
يخلو getElementByTagName
من الحرف "s"
ﻷنّه يعيد عنصرا وحيدا. لكنّ getElement<b>s</b>ByTagName
يعيد مجموعة من العناصر، لهذا فإنّ بداخله "s"
.
يعيد التابع مجموعة، وليس عنصرًا!
من الأخطاء الشائعة لدى المبتدئين أيضا هو كتابة:
// لا تعمل document.getElementsByTagName('input').value = 5;
لن يعمل ذلك، ﻷنّه يحاول إسناد القيمة إلى مجموعةٍ من المُدخلات بدل إسنادها إلى العناصر التي تحتويها.
يجب علينا إمّا التكرار على المجموعة أو تحصيل عنصر بواسطة معامله ثمّ إسناد القيمة له هكذا:
// (ًيُفترض أن تعمل (إذا كانت هناك مُدخلات document.getElementsByTagName('input')[0].value = 5;
للبحث عن العناصر ذات الصنف article.
:
<form name="my-form"> <div class="article">Article</div> <div class="long article">Long article</div> </form> <script> // name أوجد حسب السمة let form = document.getElementsByName('my-form')[0]; // أوجد حسب الصنف داخل النموذج let articles = form.getElementsByClassName('article'); alert(articles.length); // 2 - "article" وجدنا عنصران لهما الصنف </script>
المجموعات الحية
تُعيد جميع التوابع "*getElementsBy"
مجموعة حيّة (live). تعكس هذه المجموعات الوضع الحاليّ الذي عليه المستند و "تُحدَّث تلقائيًّا" كلّما تغيّر.
في المثال أدناه نصّان برمجيّان:
-
يُنشئ اﻷوّل مرجعًا إلى مجموعة العناصر
<div>
؛ طول المجموعة عندها هو1
. -
يُنفَّذ النصّ الثاني بعدما يقابل المتصفّح عنصر
<div>
آخر، ويكون طول المجموعة حينها هو2
.
<div>First div</div> <script> let divs = document.getElementsByTagName('div'); alert(divs.length); // 1 </script> <div>Second div</div> <script> alert(divs.length); // 2 </script>
في المقابل، يعيد querySelectorAll
مجموعة ساكنة (static)، كأنّها مصفوفة ثابتة من العناصر.
إذا طبقناه في المثال أعلاه، فسيُخرج كلا النصّان 1
:
<div>First div</div> <script> let divs = document.querySelectorAll('div'); alert(divs.length); // 1 </script> <div>Second div</div> <script> alert(divs.length); // 1 </script>
يمكننا الآن رؤية الفرق بوضوح. لم تزدد المجموعة الساكنة بظهور عنصر <div>
جديد في المستند.
الملخص
هناك 6 توابع رئيسيّة للبحث عن العقد في DOM:
التابع | يبحث بواسطة ... | يمكن مناداته على عنصر؟ | حيّة؟ |
---|---|---|---|
querySelector
|
محدّد CSS | ✔ | - |
querySelectorAll
|
محدّد CSS | ✔ | - |
getElementById
|
id
|
- | - |
getElementsByName
|
name
|
- | ✔ |
getElementsByTagName
|
الوسم أو '*'
|
✔ | ✔ |
getElementsByClassName
|
الصنف | ✔ | ✔ |
تُعدّ querySelector
و querySelectorAll
أكثرها استخداما على الإطلاق، لكن قد تُفيد *getElementBy
أحيانا أو قد توجد في النصوص البرمجيّة القديمة.
عدا ذلك:
-
هناك
(elem.matches(css
للتحقّق من أنّ العنصرelem
مطابق لمحدّد CSS المراد. -
هناك
(elem.closest(css
للبحث عن أقرب سلفٍ مطابق لمحدّد CSS المراد. يشمل البحثُ العنصرَ نفسه.
ولنضف هنا تابعا آخر للتحقّق من العلاقة ابن-أب، إذ قد تفيد أحيانا:
-
يعيد
(elemA.contains(elemB
القيمةtrue
إذا كانelemB
داخلا تحتelemA
(أي عنصرا سليلا لـelemA
) أو إذا كانelemA==elemB
.
التمارين
البحث عن العناصر
اﻷهميّة: 4
إليك مستندًا يحتوي على جدول ونموذج.
كيف يمكن إيجاد؟ …
-
الجدول الذي له
"id="age-table
. -
جميع العناصر
label
بداخل ذلك الجدول (من المفترض أن تكون هناك 3 منها). -
أوّل
td
في ذلك الجدول (التي فيها الكلمة "Age"). -
النموذج
form
الذي له"name="search
. -
أوّل
input
في ذلك النموذج. -
آخر
input
في ذلك النموذج
افتح table.html في نافذة مستقلة واستعمل أدوات المتصفّح لذلك.
الحل
هناك عدّة طرق لفعل ذلك.
هذه إحداها:
// id="age-table" الجدول الذي له let table = document.getElementById('age-table') // بداخل ذلك الجدول label جميع العناصر table.getElementsByTagName('label') // أو document.querySelectorAll('#age-table label') // ("Age" في ذلك الجدول (التي فيها الكلمة td أوّل table.rows[0].cells[0] // أو table.getElementsByTagName('td')[0] // أو table.querySelector('td') // name="search" النموذج الذي له // name="search" على افتراض أنّ هناك عنصرا واحدا في المستند له let form = document.getElementsByName('search')[0] // خصّيصا form ،أو document.querySelector('form[name="search"]') // في ذلك النموذج input أوّل form.getElementsByTagName('input')[0] // أو form.querySelector('input') // في ذلك النموذج input آخر let inputs = form.querySelectorAll('input') // inputs أوجد جميع الـ inputs[inputs.length-1] // اختر آخرها
ترجمة وبتصرف لمقال *Searching: getElement*, querySelector من كتاب The Modern JavaScript Tutorial
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.