DOM و مدیریت صفحات وب

DOM چیست، انتخاب و تغییر عناصر، ساخت و حذف عناصر، Attributes و Classes و Styles، پیمایش DOM، فرم‌ها و اعتبارسنجی، MutationObserver و IntersectionObserver و ResizeObserver.

DOM چیست؟

DOM (Document Object Model) نمایش درختی صفحه است — هر تگ HTML یک Node در این درخت:

// document — ریشه همه:
console.log(document.title);     // عنوان صفحه
console.log(document.domain);    // دامنه
console.log(document.head);      // <head>
console.log(document.body);      // <body>

// درخت: document → html → head/body → ...

انتخاب عناصر

// با ID — فقط یکی:
const byId = document.getElementById("app");

// با CSS Selector — مدرن و قدرتمند:
const first = document.querySelector(".item");       // اولین
const all = document.querySelectorAll(".item");      // همه — NodeList

// قدیمی‌تر:
const byClass = document.getElementsByClassName("item"); // HTMLCollection
const byTag = document.getElementsByTagName("div");

// پیمایش NodeList:
all.forEach(el => console.log(el.textContent));
console.log(all.length);
console.log(all[0]);
querySelector(All) با هر سلکتور CSS کار می‌کند: "#id"، ".class"، "div > p"، "[data-x]"، ":hover"...

تغییر محتوا

const el = document.querySelector("#message");

// textContent — متن خالص (امن):
el.textContent = "سلام <b>دنیا</b>";
// نمایش: سلام <b>دنیا</b> — تگ دیده می‌شود

// innerHTML — HTML واقعی (خطر XSS اگر ورودی کاربر!):
el.innerHTML = "سلام <b>دنیا</b>"; // bold می‌شود

// تغییر با Template:
const user = { name: "علی", score: 95 };
el.innerHTML = `
    <div class="user">
        <h3>${user.name}</h3>
        <p>امتیاز: ${user.score}</p>
    </div>
`;
هرگز innerHTML با ورودی کاربر بدون پاک‌سازی استفاده نکنید — خطر XSS!

تغییر Attributes و Styles

const img = document.querySelector("img");

// Attributes:
img.setAttribute("src", "logo.png");
console.log(img.getAttribute("alt"));
img.hasAttribute("title"); // بررسی
img.removeAttribute("title");

// dataset — data-* attributes:
const card = document.querySelector(".card");
card.dataset.userId = "42";       // data-user-id="42"
console.log(card.dataset.userId); // "42"

// Styles — یک به یک:
el.style.color = "red";
el.style.backgroundColor = "blue"; // camelCase!

// بهتر — با cssText یا class:
el.style.cssText = "color: red; font-size: 20px;";

کلاس‌ها — classList

const el = document.querySelector(".box");

el.classList.add("active", "visible");   // افزودن
el.classList.remove("hidden");           // حذف
el.classList.toggle("dark");             // خاموش/روشن
el.classList.contains("active");         // بررسی
el.classList.replace("old", "new");      // جایگزینی

console.log(el.className); // رشته کامل کلاس‌ها

ساخت و افزودن عناصر

// ساخت:
const li = document.createElement("li");
li.textContent = "مورد جدید";
li.classList.add("item");

// افزودن:
const list = document.querySelector("ul");
list.appendChild(li);          // آخر
list.prepend(li);              // اول
list.before(li);               // قبل از list
list.after(li);                // بعد از list

// insertAdjacentHTML — سریع و منعطف:
list.insertAdjacentHTML("beforeend", "<li>آخر</li>");
list.insertAdjacentHTML("afterbegin", "<li>اول</li>");

// clone:
const clone = li.cloneNode(true); // true = با فرزندان

حذف عناصر

const el = document.querySelector(".old");
el.remove();                     // مدرن — خودش

// قدیمی:
// el.parentNode.removeChild(el);

// خالی کردن:
list.innerHTML = "";

پیمایش DOM

const item = document.querySelector("li");

// والد:
item.parentElement;      // والد المنتی
item.parentNode;         // والد Node (متن هم می‌تواند باشد)

// فرزندان:
item.children;           // فقط المنت‌ها
item.firstElementChild;
item.lastElementChild;

// خواهر و برادر:
item.previousElementSibling; // قبلی
item.nextElementSibling;     // بعدی

// نزدیک‌ترین والد با سلکتور:
item.closest("ul");           // اولین ul بالاتر
item.closest(".list.container"); // با چند شرط

فرم‌ها و اعتبارسنجی

// انتخاب:
const form = document.querySelector("#signup");
const email = form.elements["email"];  // یا form.email
const name = form.querySelector("[name=name]");

// مقادیر:
console.log(email.value);
console.log(name.value);

// اعتبارسنجی با Constraint Validation:
form.addEventListener("submit", (e) => {
    e.preventDefault();  // جلوگیری از ارسال

    if (!form.checkValidity()) {
        // مرورگر پیام خطا نشان می‌دهد:
        form.reportValidity();
        return;
    }

    // دستی:
    const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    if (!emailPattern.test(email.value)) {
        email.setCustomValidity("ایمیل معتبر نیست");
        email.reportValidity();
        return;
    }

    console.log("فرم معتبر — ارسال!");
    form.submit();
});

// input event — زنده:
form.querySelector("[name=username]").addEventListener("input", (e) => {
    const isValid = e.target.value.length >= 3;
    e.target.classList.toggle("valid", isValid);
    e.target.classList.toggle("invalid", !isValid);
});

DOM Collections

// HTMLCollection — زنده (خودکار آپدیت):
const divs = document.getElementsByTagName("div");
// بعد از افزودن div جدید — divs.length خودکار بیشتر!

// NodeList — استاتیک از querySelectorAll:
const items = document.querySelectorAll(".item");
// اما قابل forEach:
items.forEach(el => console.log(el));

// تبدیل به آرایه:
const arr = [...document.querySelectorAll(".item")];
arr.filter(el => el.textContent.includes("مهم"));

MutationObserver — نظارت بر تغییرات

const target = document.querySelector("#list");

const observer = new MutationObserver((mutations) => {
    for (const mutation of mutations) {
        if (mutation.type === "childList") {
            console.log("عنصری اضافه/حذف شد");
            mutation.addedNodes.forEach(node =>
                console.log("اضافه:", node.textContent)
            );
        }
        if (mutation.type === "attributes") {
            console.log("ویژگی تغییر:", mutation.attributeName);
        }
    }
});

observer.observe(target, {
    childList: true,      // فرزندان
    attributes: true,     // ویژگی‌ها
    subtree: true,        // نوه‌ها هم
    characterData: true   // متن
});

// توقف:
// observer.disconnect();

IntersectionObserver — دید عنصر

// Lazy Loading و Infinite Scroll:
const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
        if (entry.isIntersecting) {
            // عنصر وارد صفحه شد:
            const img = entry.target;
            img.src = img.dataset.src;  // لود واقعی
            observer.unobserve(img);    // فقط بار اول
        }
    });
}, {
    root: null,        // viewport
    threshold: 0.1,    // ۱۰٪ دیده شود
    rootMargin: "100px" // زودتر شروع کن
});

document.querySelectorAll("img[data-src]").forEach(img => {
    observer.observe(img);
});

ResizeObserver — تغییر اندازه

const box = document.querySelector(".resizable");

const resizeObserver = new ResizeObserver(entries => {
    for (const entry of entries) {
        const { width, height } = entry.contentRect;
        console.log(`اندازه: ${width}×${height}`);

        // واکنش گرا:
        if (width < 600) {
            entry.target.classList.add("compact");
        } else {
            entry.target.classList.remove("compact");
        }
    }
});

resizeObserver.observe(box);

جمع‌بندی

کارابزار
انتخابquerySelector(All)
محتوای متنیtextContent — امن
HTMLinnerHTML — با احتیاط
کلاسclassList
ساختcreateElement + append
پیمایشclosest، children
نظارتObservers

این مبحث بخشی از دوره آموزش کامل جاوا اسکریپت است.

ابزارهای مرتبط