How to observe responsive breakpoints
Have you ever wanted to know in which breakpoint you page is, so you may adapt part of the page in ways CSS cannot, such as setting aria attributes?
By using custom properties and media queries, it is simpler than ever.
Let say you have defined the following custom properties, such as in Open Props
...
--size-xs: 360px;
--size-sm: 480px;
--size-md: 768px;
--size-lg: 1024px;
--size-xl: 1440px;
...
You can access those value in javascript and map them to media queries
const breakpoints = ['--size-xs', '--size-sm', '--size-md', '--size-lg', '--size-xl'];
const styles = window.getComputedStyle(document.body);
const mediaQueries = breakpoints.map(function (size) {
const value = styles.getPropertyValue(size);
return window.matchMedia(`screen and (min-width: ${value})`);
});
Because the breakpoints are sorted by ascending screen, the matches
property of the media query will be true for all breakpoints less than the current screen size.
More importantly, you can also register an event listener that will be called every time the matches
property of the media query changes.
So, what we can do is to find the last breakpoint every time one of the media query changes, and then dispatch an event of our own.
By putting it all together, we get
class Responsive {
constructor() {
this.breakpoints = [...arguments];
const styles = window.getComputedStyle(document.body);
const mediaQueries = this.breakpoints.slice(1).map(size => {
const value = styles.getPropertyValue(size);
const m = window.matchMedia(`screen and (min-width: ${value})`);
m.addEventListener('change', this);
return m;
});
this.currentBreakpoint = null;
this.handleEvent = function () {
const found = mediaQueries.findLastIndex(m => m.matches);
const newBreakpoint = this.breakpoints[1 + found];
if (this.currentBreakpoint !== newBreakpoint) {
const oldBreakpoint = this.currentBreakpoint;
this.currentBreakpoint = newBreakpoint;
window.dispatchEvent(new CustomEvent("breakpointhit", { detail: { newBreakpoint, oldBreakpoint } }));
}
};
}
}
And, to use it
window.responsive = new Responsive('--size-xs', '--size-sm', '--size-md', '--size-lg', '--size-xl');
window.addEventListener('breakpointhit', function (e) {
console.log(`Breakpoint changed from ${e.detail.oldBreakpoint} to ${e.detail.newBreakpoint}`);
});
You may have notice that we skipped the first breakpoint when we created the media query, and that we get the index of the last media query that matches, plus 1.
It is because we assume that the lowest breakpoint is always true.