खास जानकारी
उपयोगकर्ता चाहते हैं कि कैरसेल या गैलरी जैसे यूज़र इंटरफ़ेस (यूआई) एलिमेंट के साथ इंटरैक्ट करने पर, उन्हें तुरंत विज़ुअल फ़ीडबैक मिले. ट्रेडिशनल स्क्रोल स्नैप, स्क्रोल जेस्चर के पूरा होने और एलिमेंट के सेटल होने के बाद ही फ़ीडबैक देता है. स्क्रोल स्नैप इवेंट, खास तौर पर scrollsnapchanging का इस्तेमाल करके, स्क्रोल जेस्चर के दौरान रीयल-टाइम में फ़ीडबैक दिया जा सकता है. इससे उपयोगकर्ता के टच या माउस को छोड़ने से पहले, पेंडिंग स्नैप टारगेट को हाइलाइट किया जा सकता है.
लागू करना
1. scrollsnapchanging में सुनें
स्क्रोल कंटेनर में scrollsnapchanging के लिए इवेंट लिसनर अटैच करें. यह इवेंट तब ट्रिगर होता है, जब ब्राउज़र को लगता है कि कोई नया स्नैप टारगेट चुना जा सकता है.
const container = document.querySelector('#gallery');
const thumbnails = document.querySelectorAll('.thumbnail');
const items = document.querySelectorAll('.gallery-item');
container.addEventListener('scrollsnapchanging', (event) => {
// Highlight pending snap target during scroll for real-time feedback.
const pendingTarget = event.snapTargetInline;
const index = [...items].indexOf(pendingTarget);
if (index === -1 || !thumbnails[index]) return;
// Use lightweight class toggle to avoid layout thrashing during rapid events.
// Note: aria-current is NOT toggled here. It tracks the settled "current"
// item, which is updated in the scrollsnapchange handler below.
thumbnails.forEach((thumb) => thumb.classList.remove('pending'));
thumbnails[index].classList.add('pending');
});
इस उदाहरण में snapTargetInline का इस्तेमाल किया गया है, क्योंकि गैलरी हॉरिज़ॉन्टल स्क्रोल होती है. अगर आपका स्क्रोल कंटेनर वर्टिकल तौर पर स्क्रोल करता है, तो इसके बजाय snapTargetBlock का इस्तेमाल करें.
2. scrollsnapchange में सुनें
स्क्रोल जेस्चर पूरा होने और एलिमेंट के स्नैप होने पर, स्थिति को फ़ाइनलाइज़ करने के लिए, scrollsnapchange इवेंट को सुनें. यह ज़रूरी है, ताकि आखिरी ऐक्टिव स्टेट को सेट किया जा सके.
container.addEventListener('scrollsnapchange', (event) => {
// Promote pending state to active on scroll completion.
const snappedTarget = event.snapTargetInline;
const index = [...items].indexOf(snappedTarget);
if (index === -1 || !thumbnails[index]) return;
// Establish final active state and clean up pending.
thumbnails.forEach((thumb) => {
thumb.classList.remove('pending', 'active');
thumb.removeAttribute('aria-current');
});
thumbnails[index].classList.add('active');
thumbnails[index].setAttribute('aria-current', 'true');
});
3. सिंक की शुरुआती स्थिति
पेज लोड होने पर, हो सकता है कि ब्राउज़र, स्क्रोल की पोज़िशन को पहले जैसा कर दे. उदाहरण के लिए, इतिहास में जाकर या ऐंकर लिंक की मदद से. scrollsnapchange और scroll, दोनों ही इवेंट अपने-आप ट्रिगर नहीं होंगे. यूज़र इंटरफ़ेस (यूआई) को स्क्रोल की शुरुआती पोज़िशन के साथ सिंक करने के लिए, एक बार ज्यामितीय जांच करें.
// Note: For item.offsetLeft to be relative to the container,
// the container MUST be the offsetParent (e.g., `position: relative`).
const findClosestItemIndex = () => {
// Center-distance assumes scroll-snap-align: center on items.
// For start-aligned snap, compare scrollLeft to item.offsetLeft directly.
const containerCenter = container.scrollLeft + container.clientWidth / 2;
let closestIndex = 0;
let minDistance = Infinity;
items.forEach((item, index) => {
const itemCenter = item.offsetLeft + item.offsetWidth / 2;
const distance = Math.abs(containerCenter - itemCenter);
if (distance < minDistance) {
minDistance = distance;
closestIndex = index;
}
});
return closestIndex;
};
const initActiveItem = () => {
const closestIndex = findClosestItemIndex();
if (!thumbnails[closestIndex]) return;
thumbnails.forEach((thumb) => {
thumb.classList.remove('pending', 'active');
thumb.removeAttribute('aria-current');
});
thumbnails[closestIndex].classList.add('active');
thumbnails[closestIndex].setAttribute('aria-current', 'true');
};
if (document.readyState === 'complete') {
initActiveItem();
} else {
window.addEventListener('load', initActiveItem, { once: true });
}
फ़ॉलबैक रणनीतियां
scrollsnapchanging के साथ काम न करने वाले ब्राउज़र के लिए, यूज़र इंटरफ़ेस (यूआई) डिफ़ॉल्ट रूप से स्क्रोल जेस्चर के दौरान तुरंत फ़ीडबैक नहीं देगा. साथ ही, लिंक किया गया यूज़र इंटरफ़ेस (यूआई), कॉन्टेंट के साथ सिंक नहीं होगा.
ज़रूरी: ऐसे ब्राउज़र के लिए फ़ॉलबैक उपलब्ध कराएं जिनमें यह सुविधा काम नहीं करती. ऐसा न करने पर, लिंक किया गया यूज़र इंटरफ़ेस (यूआई), कॉन्टेंट के साथ सिंक नहीं होगा.
scrollsnapchanging की रीयल-टाइम नेचर को सिम्युलेट करने के लिए, requestAnimationFrame और ज्यामितीय दूरी के कैलकुलेशन के साथ scroll इवेंट लिसनर का इस्तेमाल करें. इससे, उपयोगकर्ता के स्क्रोल करने के दौरान, सबसे नज़दीकी स्नैप टारगेट का पता लगाया जा सकता है.
if ('onscrollsnapchanging' in Element.prototype) {
// Use native scroll snap events
} else {
// Fallback: use scroll + requestAnimationFrame for eager feedback
// (assumes the same container, thumbnails, items defined in step 1)
let scrollTimeout;
let rafId = null;
const promotePendingToActive = () => {
const closestIndex = findClosestItemIndex();
if (!thumbnails[closestIndex]) return;
thumbnails.forEach((thumb) => {
thumb.classList.remove('pending', 'active');
thumb.removeAttribute('aria-current');
});
thumbnails[closestIndex].classList.add('active');
thumbnails[closestIndex].setAttribute('aria-current', 'true');
};
container.addEventListener('scroll', () => {
if (rafId) return;
rafId = requestAnimationFrame(() => {
rafId = null;
const closestIndex = findClosestItemIndex();
if (!thumbnails[closestIndex]) return;
// DO NOT forget to clean up stale pending classes
thumbnails.forEach((thumb) => thumb.classList.remove('pending'));
thumbnails[closestIndex].classList.add('pending');
});
// Debounce fallback for browsers that don't support scrollend
clearTimeout(scrollTimeout);
scrollTimeout = setTimeout(promotePendingToActive, 100);
}, { passive: true });
// Fallback: use Baseline `scrollend` event to promote pending to active cleanly where supported
container.addEventListener('scrollend', () => {
clearTimeout(scrollTimeout);
promotePendingToActive();
});
}
ज्योमेट्रिक scroll + requestAnimationFrame फ़ॉलबैक, नेटिव स्नैप प्रेडिक्शन के व्यवहार को बारीकी से एम्युलेट करता है. इसमें प्रोग्राम के हिसाब से स्क्रोल करने की सुविधा को सही तरीके से मैनेज करना भी शामिल है. scrollIntoView जैसे फ़ंक्शन, अपने एक्ज़ीक्यूशन के दौरान scroll इवेंट को ट्रिगर करते हैं. इसलिए, स्क्रोल ऐनिमेशन के दौरान यूज़र इंटरफ़ेस (यूआई) आसानी से सिंक रहेगा. इसके लिए, आपको अलग से कोई कस्टम लॉजिक जोड़ने की ज़रूरत नहीं पड़ेगी.