Resize ऑब्ज़र्वर: यह एलिमेंट के लिए, document.onresize की तरह काम करता है

ResizeObserver आपको किसी एलिमेंट के साइज़ में बदलाव होने पर सूचना देता है.

ResizeObserver से पहले, आपको दस्तावेज़ के resize इवेंट से एक लिसनर अटैच करना होता था, ताकि व्यूपोर्ट के डाइमेंशन में होने वाले किसी भी बदलाव की सूचना मिल सके. इसके बाद, इवेंट हैंडलर में आपको यह पता लगाना होगा कि उस बदलाव से किन एलिमेंट पर असर पड़ा है. साथ ही, सही तरीके से जवाब देने के लिए, किसी खास रूटीन को कॉल करना होगा. अगर आपको साइज़ बदलने के बाद किसी एलिमेंट के नए डाइमेंशन की ज़रूरत होती है, तो आपको getBoundingClientRect() या getComputedStyle() को कॉल करना पड़ता था. अगर आपने सभी रीड और सभी राइट को बैच में नहीं रखा, तो लेआउट थ्रैशिंग हो सकती है.

इसमें उन मामलों को भी शामिल नहीं किया गया था जहां मुख्य विंडो का साइज़ बदले बिना, एलिमेंट का साइज़ बदल जाता है. उदाहरण के लिए, नए चाइल्ड जोड़ने, किसी एलिमेंट की display स्टाइल को none पर सेट करने या इसी तरह की अन्य कार्रवाइयों से, किसी एलिमेंट, उसके सिबलिंग या उसके पूर्वजों का साइज़ बदल सकता है.

इसलिए, ResizeObserver एक काम का प्रिमिटिव है. यह किसी भी एलिमेंट के साइज़ में हुए बदलावों पर प्रतिक्रिया देता है. इससे कोई फ़र्क़ नहीं पड़ता कि बदलाव किस वजह से हुआ है. इससे, देखे गए एलिमेंट के नए साइज़ का ऐक्सेस भी मिलता है.

Browser Support

  • Chrome: 64.
  • Edge: 79.
  • Firefox: 69.
  • Safari: 13.1.

Source

एपीआई

ऊपर बताए गए Observer सफ़िक्स वाले सभी एपीआई का डिज़ाइन एक जैसा होता है. ResizeObserver भी इससे अलग नहीं है. आपको एक ResizeObserver ऑब्जेक्ट बनाना होगा और कंस्ट्रक्टर को एक कॉलबैक पास करना होगा. कॉलबैक को ResizeObserverEntry ऑब्जेक्ट का एक कलेक्शन पास किया जाता है. इसमें हर मॉनिटर किए गए एलिमेंट के लिए एक एंट्री होती है. इसमें एलिमेंट के नए डाइमेंशन शामिल होते हैं.

var ro = new ResizeObserver(entries => {
  for (let entry of entries) {
    const cr = entry.contentRect;

    console.log('Element:', entry.target);
    console.log(`Element size: ${cr.width}px x ${cr.height}px`);
    console.log(`Element padding: ${cr.top}px ; ${cr.left}px`);
  }
});

// Observe one or multiple elements
ro.observe(someElement);

कुछ जानकारी

किस तरह के कॉन्टेंट की शिकायत की जा रही है?

आम तौर पर, ResizeObserverEntry, contentRect नाम की प्रॉपर्टी के ज़रिए किसी एलिमेंट के कॉन्टेंट बॉक्स की जानकारी देता है. यह प्रॉपर्टी, DOMRectReadOnly ऑब्जेक्ट दिखाती है. कॉन्टेंट बॉक्स वह बॉक्स होता है जिसमें कॉन्टेंट रखा जा सकता है. यह बॉर्डर बॉक्स है. इसमें से पैडिंग को घटा दिया गया है.

सीएसएस बॉक्स मॉडल का डायग्राम.

यह ध्यान रखना ज़रूरी है कि ResizeObserver, contentRect और पैडिंग, दोनों डाइमेंशन की रिपोर्ट करता है. हालांकि, यह सिर्फ़ contentRect को मॉनिटर करता है. contentRect को एलिमेंट के बाउंडिंग बॉक्स के साथ भ्रमित न करें. getBoundingClientRect() के मुताबिक, बाउंडिंग बॉक्स वह बॉक्स होता है जिसमें पूरा एलिमेंट और उसके डिसेंडेंट शामिल होते हैं. एसवीजी इस नियम का अपवाद हैं. इनमें ResizeObserver, बाउंडिंग बॉक्स के डाइमेंशन की जानकारी देगा.

Chrome 84 के बाद से, ResizeObserverEntry में तीन नई प्रॉपर्टी जोड़ी गई हैं. इनसे ज़्यादा जानकारी मिलती है. इनमें से हर प्रॉपर्टी, एक ResizeObserverSize ऑब्जेक्ट दिखाती है. इस ऑब्जेक्ट में blockSize प्रॉपर्टी और inlineSize प्रॉपर्टी होती है. यह जानकारी, कॉलबैक शुरू होने के समय देखे गए एलिमेंट के बारे में होती है.

  • borderBoxSize
  • contentBoxSize
  • devicePixelContentBoxSize

इन सभी आइटम में, सिर्फ़ पढ़ने के लिए उपलब्ध ऐरे दिखते हैं. ऐसा इसलिए है, क्योंकि आने वाले समय में इनमें ऐसे एलिमेंट इस्तेमाल किए जा सकते हैं जिनमें कई फ़्रैगमेंट होते हैं. ये फ़्रैगमेंट, मल्टी-कॉलम वाले लेआउट में दिखते हैं. फ़िलहाल, इन ऐरे में सिर्फ़ एक एलिमेंट होगा.

इन प्रॉपर्टी के लिए, प्लैटफ़ॉर्म का सपोर्ट सीमित है. हालांकि, Firefox पहले से ही पहली दो प्रॉपर्टी के साथ काम करता है.

इसकी शिकायत कब की जा रही है?

स्पेसिफ़िकेशन में बताया गया है कि ResizeObserver को पेंट से पहले और लेआउट के बाद, साइज़ बदलने से जुड़े सभी इवेंट प्रोसेस करने चाहिए. इसलिए, ResizeObserver का कॉलबैक, आपके पेज के लेआउट में बदलाव करने के लिए सबसे सही जगह है. ऐसा इसलिए, क्योंकि ResizeObserver प्रोसेसिंग, लेआउट और पेंट के बीच होती है. इसलिए, ऐसा करने से सिर्फ़ लेआउट अमान्य होगा, पेंट नहीं.

समझ गया

आपके मन में यह सवाल आ सकता है कि अगर मैंने ResizeObserver के कॉलबैक में, मॉनिटर किए जा रहे एलिमेंट का साइज़ बदल दिया, तो क्या होगा? इसका जवाब यह है कि आपको कॉलबैक फ़ंक्शन को तुरंत फिर से कॉल करना होगा. अच्छी बात यह है कि ResizeObserver में, लगातार कॉल किए जाने वाले लूप और साइक्लिक डिपेंडेंसी से बचने का तरीका मौजूद है. बदलावों को सिर्फ़ उसी फ़्रेम में प्रोसेस किया जाएगा अगर बदले गए साइज़ वाला एलिमेंट, DOM ट्री में उस सबसे छोटे एलिमेंट से ज़्यादा डीप है जिसे पिछले कॉलबैक में प्रोसेस किया गया था. ऐसा न होने पर, उन्हें अगले फ़्रेम पर भेज दिया जाएगा.

ऐप्लिकेशन

ResizeObserver की मदद से, हर एलिमेंट के लिए मीडिया क्वेरी लागू की जा सकती हैं. एलिमेंट को देखकर, अपने डिज़ाइन के ब्रेकपॉइंट तय किए जा सकते हैं. साथ ही, किसी एलिमेंट की स्टाइल बदली जा सकती हैं. नीचे दिए गए उदाहरण में, दूसरे बॉक्स की चौड़ाई के हिसाब से उसके बॉर्डर रेडियस में बदलाव होगा.

const ro = new ResizeObserver(entries => {
  for (let entry of entries) {
    entry.target.style.borderRadius =
        Math.max(0, 250 - entry.contentRect.width) + 'px';
  }
});
// Only observe the second box
ro.observe(document.querySelector('.box:nth-child(2)'));

चैट विंडो एक और दिलचस्प उदाहरण है. ऊपर से नीचे की ओर बातचीत के लेआउट में, स्क्रोल करने की पोज़िशन से जुड़ी समस्या आती है. उपयोगकर्ता को भ्रमित होने से बचाने के लिए, यह मददगार होता है कि विंडो को बातचीत के सबसे नीचे रखा जाए. यहीं पर नए मैसेज दिखते हैं. इसके अलावा, लेआउट में किसी भी तरह के बदलाव (जैसे, फ़ोन को लैंडस्केप से पोर्ट्रेट या पोर्ट्रेट से लैंडस्केप मोड में बदलना) से भी यही नतीजा मिलना चाहिए.

ResizeObserver की मदद से, एक ऐसा सिंगल कोड लिखा जा सकता है जो दोनों स्थितियों को हैंडल कर सकता है. विंडो का साइज़ बदलना एक ऐसा इवेंट है जिसे ResizeObserver कैप्चर कर सकता है. हालांकि, appendChild() को कॉल करने पर भी एलिमेंट का साइज़ बदल जाता है. ऐसा तब तक होता है, जब तक overflow: hidden सेट न हो. ऐसा इसलिए होता है, क्योंकि इसे नए एलिमेंट के लिए जगह बनानी होती है. इसे ध्यान में रखते हुए, मनमुताबिक इफ़ेक्ट पाने के लिए बहुत कम लाइनों की ज़रूरत होती है:

const ro = new ResizeObserver(entries => {
  document.scrollingElement.scrollTop =
    document.scrollingElement.scrollHeight;
});

// Observe the scrollingElement for when the window gets resized
ro.observe(document.scrollingElement);

// Observe the timeline to process new messages
ro.observe(timeline);

यह काफ़ी अच्छा है, है न?

यहां से, मैं ऐसा कोड जोड़ सकता हूं जो इस स्थिति को हैंडल करे. इसमें उपयोगकर्ता ने मैन्युअल तरीके से ऊपर की ओर स्क्रोल किया है और जब कोई नया मैसेज आता है, तो वह चाहता है कि स्क्रोल उस मैसेज पर बना रहे.

इसका इस्तेमाल, किसी भी तरह के कस्टम एलिमेंट के लिए किया जा सकता है. ResizeObserver तक, इसके डाइमेंशन में बदलाव होने पर सूचना पाने का कोई भरोसेमंद तरीका नहीं था, ताकि इसके चाइल्ड एलिमेंट को फिर से व्यवस्थित किया जा सके.

पेज के रिस्पॉन्स में लगने वाले समय (आईएनपी) पर पड़ने वाले असर

पेज के रिस्पॉन्स में लगने वाला समय (आईएनपी) एक ऐसी मेट्रिक है जिससे यह पता चलता है कि किसी व्यक्ति के पेज से इंटरैक्शन करते समय, वह पेज कितना रिस्पॉन्सिव था. अगर किसी पेज का आईएनपी, "अच्छा" थ्रेशोल्ड में है, यानी कि 200 मिलीसेकंड या इससे कम है, तो इसका मतलब है कि पेज, उपयोगकर्ता के साथ होने वाले इंटरैक्शन पर तुरंत प्रतिक्रिया देता है.

उपयोगकर्ता के इंटरैक्शन के जवाब में इवेंट कॉलबैक को पूरा होने में लगने वाला समय, इंटरैक्शन की कुल लेटेन्सी में अहम भूमिका निभा सकता है. हालांकि, INP के लिए सिर्फ़ इसी पहलू पर विचार नहीं किया जाता. आईएनपी, इंटरैक्शन के अगले पेंट में लगने वाले समय को भी ध्यान में रखता है. यह वह समय है जो किसी इंटरैक्शन के जवाब में, उपयोगकर्ता इंटरफ़ेस को अपडेट करने के लिए ज़रूरी रेंडरिंग के काम को पूरा करने में लगता है.

ResizeObserver के मामले में, यह ज़रूरी है, क्योंकि ResizerObserver इंस्टेंस से चलने वाला कॉलबैक, रेंडरिंग से ठीक पहले होता है. ऐसा इसलिए होता है, क्योंकि कॉलबैक में होने वाले काम को ध्यान में रखना होता है. ऐसा इसलिए, क्योंकि उस काम के नतीजे के लिए यूज़र इंटरफ़ेस में बदलाव करना ज़रूरी होगा.

ResizeObserverकॉल बैक में, सिर्फ़ उतना रेंडरिंग वर्क करें जितना ज़रूरी हो. ज़्यादा रेंडरिंग वर्क करने से ऐसी स्थितियां पैदा हो सकती हैं जहां ब्राउज़र को ज़रूरी काम करने में देरी हो सकती है. उदाहरण के लिए, अगर किसी इंटरैक्शन में ऐसा कॉलबैक है जिसकी वजह से ResizeObserver कॉलबैक चलता है, तो पक्का करें कि आप सबसे बेहतर अनुभव देने के लिए, यह काम कर रहे हों:

  • पक्का करें कि आपके सीएसएस सिलेक्टर ज़्यादा से ज़्यादा आसान हों, ताकि स्टाइल को बार-बार कैलकुलेट करने से बचा जा सके. स्टाइल की फिर से गणना, लेआउट से ठीक पहले होती है. साथ ही, जटिल सीएसएस सिलेक्टर, लेआउट की कार्रवाइयों में देरी कर सकते हैं.
  • ResizeObserver कॉलबैक में ऐसा कोई काम न करें जिससे फ़ोर्स रीफ़्लो ट्रिगर हो सकें.
  • किसी पेज के लेआउट को अपडेट करने में लगने वाला समय आम तौर पर, पेज पर मौजूद डीओएम एलिमेंट की संख्या के साथ बढ़ता है. यह बात इस पर निर्भर नहीं करती कि पेज ResizeObserver का इस्तेमाल करते हैं या नहीं. हालांकि, पेज की स्ट्रक्चरल जटिलता बढ़ने पर, ResizeObserver कॉलबैक में किया गया काम ज़्यादा अहम हो सकता है.

नतीजा

ResizeObserver, सभी मुख्य ब्राउज़र में उपलब्ध है. साथ ही, यह एलिमेंट के लेवल पर एलिमेंट के साइज़ में होने वाले बदलावों को मॉनिटर करने का एक असरदार तरीका है. बस इस बात का ध्यान रखें कि इस पावरफ़ुल एपीआई का इस्तेमाल करते समय, रेंडरिंग में ज़्यादा समय न लगे.