مقدمه

در فصل قبل، هسته‌ی اصلی کار با DOM را یاد گرفتیم. در این فصل، به بررسی «اکستنشن‌های DOM» می‌پردازیم؛ این اکستنشن‌ها مجموعه‌ای از APIها و پراپرتی‌های پیشرفته‌تر هستند که قابلیت‌های جدیدی را به DOM اضافه کرده و یا روش‌های بهینه‌تر و ساده‌تری برای انجام کارهای رایج فراهم می‌کنند. در اولین درس از این فصل، به دو موضوع کلیدی می‌پردازیم: روش‌های استاندارد برای دستکاری استایل‌های CSS یک عنصر و تکنیک‌های پیمایش مدرن و کارآمد در درخت DOM.

کار با استایل‌های عناصر

بهترین روش برای تغییر ظاهر عناصر، تغییر کلاس‌های CSS آن‌ها از طریق classList است. اما گاهی نیاز داریم مقادیر استایل خاصی را به صورت پویا و مستقیم از طریق جاوااسکریپت تنظیم کنیم. برای این کار، DOM دو ابزار اصلی در اختیار ما قرار می‌دهد.

پراپرتی style

هر عنصر HTML یک پراپرتی به نام style دارد که به ما امکان دسترسی به استایل‌های درون‌خطی (inline styles) آن عنصر را می‌دهد. این پراپرتی خود یک شیء است که پراپرتی‌های CSS را به فرمت camelCase در خود دارد (برای مثال، background-color در CSS به backgroundColor در جاوااسکریپت تبدیل می‌شود).

Copy Icon JAVASCRIPT
const box = document.getElementById('myBox');

// Set inline styles dynamically
box.style.width = '200px';
box.style.height = '100px';
box.style.backgroundColor = 'tomato';
box.style.borderLeft = '5px solid black';

این کد به صورت مستقیم مقادیر استایل را به اتریبیوت style عنصر اضافه می‌کند. این استایل‌ها به دلیل ماهیت خطی بودن، بالاترین اولویت را دارند و استایل‌های تعریف شده در فایل‌های CSS خارجی را لغو می‌کنند (مگر اینکه آن استایل‌ها دارای `!important` باشند).

خواندن استایل‌های محاسبه‌شده

یک محدودیت بزرگ پراپرتی style این است که فقط می‌تواند استایل‌هایی را بخواند که به صورت خطی (inline) روی خود عنصر تنظیم شده باشند. این پراپرتی نمی‌تواند به استایل‌هایی که از طریق استایل‌شیت‌های خارجی (external) یا داخلی (internal) به عنصر اعمال شده‌اند، دسترسی داشته باشد.

برای خواندن مقدار نهایی و محاسبه‌شده‌ی یک پراپرتی CSS روی یک عنصر، باید از تابع سراسری getComputedStyle() استفاده کنیم. این تابع عنصر مورد نظر را به عنوان آرگومان گرفته و یک شیء فقط-خواندنی برمی‌گرداند که حاوی تمام مقادیر CSS نهایی آن عنصر است.

Copy Icon JAVASCRIPT
// CSS: #myBox { padding: 10px; font-family: Arial; }
const box = document.getElementById('myBox');
const styles = window.getComputedStyle(box);

console.log(styles.backgroundColor); // "tomato" (from inline style)
console.log(styles.padding);         // "10px" (from stylesheet)
console.log(styles.fontFamily);      // "Arial" (from stylesheet)

همانطور که می‌بینید، getComputedStyle() هم استایل خطی (backgroundColor) و هم استایل‌های اعمال شده از فایل CSS (padding و fontFamily) را به درستی می‌خواند.

پیمایش بهینه درخت (Element Traversal)

همانطور که در فصل قبل دیدیم، پراپرتی‌های پیمایشی مانند childNodes و firstChild تمام انواع گره، از جمله گره‌های متنی (فضاهای خالی) و کامنت‌ها را برمی‌گردانند. این رفتار اغلب منجر به کدهای پیچیده‌تر برای فیلتر کردن گره‌های غیرعنصری می‌شود. اکستنشن Element Traversal مجموعه‌ای از پراپرتی‌های جایگزین را فراهم می‌کند که فقط روی گره‌های عنصر تمرکز دارند.

  • children: یک HTMLCollection زنده از فرزندان عنصر یک گره.
  • firstElementChild / lastElementChild: اولین و آخرین فرزند عنصر.
  • nextElementSibling / previousElementSibling: خواهر و برادر عنصر بعدی و قبلی.
  • childElementCount: تعداد فرزندان عنصر.
Copy Icon JAVASCRIPT
// HTML: <div id="parent"> <!-- comment --> <p>One</p> <p>Two</p> </div>
const parent = document.getElementById('parent');

console.log(parent.childNodes.length);        // 5 (text, comment, text, p, text, p, text)
console.log(parent.children.length);          // 2 (p, p)

console.log(parent.firstChild.nodeName);         // "#text"
console.log(parent.firstElementChild.tagName); // "P"

این مثال به وضوح نشان می‌دهد که چگونه پراپرتی‌های Element Traversal (مانند children و firstElementChild) با نادیده گرفتن گره‌های متنی و کامنت، کد پیمایشی ما را بسیار تمیزتر و قابل پیش‌بینی‌تر می‌کنند.

پیدا کردن نزدیک‌ترین والد با closest

یکی دیگر از متدهای پیمایشی بسیار مفید، closest() است. این متد که روی یک عنصر فراخوانی می‌شود، یک سلکتور CSS می‌گیرد و درخت DOM را به سمت بالا (از خود عنصر شروع به سمت والدین) پیمایش می‌کند. اولین عنصری که با سلکتور مطابقت داشته باشد را برمی‌گرداند. اگر هیچ والد منطبقی پیدا نشود، null برمی‌گرداند.

Copy Icon JAVASCRIPT
// HTML: <article><div class="container"><p><span id="start">...</span></p></div></article>
const startNode = document.getElementById('start');

// Find the closest ancestor that is a div
const container = startNode.closest('div.container');

// Find the closest article element
const article = startNode.closest('article');

console.log(container.className); // "container"
console.log(article.tagName);   // "ARTICLE"

متد closest() برای پیدا کردن والد یا ظرف نگهدارنده‌ی یک عنصر، به ویژه در مدیریت رویدادها (event handling)، فوق‌العاده کاربردی است و ما را از نوشتن حلقه‌های پیمایشی با parentNode بی‌نیاز می‌کند.

در این درس روش‌های پیشرفته‌تری برای کار با استایل‌ها و پیمایش DOM را بررسی کردیم. یاد گرفتیم که چگونه با پراپرتی style و تابع getComputedStyle() استایل‌ها را مدیریت کنیم و چگونه با استفاده از پراپرتی‌های پیمایش عنصر و متد closest() به شکلی بهینه‌تر و خواناتر در درخت DOM حرکت کنیم. در درس بعدی، به سراغ یکی از مفاهیم قدرتمند اما کمتر شناخته‌شده‌ی DOM یعنی `Ranges` خواهیم رفت و یاد می‌گیریم که چگونه بخش‌هایی از یک سند را به صورت برنامه‌نویسی انتخاب کرده و دستکاری کنیم، مشابه کاری که کاربر با ماوس انجام می‌دهد.