مقدمه

بسیاری از تعاملات ما با DOM به صورت دستوری (imperative) است؛ یعنی ما به صراحت به مرورگر می‌گوییم چه کاری انجام دهد. اما در وب اپلیکیشن‌های مدرن، اغلب نیاز داریم تا به تغییراتی که در DOM رخ می‌دهد، واکنش (react) نشان دهیم. Observable APIs مجموعه‌ای از رابط‌های برنامه‌نویسی هستند که به ما اجازه می‌دهند تغییرات خاصی را در DOM "مشاهده" کرده و به محض وقوع، یک تابع را اجرا کنیم. این روش بسیار بهینه‌تر از بررسی مداوم و دستی (polling) وضعیت DOM است. در این درس با دو مورد از مهم‌ترین این APIها آشنا می‌شویم: MutationObserver و IntersectionObserver.

رهگیری تغییرات DOM با MutationObserver

MutationObserver یک API مدرن برای مشاهده‌ی تغییرات در درخت DOM است. این تغییرات می‌تواند شامل افزودن یا حذف گره‌های فرزند، تغییر در اتریبیوت‌های یک گره، یا تغییر در محتوای یک گره متنی باشد. این API جایگزین متدهای قدیمی و منسوخ شده‌ی Mutation Events شده است که مشکلات عملکردی جدی داشتند.

روند کار با آن شامل سه مرحله است: ابتدا یک تابع callback تعریف می‌کنیم که هنگام بروز تغییرات اجرا شود. سپس یک نمونه از MutationObserver با آن callback می‌سازیم. در نهایت، متد observe() را روی آن نمونه فراخوانی کرده و گره هدف به همراه یک شیء پیکربندی (که مشخص می‌کند چه نوع تغییراتی باید رهگیری شوند) را به آن پاس می‌دهیم.

Copy Icon JAVASCRIPT
// HTML: <ul id="my-list"></ul>
const list = document.getElementById('my-list');

// 1. The callback function gets a list of mutation records
const callback = function(mutationsList, observer) {
    for(const mutation of mutationsList) {
        if (mutation.type === 'childList') {
            console.log('A child node has been added or removed.');
        }
    }
};
// 2. Create an observer instance
const observer = new MutationObserver(callback);
// 3. Configuration and start observing
const config = { childList: true, subtree: true };
observer.observe(list, config);

// Later in the code, a change is made
setTimeout(() => list.appendChild(document.createElement('li')), 1000);
// To stop observing: observer.disconnect();

در این کد، ما یک `observer` ایجاد می‌کنیم که تغییرات در لیست فرزندان (childList) عنصر list را مشاهده می‌کند. یک ثانیه بعد، یک <li> جدید به لیست اضافه می‌شود و بلافاصله تابع callback ما اجرا شده و پیام را در کنسول ثبت می‌کند.

رهگیری نمایان شدن عناصر با IntersectionObserver

یکی از چالش‌های رایج در توسعه وب، فهمیدن این است که چه زمانی یک عنصر وارد محدوده دید کاربر (viewport) می‌شود. IntersectionObserver یک API بسیار کارآمد برای حل همین مشکل است. کاربردهای اصلی آن شامل بارگذاری تنبل (lazy-loading) تصاویر و ویدئوها، پیاده‌سازی اسکرول بی‌نهایت (infinite scrolling)، و اجرای انیمیشن‌ها هنگام نمایان شدن عناصر است.

روش کار با آن بسیار شبیه به MutationObserver است: یک تابع callback و یک شیء options (برای تنظیم دقیق نحوه تقاطع) تعریف می‌کنیم، یک نمونه IntersectionObserver می‌سازیم و سپس با متد observe()، آن را روی هر عنصری که می‌خواهیم مشاهده کنیم، فعال می‌کنیم.

Copy Icon JAVASCRIPT
// HTML: <img class="lazy" data-src="real-image.jpg">
const lazyImages = document.querySelectorAll('.lazy');

const imageObserver = new IntersectionObserver((entries, observer) => {
    entries.forEach(entry => {
        // When the image is intersecting the viewport
        if (entry.isIntersecting) {
            const img = entry.target;
            img.src = img.dataset.src;
            img.classList.remove('lazy');
            observer.unobserve(img); // Stop observing this image
        }
    });
});

lazyImages.forEach(img => {
    imageObserver.observe(img);
});

این مثال یک الگوی رایج برای بارگذاری تنبل تصاویر را نشان می‌دهد. ما به IntersectionObserver می‌گوییم که هر تصویر با کلاس lazy را مشاهده کند. به محض اینکه یک تصویر وارد محدوده دید کاربر شود (isIntersecting برابر با true می‌شود)، ما آدرس واقعی تصویر را از اتریبیوت data-src به اتریبیوت src منتقل کرده و سپس مشاهده‌ی آن تصویر را متوقف می‌کنیم تا منابع بیهوده مصرف نشود.

در این درس با دو API قدرتمند برای مشاهده‌ی واکنشی DOM آشنا شدیم: MutationObserver برای رهگیری تغییرات در ساختار DOM و IntersectionObserver برای تشخیص ورود و خروج عناصر از دید کاربر. با این درس، فصل «اکستنشن‌های DOM» به پایان می‌رسد. ما در این فصل دانش خود را با بررسی استایل‌ها، روش‌های پیمایش مدرن، مفهوم Ranges و APIهای مشاهده‌گر، عمیق‌تر کردیم. در فصل بعدی، به یکی از بنیادی‌ترین و مهم‌ترین بخش‌های جاوااسکریپت تعاملی، یعنی «رویدادها» (Events) خواهیم پرداخت و یاد می‌گیریم که چگونه به تعاملات کاربر مانند کلیک، حرکت ماوس و ورودی‌های کیبورد واکنش نشان دهیم.