روشهای انتخاب عناصر از DOM
مقدمه
در درس قبل دیدیم که صفحهی HTML، در قالب یک ساختار درختی از Node ها در دسترس جاوااسکریپت قرار میگیرد. اما برای اینکه بتوانیم روی یک عنصر خاص کاری انجام دهیم -مثلاً تغییر متن آن یا اضافه کردن یک کلاس- ابتدا باید بتوانیم آن عنصر را از میان کل درخت DOM پیدا و انتخاب کنیم. در این درس، رایجترین متدهای انتخاب عناصر را بررسی میکنیم.
getElementById — انتخاب بر اساس شناسه
اگر یک عنصر HTML مشخصهی id داشته باشد، سادهترین و سریعترین روش انتخاب آن، استفاده از getElementById است:
<h1 id="main-title">عنوان اصلی</h1>
تحلیل کد
این متد، مستقیماً روی document فراخوانی میشود (نه روی یک عنصر دیگر) و همیشه دقیقاً یک عنصر برمیگرداند؛ چون id در یک صفحهی HTML معتبر، باید یکتا باشد. اگر هیچ عنصری با آن id پیدا نشود، مقدار null بازگردانده میشود، نه خطا؛ این نکتهی مهمی است که در ادامهی همین درس دوباره به آن اشاره میکنیم.
querySelector — انتخاب با نحو CSS Selector
متد querySelector انعطافپذیرتر است؛ میتوان هر Selector معتبر CSS را به آن پاس داد و اولین عنصری که با آن مطابقت دارد، بازگردانده میشود:
تحلیل رفتار جاوااسکریپت
همانطور که مشاهده میکنید، querySelector میتواند با تگ (p)، کلاس (.highlight)، شناسه (#main-title) یا حتی Selectorهای پیچیده و تودرتو (div.card > p) کار کند؛ دقیقاً همان نحوی که در دورهی CSS برای انتخاب عناصر یاد گرفتهاید. اگر چند عنصر با Selector مطابقت داشته باشند، فقط اولین عنصر (طبق ترتیب ظاهر شدن در سند) بازگردانده میشود.
querySelectorAll — انتخاب تمام عناصر مطابق
برای دریافت تمام عناصری که با یک Selector مطابقت دارند، از querySelectorAll استفاده میکنیم:
تحلیل کد
خروجی querySelectorAll، برخلاف querySelector، یک عنصر واحد نیست؛ بلکه ساختاری به نام NodeList است که شبیه آرایه عمل میکند: دارای length است و متد forEach را مستقیماً پشتیبانی میکند. اما نکتهی مهم این است که NodeList یک آرایهی واقعی نیست؛ متدهایی مثل map یا filter که در فصل آرایه دیدیم، مستقیماً روی آن کار نمیکنند (مگر اینکه ابتدا با Array.from(allParagraphs) آن را به یک آرایهی واقعی تبدیل کنیم).
getElementsByClassName و getElementsByTagName
پیش از رواج querySelector، دو متد قدیمیتر برای انتخاب گروهی عناصر رایج بودند:
let cards = document.getElementsByClassName("card");
let allDivs = document.getElementsByTagName("div");
این دو متد، برخلاف querySelectorAll، یک HTMLCollection برمیگردانند؛ ساختاری که حتی از NodeList هم محدودتر است (مثلاً متد forEach را مستقیماً پشتیبانی نمیکند) و یک تفاوت مهم دیگر هم دارد: HTMLCollection زنده (Live) است؛ یعنی اگر عنصری با همان کلاس یا تگ بعداً به صفحه اضافه شود، بهطور خودکار به این مجموعه هم اضافه میشود. در مقابل، نتیجهی querySelectorAll یک NodeList ایستا (Static) است؛ یعنی دقیقاً یک عکس لحظهای از عناصر مطابق در همان لحظهی فراخوانی است و با تغییرات بعدی صفحه، بهروزرسانی نمیشود.
بررسی نتیجهی null هنگام پیدا نشدن عنصر
همانطور که در ابتدای درس اشاره کردیم، اگر عنصر موردنظر پیدا نشود، این متدها معمولاً null (برای متدهای تکعنصری) یا یک مجموعهی خالی (برای متدهای چندعنصری) برمیگردانند، نه خطا. نادیده گرفتن این نکته، یکی از رایجترین منابع خطا در کدنویسی DOM است:
تحلیل رفتار جاوااسکریپت
خط اول بهدرستی null را چاپ میکند. اما خط دوم با خطای TypeError: Cannot read properties of null متوقف میشود؛ چون تلاش میکنیم به textContent روی مقدار null دسترسی پیدا کنیم. این دقیقاً همان نوع خطایی است که در فصل انواع داده، هنگام بررسی null بهعنوان یک Primitive Type مستقل، دیدیم که رفتار متفاوتی از undefined دارد؛ در کدنویسی DOM واقعی، همیشه باید پیش از استفاده از نتیجهی این متدها، از وجود آنها مطمئن شویم.
جدول جمعبندی
| متد | خروجی | Selector ورودی |
|---|---|---|
| getElementById | یک عنصر یا null | فقط id (بدون #) |
| querySelector | اولین عنصر مطابق یا null | هر CSS Selector |
| querySelectorAll | NodeList ایستا | هر CSS Selector |
| getElementsByClassName | HTMLCollection زنده | فقط نام کلاس |
| getElementsByTagName | HTMLCollection زنده | فقط نام تگ |
جمعبندی
برای انتخاب عناصر از DOM، جاوااسکریپت چند متد در اختیار میگذارد: getElementById برای انتخاب سریع بر اساس شناسه، و querySelector/querySelectorAll برای انتخاب انعطافپذیر با نحو CSS Selector که امروزه رایجترین روش هستند. تفاوت مهم بین HTMLCollection (زنده) و NodeList (ایستا) باید در نظر گرفته شود، و همیشه باید پیش از استفاده از نتیجهی این متدها، احتمال بازگشت null یا مجموعهی خالی را بررسی کرد.
