تُفيد خصائص التنقّل في 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

 
	 
			
				 
			
				 
			
				 
	 
	 
	 
	 
			
				 
			
				 
			
				
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.