Media queries in javascript — the better way
We all know and love the CSS media queries, they make our lives much easier when we build responsive websites.
Now in the days of CSS preprocessors we mainly use variables for our media queries in order to keep them synced across our project:
$small:”(max-width: 500px)”;
$medium:”(min-width: 501px) and (max-width: 1200px)”;
$xlarge:”(min-width: 1920px)”;.elem {
background: red; // the default is large viewport
@media #{$small}{ background: blue; }
@media #{$medium}{ background: green; }
@media #{$xlarge}{ background: green; }
}

But what we should do when we want to use media queries in our javascript? the usual suspects will be window.matchMedia which will return a new MediaQueryList object representing the parsed results of the specified media query string (MDN docs) for example:
function windowWidthChange(mq) {
if (mq.matches) {
console.log('window width less than 500px');
} else {
console.log('window width is more than 500px');
}
}const $small = '(max-width: 500px)';
const mq = window.matchMedia($small);
mq.addListener(windowWidthChange);
This works great but has a basic issue, we need to define the $small variable in two decoupled files, javascript and sass, and the best thing to do in big projects with a lot of sass/js files is to define a variable once and use it everywhere. if the variable is changed in one place we want it to be applied everywhere.
To achieve this (define a variable once) we can use animations and animationend event:
// SASS
$small:”(max-width: 500px)”;
$medium:”(min-width: 501px) and (max-width: 1200px)”;
$large:”(min-width: 1201) and (max-width: 1919px)”;
$xlarge:”(min-width: 1920px)”;@keyframes small {
from { clip: rect(1px, auto, auto, auto); }
to { clip: rect(0px, auto, auto, auto); }
}
@keyframes medium {
from { clip: rect(1px, auto, auto, auto); }
to { clip: rect(0px, auto, auto, auto); }
}
@keyframes large {
from { clip: rect(1px, auto, auto, auto); }
to { clip: rect(0px, auto, auto, auto); }
}
@keyframes xlarge {
from { clip: rect(1px, auto, auto, auto); }
to { clip: rect(0px, auto, auto, auto); }
}body {
animation-duration: 0.001s;
animation-name: large;
@media #{$small}{ animation-name: small; }
@media #{$medium}{ animation-name: medium; }
@media #{$xlarge}{ animation-name: xlarge; }
}
// JS
document.addEventListener(‘animationend’, (event) => {
switch (event.animationName) {
case ‘small’: console.log(‘small’); break;
case ‘medium’: console.log(‘medium’); break;
case ‘large’: console.log(‘large’); break;
case ‘xlarge’: console.log(‘xlarge’); break;
}
}, false);With this approach we get the same functionality as window.matchMedia but we define the variables only in the sass code, so if we deiced to change our media queries the change will apply in the javascript as well and our project will be consistent!
