FA-TOOLS — Header Component
آموزش dom در جاوا اسکریپت

آموزش جامع DOM در جاوا اسکریپت: از مفاهیم پایه تا مدیریت پیشرفته صفحات وب

فهرست مطالب

  • DOM چیست و چگونه کار می‌کند؟
  • تفاوت سند HTML و درخت DOM
  • روش‌های انتخاب عناصر در DOM
  • پیمایش و حرکت بین گره‌ها (DOM Traversal)
  • دستکاری عناصر: ساخت، تغییر و حذف
  • مدیریت رویدادها و تکنیک Event Delegation
  • بهینه‌سازی عملکرد و جلوگیری از Reflow
  • پروژه عملی: ساخت لیست تعاملی پویا
  • عیب‌یابی سریع و رفع خطاهای رایج
  • پرسش‌های متداول

خلاصه کاربردی مقاله

یادگیری DOM (شیء ساختار یافته سند) به شما اجازه می‌دهد صفحات ایستا را به اپلیکیشن‌های پویا و تعاملی تبدیل کنید. در این مقاله، نحوه دسترسی به عناصر، تغییر محتوا و استایل، مدیریت رویدادها، جلوگیری از افت سرعت مرورگر با ساختار DocumentFragment و رفع خطاهای مرسوم DOM را به صورت کاملاً کدنویسی‌شده و گام‌به‌گام می‌آموزید.

تسلط بر DOM مرز بین یک کدنویس مبتدی و یک توسعه‌دهنده حرفه‌ای فرانت‌اند است. با فهم درست نحوه تعامل جاوا اسکریپت با DOM، می‌توانید بدون وابستگی به فریم‌ورک‌های سنگین، رابط‌های کاربری بسیار سریع، تعاملی و بهینه بسازید و کنترل کامل هر عنصر در صفحه را به دست بگیرید.

DOM چیست و چگونه کار می‌کند؟

DOM چیست و چگونه کار می‌کند؟

پاسخ کوتاه: DOM یا Document Object Model یک رابط برنامه‌نویسی (API) استاندارد برای اسناد HTML و XML است. مرورگر هنگام بارگذاری کدهای HTML، ساختاری درختی از اشیاء به نام DOM Tree در حافظه ایجاد می‌کند تا جاوا اسکریپت بتواند به عناصر، استایل‌ها و محتوای صفحه دسترسی داشته باشد و آن‌ها را تغییر دهد.

وقتی یک صفحه وب را باز می‌کنید، مرورگر کدهای متنی HTML شما را می‌خواند و آن‌ها را به شیء‌های قابل درک برای جاوا اسکریپت تبدیل می‌کند. شیء اصلی و ریشه این ساختار، document نام دارد. تمام گره‌ها (Nodes) شامل تگ‌ها، متن‌ها، صفت‌ها (Attributes) و حتی کامنت‌ها در زیرمجموعه این شیء قرار می‌گیرند.

برای نمونه، در مطالعه ساختار استاندارد در قطعه کدهای HTML مشاهده می‌کنید که چگونه تگ‌های آشیانه‌ای (Nested) یک درخت کاملاً منطقی تشکیل می‌دهند که هر تگ والد (Parent)، فرزندانی (Children) در زیرمجموعه خود دارد.

تفاوت سند HTML و درخت DOM

تفاوت سند HTML و درخت DOM

اشتباه متداول بسیاری از توسعه‌دهندگان این است که DOM را عیناً همان کدهای HTML می‌دانند. HTML کد متنی اولیه شماست، اما DOM ساختار پویایی است که مرورگر رندر کرده و ممکن است به دلایل مختلف (مانند اجرا شدن جاوا اسکریپت یا تصحیح کدهای اشتباه HTML توسط مرورگر) با کد اولیه متفاوت باشد.

سند HTML اولیه درخت DOM در حافظه مرورگر
یک فایل متنی ایستا و غیرقابل تغییر پس از بارگذاری یک مدل زنده، پویا و قابل دستکاری با جاوا اسکریپت
فقط حاوی کدها و متن‌های نوشته‌شده توسط برنامه‌نویس شامل اصلاحات اتوماتیک مرورگر و تغییرات لحظه‌ای کدها
پاسخی که مستقیماً از سرور دریافت می‌شود نمای بصری جاری که کاربر با آن تعامل برقرار می‌کند

روش‌های انتخاب عناصر در DOM

روش‌های انتخاب عناصر در DOM

پاسخ کوتاه: برای پیدا کردن و دسترسی به عناصر صفحه، می‌توان از متدهای مدرن مانند querySelector و querySelectorAll با سینتکس CSS یا متدهای قدیمی‌تر مانند getElementById و getElementsByClassName استفاده کرد.

شناخت نحوه انتخاب دقیق عناصر اولین قدم در پردازش‌های DOM است. روش‌های اصلی انتخاب به شرح زیر هستند:

// انتخاب بر اساس شناسه (ID) - خروجی: یک عنصر تک
const mainHeader = document.getElementById('main-title');

// انتخاب اولین عنصری که با انتخابگر CSS تطبیق دارد
const submitBtn = document.querySelector('.form-container .btn-primary');

// انتخاب تمام عناصر با انتخابگر CSS - خروجی: NodeList ایستا
const allCards = document.querySelectorAll('.card');

// انتخاب بر اساس کلاس - خروجی: HTMLCollection زنده
const items = document.getElementsByClassName('list-item');

نکته تخصصی: مقایسه NodeList زنده (Live) و ایستا (Static)

یکی از اشتباهات رایج توسعه‌دهندگان، نادیده گرفتن تفاوت بین HTMLCollection و NodeList است. وقتی از getElementsByClassName استفاده می‌کنید، آرایه‌ای «زنده» دریافت می‌کنید؛ یعنی اگر عنصری جدید به DOM اضافه شود، لیست شما به صورت خودکار به‌روزرسانی می‌شود. اما querySelectorAll یک NodeList «ایستا» برمی‌گرداند که یک اسنپ‌شات ثابت از وضعیت لحظه‌ای سند است و تغییرات بعدی DOM روی آن منعکس نمی‌شود.

پیمایش و حرکت بین گره‌ها (DOM Traversal)

پیمایش و حرکت بین گره‌ها (DOM Traversal)

گاهی اوقات نیاز دارید بدون استفاده از ID یا کلاس جدید، بر اساس موقعیت یک عنصر نسبت به عنصر دیگر در درخت حرکت کنید. برای این کار از متدهای پیمایش استفاده می‌کنیم:

  • حرکت به سمت والد: استفاده از parentElement یا متد کاربردی element.closest(selector) که نزدیک‌ترین والد با انتخابگر مشخص را پیدا می‌کند.
  • حرکت به سمت فرزندان: استفاده از children (فقط عناصر HTML) یا childNodes (شامل متن‌ها و کامنت‌ها).
  • حرکت بین همسایه‌ها (Siblings): استفاده از nextElementSibling و previousElementSibling.
const currentItem = document.querySelector('.active-item');

// پیدا کردن اولین والدی که کلاس container دارد
const container = currentItem.closest('.container');

// انتخاب عنصر بعدی در همان سطح
const nextItem = currentItem.nextElementSibling;

دستکاری عناصر: ساخت، تغییر، حذف و استایل‌دهی

دستکاری عناصر: ساخت، تغییر، حذف و استایل‌دهی

پاسخ کوتاه: برای تغییر محتوا از textContent، برای ساخت عنصر از document.createElement، برای افزودن به DOM از append یا appendChild و برای تغییر استایل‌ها از classList استفاده می‌شود.

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

تفاوت امنیتی textContent و innerHTML

همواره توصیه می‌شود برای جایگذاری متون عادی از textContent استفاده کنید. استفاده بی‌پروا از innerHTML هنگامی که داده‌ها از ورودی کاربر دریافت می‌شوند، صفحه شما را در معرض حملات خطرناک XSS (Cross-Site Scripting) قرار می‌دهد.

// ساخت یک عنصر جدید
const newCard = document.createElement('div');

// مدیریت کلاس‌ها به روش صحیح
newCard.classList.add('card', 'shadow-sm');
newCard.classList.toggle('highlight');

// افزودن متن ایمن
newCard.textContent = 'این یک کارت جدید است';

// اضافه کردن به صفحه
document.body.append(newCard);

// حذف عنصر از DOM
// newCard.remove();

اگر قصد دارید به جای دستکاری مستقیم ویژگی style، استایل‌های پیچیده‌تری اضافه کنید، پیشنهاد می‌شود از روش مدیریت کلاس‌ها (Class List) استفاده کرده و کدهای ظاهر را در لیست کدهای آماده CSS پیاده‌سازی کنید.

مدیریت رویدادها و تکنیک Event Delegation

پاسخ کوتاه: رویدادها (Events) مانند کلیک، تایپ یا اسکرول با متد addEventListener شنود می‌شوند. تکنیک Event Delegation امکان مدیریت رویدادهای چندین عنصر فرزند را تنها با تعریف یک شنونده روی عنصر والد فراهم می‌کند.

وقتی روی عنصری کلیک می‌کنید، رویداد در سه مرحله حرکت می‌کند: انتشار به پایین (Capturing)، رسیدن به هدف (Target) و صعود به بالا (Bubbling). با بهره‌گیری از پدیده Event Bubbling، می‌توانید مصرف حافظه مرورگر را به شدت کاهش دهید.

// مدیریت رویداد به روش الگوی Event Delegation
const listContainer = document.querySelector('#todo-list');

listContainer.addEventListener('click', function(event) {
    // بررسی اینکه آیا روی دکمه حذف کلیک شده است یا خیر
    if (event.target.classList.contains('delete-btn')) {
        const listItem = event.target.closest('li');
        listItem.remove();
    }
});

این تکنیک برای فرم‌ها و صفحات فروش فوق‌العاده حیاتی است. مثلاً در سناریوهایی نظیر افزایش تعامل و فروش در طراحی صفحه فرود حرفه‌ای، دکمه‌های خرید دینامیک و عناصر تعاملی بدون افت سرعت لود کار خواهند کرد.

بهینه‌سازی عملکرد DOM: جلوگیری از Reflow و Repaint

هر زمان که ابعاد، موقعیت یا ساختار عناصر در DOM تغییر کند، مرورگر مجبور است چیدمان صفحه را مجدداً محاسبه کند (Reflow) و سپس پیکسل‌های صفحه را بازنویسی نماید (Repaint). انجام بیش از حد این عملیات باعث کندی و لگ زدن شدید وب‌سایت می‌شود.

استفاده از DocumentFragment برای تغییرات دسته‌ای

اگر قرار است ۱۰۰ عنصر جدید به یک لیست اضافه کنید، اضافه کردن تک‌تک آن‌ها به DOM اصلی ۱۰۰ بار فرایند Reflow را تحریک می‌کند. حل این مشکل با استفاده از حافظه موقت `DocumentFragment` انجام می‌شود:

const list = document.querySelector('#product-list');
const fragment = document.createDocumentFragment();

const products = ['محصول ۱', 'محصول ۲', 'محصول ۳'];

products.forEach(product => {
    const li = document.createElement('li');
    li.textContent = product;
    fragment.appendChild(li); // اضافه کردن به حافظه موقت
});

// فقط ۱ بار تغییر در DOM اصلی رخ می‌دهد
list.appendChild(fragment);

همچنین هنگام ارسال و دریافت اطلاعات از سرور با آموزش fetch api در جاوا اسکریپت، ساخت عناصر لیست با این روش کارایی برنامه شما را چندین برابر افزایش می‌دهد.

پروژه عملی: ساخت لیست وظایف (To-Do List) پویا

برای جمع‌بندی تمام مفاهیم، یک برنامه ساده اما کامل ساخت لیست وظایف طراحی می‌کنیم که امکان اضافه کردن، خط زدن و حذف آیتم‌ها را فراهم می‌سازد:

// ۱. انتخاب عناصر اصلی
const taskInput = document.querySelector('#task-input');
const addBtn = document.querySelector('#add-task-btn');
const todoList = document.querySelector('#todo-list');

// ۲. متد اضافه کردن وظیفه جدید
function addTask() {
    const taskText = taskInput.value.trim();
    if (taskText === '') return;

    const li = document.createElement('li');
    li.className = 'todo-item';
    li.innerHTML = `
        <span class="task-text">${taskText}</span>
        <button class="delete-btn">حذف</button>
    `;

    todoList.appendChild(li);
    taskInput.value = '';
}

// ۳. گوش دادن به رویداد کلیک دکمه افزودن
addBtn.addEventListener('click', addTask);

// ۴. مدیریت رویدادهای تعاملی لیست (حذف و علامت‌گذاری)
todoList.addEventListener('click', function(e) {
    if (e.target.classList.contains('delete-btn')) {
        e.target.closest('.todo-item').remove();
    } else if (e.target.classList.contains('task-text')) {
        e.target.classList.toggle('completed');
    }
});

عیب‌یابی سریع و رفع خطاهای رایج DOM

در این بخش، رایج‌ترین خطاهایی که توسعه‌دهندگان هنگام کار با DOM با آن مواجه می‌شوند و راه‌حل هر یک ارائه شده است:

  1. خطای Uncaught TypeError: Cannot read properties of null (reading 'addEventListener')
    علت: کدهای جاوا اسکریپت قبل از رندر شدن عناصر HTML در صفحه اجرا شده‌اند.
    راه‌حل: تگ script را به انتهای تگ <body> منتقل کنید یا از صفت defer در تگ script استفاده نمایید.
  2. عدم کارکرد event listener روی عنصری که با جاوا اسکریپت اضافه شده است
    علت: هنگام اجرا شدن کد اولیه، عنصر هنوز وجود نداشته تا شنونده روی آن ثبت شود.
    راه‌حل: از تکنیک Event Delegation که در بالا آموزش داده شد روی یک والد ثابت استفاده کنید.
  3. عدم اعمال متدهای آرایه روی خروجی querySelectorAll
    علت: خروجی این متد NodeList است نه یک آرایه استاندارد (Array).
    راه‌حل: لیست را با Array.from(nodeList) به آرایه تبدیل کنید یا متد forEach را مستقیم به کار ببرید.

پرسش‌های متداول

آیا DOM همان جاوا اسکریپت است؟

خیر، DOM یک Web API تحت استاندارد W3C است که توسط مرورگر ارائه می‌شود. جاوا اسکریپت فقط زبان برنامه‌نویسی است که از این API برای دستکاری صفحات وب استفاده می‌کند.

تفاوت innerHTML و textContent چیست؟

متد textContent متن را به صورت خام و بدون تحلیل تگ‌های HTML قرار می‌دهد که کاملاً امن است، در حالی که innerHTML رشته متنی را تحلیل کرده و تگ‌های HTML درون آن را رندر می‌کند.

Virtual DOM چیست و چه فرقی با Real DOM دارد؟

دستکاری مستقیم DOM مرورگر (Real DOM) کند است. فریم‌ورک‌هایی مثل React یک نسخه سبک و مجازی در حافظه (Virtual DOM) نگه می‌دارند، تغییرات را پردازش کرده و تنها بخش‌های لازم را روی DOM اصلی به‌روزرسانی می‌کنند.

چگونه می‌توان از کند شدن مرورگر هنگام دستکاری‌های زیاد DOM جلوگیری کرد؟

با استفاده از DocumentFragment برای دسته‌بندی تغییرات، مخفی کردن عنصر با display: none قبل از انجام تغییرات سنگین، و بهره‌گیری از تکنیک Event Delegation می‌توان بهینه‌ترین سرعت را تجربه کرد.

سوالات متداول

DOM چیست و چه کاربردی در جاوا اسکریپت دارد؟

مدل شیء سند یا DOM یک رابط برنامه‌نویسی است که HTML را به ساختاری درختی تبدیل می‌کند. این ساختار به جاوا اسکریپت امکان می‌دهد محتوا، استایل و عناصر صفحه را تغییر دهد.

تفاوت اصلی سند HTML و درخت DOM چیست؟

کدهای HTML فایل متنی ایستا هستند که از سرور دریافت می‌شوند، اما DOM ساختار پویا و زنده‌ای در حافظه مرورگر است که پس از بارگذاری قابل تغییر است.

بهترین روش انتخاب عناصر در DOM کدام است؟

متدهای querySelector و querySelectorAll به دلیل پشتیبانی کامل از انتخابگرهای CSS محبوب‌ترین و انعطاف‌پذیرترین روش‌های انتخاب عناصر هستند.

چرا استفاده از innerHTML به جای textContent خطرساز است؟

استفاده از innerHTML برای داده‌های دریافتی از کاربر می‌تواند منجر به اجرای کدهای مخرب و بروز روزنه‌های امنیتی مانند حملات XSS شود.

Table of Contents

آخرین نوشته‌ها

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *