Element resize: DOM events and CSS queries

At the time of writing, acting upon a change in a HTML element size is still problematic.
In javascript the official support is limited, whilst in CSS there seems to be none. However, all is not lost as some passionate people
have come up with some crafty solutions.

Withing CSS element queries implementations, same as with @media queries you define discreet breakpoints where you apply new rule, as opposed to a continuous event firing in the Javascript approaches. It is often the case that many that events from Javascript power the behavior of the definitions found in the style sheet.

In the resources referenced here, there’s a chance you’ll come across both element queries and container queries terminology. The difference is that the former has the breakpoints and behavior associated to the same element whilst the latter has breakpoints on an element but the behavior for one or more of its children.

To easily showcase the example I’m using a plain textarea element, that’s resizable with the click drag by default.

1
<textarea></textarea>

The examples of this article have the width property mutation as the trigger, but you many of the solutions can let you hook to more changing values, height included. Before using any of these in production I advise you to explore their documentation.

I’m importing the libraries via the script tag but that does not mean you cannot toss them in using your preferred bundler.
I’m using query selectors to access the element.
Depending on your frontend library/framework of choice, you might have to use the corresponding way to get the element and check its existence.

Interval loop

You’ve probably though of this already. If there’s no solution out there, a setInterval call should do it.

1
2
3
4
5
6
7
8
9
let intervalId = setInterval(() => {
let el = document.getElementsByTagName('textarea')[0];
if (el) {
let width = el.clientWidth;
el.style.background = `rgb(${width % 255}, 40, 190)`;
} else {
clearInterval(intervalId);
}
}, 250);

Simple for a simple case. If you dealing with a more complex scenario, you’d need to think about scalability, efficiency and the intriguing corner cases.

ResizeObserver

The ResizeObserver is an API supported by modern versions of browsers, exceptions being Safari, Edge, Internet Explorer, Firefox Android, Safari iOS. Don’t feel down just yet, because polyfills are coming to the rescue. I would check ResizeObserver Polyfill or Resize Observer out. Happily enough, neither of them use a polling loop.

1
2
3
4
5
6
7
let el = document.getElementsByTagName('textarea')[0];
const observer = new ResizeObserver(entries => {
let entry = entries[0];
let width = entry.contentRect.width;
el.style.background = `rgb(${width % 255}, 40, 190)`;
});
observer.observe(el);

For details about the motivation and further applications you can check this page;

If you want to go the ResizeObserver-Vanilla path I recommend this excellent guide.

EQCSS

The EQCSS project is a draft specification and implementation of element queries in CSS.

You can easily download it and write queries as below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<style>
@element textarea and (max-width: 400px) {
:self {
background: rgb(0, 40, 190);
}
}
@element textarea and (max-width: 200px) {
:self {
background: rgb(200, 40, 190);
}
}
</style>

<script src="https://cdnjs.cloudflare.com/ajax/libs/eqcss/1.9.2/EQCSS.min.js"></script>

What’s awesome about EQCSS is that it exposes a vast number of queries, so you can react to width, height, characters, children, lines, scroll, orientation. On top of that it has selectors for parent, previous and next elements and also CSS units for the element. This makes it the approach with the largest API surface from this list.

CSS Element queries

CSS Element queries is another path you can take to defined element queries in CSS. The project is qualitative and has a good traction on Github. Currently it only supports interrogations for height and width, which might be all you need.

Curiously I could not make it work for the textarea element so I’ve replaced it with a simple resizable div.

1
<div class="resizable-area"></div>

For some reason the code is not deployed to a registry or a content delivery network, hence it needs downloading locally.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<style>
.resizable-area {
height: 30px;
width: 150px;
resize: both;
border: 1px solid black;
overflow: auto;
}
.resizable-area[min-width~="200px"] {
background: rgb(200, 40, 190);
}

.resizable-area[min-width~="400px"] {
background: rgb(0, 40, 190);
}
</style>

<script src="css-element-queries/src/ResizeSensor.js"></script>
<script src="css-element-queries/src/ElementQueries.js"></script>

As a bonus, the methods of ResizeSensor are “public” so you may employ them in Javascript.

1
2
3
4
5
let el = document.getElementsByClassName('resizable-area')[0];
new ResizeSensor(el, () => {
let width = el.clientWidth;
el.style.background = `rgb(${width % 256}, 40, 190)`;
});

jQuery resize plugin

Even if it has been ten years since the last commit, it’s worth mentioning the resize plugin for jQuery. The implementation is the simple polling using setTimeout but offers you the path of going straight to a familiar API, especially if your site is already powered by jQuery.

For installation I’m just pasting a CDN link towards the main library.

1
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>

For the plugin part, it seems you’d need to download (and re-upload somewhere else) the source as using the https://raw.githubusercontent.com/cowboy/jquery-resize/v1.1/jquery.ba-resize.min.js with a script tag yields a CORB error.

1
2
3
4
5
$('textarea').resize((e) => {
let el = e.target;
let width = el.clientWidth;
el.style.background = `rgb(${width % 255}, 40, 190)`;
});

Even more alternatives

I won’t bore you with syntax, so here are other good and more than good implementations, in no particular order:

Conclusion

Even if official support for an element’s resize is late to the part and limited, the community does not fail to provide the means. Therefore, you can use it in you project today!

Thumbnail image copyright © Michael Whelan.