ResizeObserver - a new powerful tool for working with adaptability

Good day, friends!

Responsive is one of the standards of web development. There are a large number of screen resolutions, and this number is increasing all the time. We strive to support all possible screen sizes while maintaining a friendly user interface. An excellent solution to this problem are media queries (media-queries). But what about web components? Modern web development is based on components, and we need a way to make them responsive. Today I want to talk about the ResizeObserver API, which allows you to monitor (observe) the changes in the size of a particular element, and not the entire viewport (viewport), as is the case with media queries.

A bit of history


Previously, we had only media queries at our disposal - a CSS solution based on the size, type and resolution of the screen of a media device (by a media device, I mean a computer, phone or tablet). Media queries are quite flexible and easy to use. For a long time, media queries were only available in CSS, now they are also available in JS through window.matchMedia (mediaQueryString). Now we can check from which device the page is viewed, and also monitor the change in the size of the viewing area (we are talking about the MediaQueryList.addListener () method - approx.

Element Queries


What we lacked was the ability to monitor the size of a single DOM element, and not just the entire viewport. Developers have been complaining about this for many years. This is one of the most anticipated features. In 2015, even a proposal was put forward - requests for container sizes ( Container Queries ):
Developers often need the ability to style elements when they resize their parent container, regardless of the viewing area. Requests for container sizes give them this opportunity. CSS usage example:
.element: media (min-width: 30em) screen {***}
It sounds great, but browser makers had a good reason to reject this offer - circular dependency (when one size defines another, this leads to an infinite loop; more on this can be found here ). What other options are there? We can use window.resize (callback), but this is an “expensive pleasure” - a callback will be called every time an event occurs, and we will need a lot of calculations to determine that the size of our component has really changed ...

Monitoring element resizing using the ResizeObserver API


Meet the ResizeObserver API from Chrome:
The ResizeObserver API is an interface for tracking element resizing. This is a kind of analogue of the window.resize event for an element.

The ResizeObserver API is a live draft. It is already implemented in Chrome, Firefox and Safari for PC. Mobile support is less impressive - only Chrome on Android and Samsung Internet. Unfortunately, a full-fledged polyphile does not exist. Available polyphiles contain some limitations (for example, a slow response to resizing or lack of support for a smooth transition). However, this should not stop us from testing this API. So let's do it!

Example: changing text when resizing an element


Imagine the following situation - the text inside the element should change depending on the size of the element. The ResizeObserver API provides two tools - ResizeObserver and ResizeObserverEntry. ResizeObserver is used to track the size of an item, and ResizeObserverEntry contains information about the item that has resized.
The code is very simple:

<h1> size </h1>
<h2> boring text </h2>

const ro = new ResizeObserver(entries => {
    for(let entry of entries){
        const width = entry.contentBoxSize
        ? entry.contentBoxSize.inlineSize
        : entry.contentRect.width

        if(entry.target.tagName === 'H1'){
            entry.target.textContent = width < 1000 'small' : 'big'
        }

        if(entry.target.tagName === 'H2' && width < 500){
            entry.target.textContent = `I won't change anymore`
            ro.unobserve(entry.target) //  ,     500px
        }
    }
})

//       
ro.observe(document.querySelector('h1'))
ro.observe(document.querySelector('h2'))

First, create a ResizeObserver object and pass a callback function to it as a parameter:

const resizeObserver = new ResizeObserver((entries, observer) => {
    for(let entry of entries){
        // 
    }
})

The function is called every time one of the elements contained in ResizeObserverEntries is resized. The second parameter of the callback function is observer itself. We can use it, for example, to stop tracking when a certain condition is met.

Callback gets an array of ResizeObserverEntry. Each entry contains the dimensions of the observed element (target).

for(let entry of entries){
    const width = entry.contentBoxSize
    ? entry.contentBoxSize.inlineSize
    : entry.contentRect.width

    if(entry.target.tagName === 'H1'){
        entry.target.textContent = width < 1000 ? 'small' : 'big'
    }
    ...
}

We have three properties that describe the size of the element - borderBoxSize, contentBoxSize, and contentRect. They represent the box model of the element, which we will talk about later. Now a few words about support. Most browsers support contentRect, however, apparently, this property will be deprecated:
contentRect appeared at the preliminary development stage of ResizeObserver and was added only in the interests of current compatibility. Probably in the future it will be considered obsolete.


Therefore, I would highly recommend using contentRect in conjunction with bordeBoxSize and contentBoxSize. ResizeObserverSize includes two properties: inlineSize and blockSize, which can be interpreted as width and height (provided that we work in the horizontal orientation of the text - writing-mode: horizontal).

Element Observation


The last thing to do is to start tracking the item. To do this, we call ResizeObserver.observe (). This method adds a new target to the list of observed items. We can add to this list either one or several elements at once:

// resizeObserver(target, options)
ro.observe(document.querySelector('h1'))
ro.observe(document.querySelector('h2'))

The second parameter is “optional”. To date, the only option available is box, which defines a block model. Possible values ​​are content-box (default), border-box, and device-pixel-content-box (Chrome only). Only one block model can be defined in one ResizeObserver.

To stop monitoring, use ResizeObserver.unobserve (target). To stop tracking all elements, use ResizeObserver.disconnect ().

Block model


Content box is the content of the block without padding, border and margin. Border box includes padding and border (no margin).



Device pixels content box is the content of the element in physical pixels. I have not seen examples of the use of this model, but it seems that this may come in handy when working with canvas. Here's an interesting discussion on this topic on Github.

When does an observer find out about changes?


A callback is called each time the target element is resized. Here's what the spec says about it:

  • Observation is triggered when an observed item is added / removed from the DOM.
  • Observation is triggered when the display property of the observed element is set to none.
  • Observation does not work for “unsubstituted” line elements.
  • Observation does not work in CSS transformation.
  • , , .. , 0,0.

According to the first paragraph, we can determine the change in the parent container when changing its children. A great example of such a use of the ResizeObserver API is to scroll down the chat window when adding a new message. An example can be seen here .

Remember the container size queries I mentioned earlier? About his problem of circular dependence? So, the ResizeObserver API has a built-in solution to prevent an endless loop of "resize". Read about it here .

Thank you for attention.

Useful Links: MDN CanIUse

Specification First article from the development team Most popular polyfil



Source: https://habr.com/ru/post/undefined/


All Articles