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

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

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

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

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

ब्राउज़र के इस्तेमाल से जुड़ी सहायता

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

सोर्स

एपीआई

ऊपर बताए गए 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 में एक ऐसा तरीका है जिससे अनलिमिटेड कॉलबैक लूप और साइकलिकल डिपेंडेंसी से बचा जा सकता है. बदलावों को सिर्फ़ उसी फ़्रेम में प्रोसेस किया जाएगा, अगर साइज़ बदला गया एलिमेंट, पिछले कॉलबैक में प्रोसेस किए गए सबसे छोटे एलिमेंट से ज़्यादा डीप में डीओएम ट्री में है. ऐसा न करने पर, उन्हें अगले फ़्रेम में भेज दिया जाएगा.

ऐप्लिकेशन

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 मिलीसेकंड या उससे कम है, तो यह कहा जा सकता है कि पेज, उपयोगकर्ता के इंटरैक्शन के लिए भरोसेमंद तरीके से काम कर रहा है.

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

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

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

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

नतीजा

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