कम शब्दों में कहा जाए तो
यह रही सीक्रेट कुंजी: ऐसा हो सकता है कि आपको अपने अगले ऐप्लिकेशन में scroll
इवेंट की ज़रूरत न पड़े. किसी
IntersectionObserver
,
मैं दिखाता हूं कि position:sticky
एलिमेंट के ठीक होने या वे बने रहने बंद हो जाने पर, कस्टम इवेंट कैसे ट्रिगर किया जा सकता है. बिना
स्क्रोल लिसनर का इस्तेमाल करते हैं. यहां यह साबित करने के लिए एक शानदार डेमो भी है:
पेश है sticky-change
इवेंट
सीएसएस स्टिकी पोज़िशन का इस्तेमाल करने की एक असल सीमा यह है कि यह यह जानने के लिए प्लैटफ़ॉर्म सिग्नल नहीं देता कि प्रॉपर्टी कब चालू है. दूसरे शब्दों में, यह जानने का कोई इवेंट नहीं है कि कोई एलीमेंट कब स्टिकी बन जाता है या कब वह स्टिकी नहीं रह जाता.
यहां दिए गए उदाहरण पर नज़र डालें, जो वीडियो के <div class="sticky">
अपने पैरंट कंटेनर के सबसे ऊपरी हिस्से में:
.sticky {
position: sticky;
top: 10px;
}
क्या यह अच्छा नहीं होगा कि ब्राउज़र तब बताए, जब एलिमेंट उस चिह्न पर हिट करें?
ऐसा लगता है कि सिर्फ़ मैं ही नहीं हूं
सुझाव देता है. position:sticky
के सिग्नल की मदद से, कई इस्तेमाल के उदाहरणों को अनलॉक किया जा सकता है:
- अगर बैनर चिपक जाता है, तो उस पर ड्रॉप शैडो लगाएं.
- जब कोई उपयोगकर्ता आपका कॉन्टेंट पढ़ रहा हो, तो आंकड़ों से जुड़े हिट रिकॉर्ड करें प्रगति.
- जब उपयोगकर्ता पेज स्क्रोल करता है, तो फ़्लोटिंग टीओसी विजेट को मौजूदा सेक्शन में जाएं.
इस्तेमाल के इन उदाहरणों को ध्यान में रखते हुए, हमने एक लक्ष्य बनाया है: ऐसा इवेंट बनाएं जो
position:sticky
एलिमेंट के ठीक होने पर ट्रिगर होता है. चलिए इसे नाम देते हैं
sticky-change
इवेंट:
document.addEventListener('sticky-change', e => {
const header = e.detail.target; // header became sticky or stopped sticking.
const sticking = e.detail.stuck; // true when header is sticky.
header.classList.toggle('shadow', sticking); // add drop shadow when sticking.
document.querySelector('.who-is-sticking').textContent = header.textContent;
});
डेमो, इस इवेंट को ठीक कर दिए जाने पर, हेडर के बगल में ड्रॉप शैडो भेजा जाता है. इससे आपको यह भी अपडेट होता है नया शीर्षक क्लिक करें.
क्या स्क्रोल इवेंट के बिना इफ़ेक्ट स्क्रोल करने हैं?
![पेज का स्ट्रक्चर.](https://developer.chrome.google.cn/static/docs/css-ui/sticky-headers/image/structure-the-page-de19d5c111bd1.png?authuser=5&hl=hi)
चलिए, कुछ शब्दावली समझ लेते हैं, ताकि मैं इन नामों के लिए इनका इस्तेमाल कर सकूँ पोस्ट के बाकी हिस्से में:
- स्क्रोल करने वाला कंटेनर - वह कॉन्टेंट एरिया (दिखने वाला व्यूपोर्ट) जिसमें "ब्लॉग पोस्ट" की सूची.
- हेडर - हर सेक्शन में नीला टाइटल होता है, जिसमें
position:sticky
होता है. - स्टिकी सेक्शन - हर कॉन्टेंट सेक्शन. जिसके नीचे स्क्रॉल किया गया है स्टिकी हेडर.
- "स्टिकी मोड" - जब
position:sticky
एलिमेंट पर लागू किया जा रहा हो.
यह जानने के लिए कि कौनसा हेडर "स्टिकी मोड" में आता है, हमें पता लगाने का कोई तरीका चाहिए
स्क्रोल करने वाले कंटेनर का स्क्रोल ऑफ़सेट. ऐसा करने से, हमें एक
अभी दिखाई दे रहे हेडर का हिसाब लगाने के लिए. हालांकि, यह अच्छा
scroll
इवेंट के बिना करना मुश्किल है :) दूसरी समस्या यह है कि
position:sticky
, एलिमेंट का समाधान हो जाने पर, एलिमेंट को लेआउट से हटा देता है.
इसलिए, स्क्रोल इवेंट के बिना हमने लेआउट से जुड़ी कोई गतिविधि नहीं करवाई है कैलकुलेशन के लिए इस्तेमाल किया जाता है.
स्क्रोल की पोज़िशन तय करने के लिए, डमी डीओएम जोड़ना
scroll
इवेंट के बजाय, हम इस इवेंट के लिए IntersectionObserver
का इस्तेमाल करेंगे
तय करें कि हेडर स्टिकी मोड में कब आएं और कब बाहर निकलें. दो नोड जोड़ना
(इन्हें सेंटिनल भी कहते हैं), हर स्टिकी सेक्शन में, एक सबसे ऊपर और एक
सबसे नीचे, स्क्रोल की स्थिति का पता लगाने के लिए वेपॉइंट के तौर पर काम करेगा. जैसे,
मार्कर कंटेनर में प्रवेश करते और जाते हैं, और उनकी दृश्यता बदल जाती है और
इंटरसेक्शन ऑब्ज़र्वर, कॉलबैक ट्रिगर करता है.
![सेंटिनल एलिमेंट दिखाए बिना](https://developer.chrome.google.cn/static/docs/css-ui/sticky-headers/image/without-sentinel-elements-f9529d3483c91.gif?authuser=5&hl=hi)
हमें ऊपर और नीचे स्क्रोल करने के चार मामलों को कवर करने के लिए दो सहायकों की ज़रूरत है:
- नीचे स्क्रोल करना - जब हेडर का ऊपर का हिस्सा पार हो जाता है, तो हेडर स्टिकी हो जाता है सबसे ऊपर.
- नीचे स्क्रोल करना - हेडर एक पेज के सबसे निचले हिस्से तक पहुंचने पर, स्टिकी मोड को छोड़ देता है सेक्शन और उसका निचला सेंटिनल कंटेनर के ऊपरी हिस्से से गुज़रता हो.
- ऊपर की ओर स्क्रोल करना - जब हेडर का ऊपर का हिस्सा स्क्रोल करने पर, हेडर का स्टिकी मोड बंद हो जाता है उसे वापस ऊपर से देखा जा सकता है.
- ऊपर की ओर स्क्रोल करना - हेडर का नीचे वाला सेंटिनल पीछे जाने पर स्टिकी हो जाता है को ऊपर से देख सकता है.
एक से चार के क्रम में स्क्रीनकास्ट देखना फ़ायदेमंद होता है:
सीएसएस
निगरानी वाले ग्रुप को हर सेक्शन के सबसे ऊपर और सबसे नीचे रखा जाता है.
.sticky_sentinel--top
हेडर पर सबसे ऊपर है और
.sticky_sentinel--bottom
, सेक्शन के सबसे नीचे रहता है:
![नीचे का रक्षक अपनी सीमा पर पहुंच रहा है.](https://developer.chrome.google.cn/static/docs/css-ui/sticky-headers/image/bottom-sentinel-reaching-208a13f07bd7c.png?authuser=5&hl=hi)
:root {
--default-padding: 16px;
--header-height: 80px;
}
.sticky {
position: sticky;
top: 10px; /* adjust sentinel height/positioning based on this position. */
height: var(--header-height);
padding: 0 var(--default-padding);
}
.sticky_sentinel {
position: absolute;
left: 0;
right: 0; /* needs dimensions */
visibility: hidden;
}
.sticky_sentinel--top {
/* Adjust the height and top values based on your on your sticky top position.
e.g. make the height bigger and adjust the top so observeHeaders()'s
IntersectionObserver fires as soon as the bottom of the sentinel crosses the
top of the intersection container. */
height: 40px;
top: -24px;
}
.sticky_sentinel--bottom {
/* Height should match the top of the header when it's at the bottom of the
intersection container. */
height: calc(var(--header-height) + var(--default-padding));
bottom: 0;
}
इंटरसेक्शन ऑब्ज़र्वर सेट अप करना
प्रतिच्छेदन पर्यवेक्षक एसिंक्रोनस रूप से टारगेट एलिमेंट और दस्तावेज़ के व्यूपोर्ट या पैरंट कंटेनर की जानकारी दे सकते हैं. हमारे मामले में, हम पैरंट कंटेनर के साथ चौराहों का पता लगाते हैं.
मैजिक सॉस IntersectionObserver
है. हर सेंटिनल को
IntersectionObserver
की मदद से,
स्क्रोल कंटेनर. जब कोई चेतावनी देने वाला व्यक्ति, दिखने वाले व्यूपोर्ट में स्क्रोल करता है, तो हमें पता चलता है कि
हेडर ठीक हो गया हो या स्टिकी होना बंद हो गया हो. इसी तरह, जब कोई निगरानी रखने वाला व्यक्ति बाहर निकल जाता है
व्यूपोर्ट.
सबसे पहले, मैंने हेडर और फ़ुटर पर नज़र रखने वाले लोगों के लिए ऑब्ज़र्वर सेट अप किए:
/**
* Notifies when elements w/ the `sticky` class begin to stick or stop sticking.
* Note: the elements should be children of `container`.
* @param {!Element} container
*/
function observeStickyHeaderChanges(container) {
observeHeaders(container);
observeFooters(container);
}
observeStickyHeaderChanges(document.querySelector('#scroll-container'));
इसके बाद, .sticky_sentinel--top
एलिमेंट के पास होने पर मैंने एक ऑब्ज़र्वर जोड़ा, ताकि फ़ायर हो सके
स्क्रोल कंटेनर के ऊपरी हिस्से से गुज़रते हुए (किसी भी दिशा में).
observeHeaders
फ़ंक्शन मुख्य चेतावनी संदेश बनाता है और उन्हें
सेक्शन में फ़िल्टर करें. ऑब्ज़र्वर, सेंटिनल के इंटरसेक्शन की गणना
कंटेनर का ऊपरी हिस्सा मौजूद होता है और तय करता है कि इसे व्यूपोर्ट में शामिल किया जा रहा है या छोड़ा जा रहा है. वह
जानकारी से यह तय होता है कि सेक्शन हेडर मौजूद है या नहीं.
/**
* Sets up an intersection observer to notify when elements with the class
* `.sticky_sentinel--top` become visible/invisible at the top of the container.
* @param {!Element} container
*/
function observeHeaders(container) {
const observer = new IntersectionObserver((records, observer) => {
for (const record of records) {
const targetInfo = record.boundingClientRect;
const stickyTarget = record.target.parentElement.querySelector('.sticky');
const rootBoundsInfo = record.rootBounds;
// Started sticking.
if (targetInfo.bottom < rootBoundsInfo.top) {
fireEvent(true, stickyTarget);
}
// Stopped sticking.
if (targetInfo.bottom >= rootBoundsInfo.top &&
targetInfo.bottom < rootBoundsInfo.bottom) {
fireEvent(false, stickyTarget);
}
}
}, {threshold: [0], root: container});
// Add the top sentinels to each section and attach an observer.
const sentinels = addSentinels(container, 'sticky_sentinel--top');
sentinels.forEach(el => observer.observe(el));
}
ऑब्ज़र्वर को threshold: [0]
के साथ कॉन्फ़िगर किया गया है, इसलिए इसका कॉलबैक जल्द ही ट्रिगर हो जाता है
जैसे ही मैसेज मिलता है.
नीचे की तरफ़दार (.sticky_sentinel--bottom
) के लिए भी यही प्रोसेस है.
जब फ़ुटर सबसे नीचे से गुज़रते हैं, तब एक दूसरा ऑब्ज़र्वर बनाया जाता है, जो सक्रिय होता है
स्क्रोल करने वाले कंटेनर का. observeFooters
फ़ंक्शन,
सेंटिनल नोड्स में जोड़ देता है और उन्हें हर सेक्शन में जोड़ता है. ऑब्ज़र्वर
कंटेनर के निचले हिस्से के साथ सेंटिनल को इंटरसेक्शन करता है और तय करता है कि यह
दर्ज करने या छोड़ने के लिए. उस जानकारी से तय होता है कि सेक्शन हेडर
चिपकना चाहिए या नहीं.
/**
* Sets up an intersection observer to notify when elements with the class
* `.sticky_sentinel--bottom` become visible/invisible at the bottom of the
* container.
* @param {!Element} container
*/
function observeFooters(container) {
const observer = new IntersectionObserver((records, observer) => {
for (const record of records) {
const targetInfo = record.boundingClientRect;
const stickyTarget = record.target.parentElement.querySelector('.sticky');
const rootBoundsInfo = record.rootBounds;
const ratio = record.intersectionRatio;
// Started sticking.
if (targetInfo.bottom > rootBoundsInfo.top && ratio === 1) {
fireEvent(true, stickyTarget);
}
// Stopped sticking.
if (targetInfo.top < rootBoundsInfo.top &&
targetInfo.bottom < rootBoundsInfo.bottom) {
fireEvent(false, stickyTarget);
}
}
}, {threshold: [1], root: container});
// Add the bottom sentinels to each section and attach an observer.
const sentinels = addSentinels(container, 'sticky_sentinel--bottom');
sentinels.forEach(el => observer.observe(el));
}
ऑब्ज़र्वर को threshold: [1]
के साथ कॉन्फ़िगर किया गया है, इसलिए इसका कॉलबैक तब ट्रिगर होता है, जब
पूरा नोड व्यू के अंदर है.
आखिर में, sticky-change
कस्टम इवेंट ट्रिगर करने के लिए मेरी दो सुविधाएं उपलब्ध हैं
और सेंटिनेल जनरेट करना:
/**
* @param {!Element} container
* @param {string} className
*/
function addSentinels(container, className) {
return Array.from(container.querySelectorAll('.sticky')).map(el => {
const sentinel = document.createElement('div');
sentinel.classList.add('sticky_sentinel', className);
return el.parentElement.appendChild(sentinel);
});
}
/**
* Dispatches the `sticky-event` custom event on the target element.
* @param {boolean} stuck True if `target` is sticky.
* @param {!Element} target Element to fire the event on.
*/
function fireEvent(stuck, target) {
const e = new CustomEvent('sticky-change', {detail: {stuck, target}});
document.dispatchEvent(e);
}
हो गया!
आखिरी डेमो
हमने कस्टम इवेंट तब बनाया, जब position:sticky
वाले एलिमेंट बन जाते हैं
scroll
इवेंट का इस्तेमाल किए बिना, स्क्रोल इफ़ेक्ट जोड़े गए और उन्हें ठीक किया गया.
नतीजा
मैं अक्सर सोचती हूं कि IntersectionObserver
एक मददगार टूल की मदद से, scroll
इवेंट पर आधारित यूज़र इंटरफ़ेस (यूआई) पैटर्न को बदला जा सकता है
जो कई सालों में बेहतर हुए हैं. ऐसा करने से जवाब हां और नहीं में आएगा. द सिमेंटिक्स
IntersectionObserver
API का इस्तेमाल करने पर हर चीज़ के लिए इस्तेमाल करना मुश्किल हो जाता है. हालांकि, जैसा कि
मैंने यहां दिखाया है, आप कुछ दिलचस्प तकनीकों के लिए इसका इस्तेमाल कर सकते हैं.
शैली में हुए बदलावों का पता लगाने का दूसरा तरीका क्या है?
दरअसल ऐसा नहीं है. हमें डीओएम एलिमेंट की स्टाइल में हुए बदलावों का पता लगाने के लिए, एक तरीके की ज़रूरत थी. माफ़ करें, वेब प्लैटफ़ॉर्म एपीआई में ऐसा कुछ नहीं है जो आपको ये काम करने की अनुमति दे देखने की स्टाइल में बदलाव होता है.
MutationObserver
पहली पसंद होगी, लेकिन यह
ज़्यादातर मामलों में. उदाहरण के लिए, डेमो में हमें तब कॉलबैक मिलेगा, जब sticky
क्लास को एलिमेंट में जोड़ा जाता है, लेकिन एलिमेंट की कंप्यूट की गई स्टाइल बदलने पर नहीं.
याद रखें कि sticky
क्लास का एलान, पेज लोड होने पर पहले ही किया जा चुका था.
आने वाले समय में,
"स्टाइल म्यूटेशन ऑब्ज़र्वर"
म्यूटेशन ऑब्ज़र्वर का एक्सटेंशन
के लिए कंप्यूट किए गए एलिमेंट के साथ भी यही किया जा सकता है.
position: sticky
.