DEV Community

Cover image for Using media queries with JavaScript
Maroun Baydoun
Maroun Baydoun

Posted on • Edited on

Using media queries with JavaScript

Media queries are not limited to CSS. JavaScript can also react to them. This can be very useful when building applications.


If you're looking for an existing solution for using media queries in JavaScript, checkout my small library mediaq.


The window.matchMedia API

Browsers expose the window.matchMedia method. It takes a media query as an argument and returns a MediaQueryList object.

 const mediaQueryList = window.matchMedia("only screen and (max-width: 600px");
Enter fullscreen mode Exit fullscreen mode

Checking if a media query matches

Once created, a MediaQueryList object has the boolean property matches. Use that property to check, at any time, if the media query is matching.

 if (mediaQueryList.matches) {
    console.log("It matches!");
 } else {
    console.log("It doesn't match.");
 }
Enter fullscreen mode Exit fullscreen mode

Listening for updates

Instead of checking if the MediaQueryList object matches, you can attach an event listener to it. The listener will trigger whenever the media query matches or stops matching (the window is resized, the device orientation changes etc.).

Since MediaQueryList inherits EventTarget, listening to the events it fires is very straightforward.

const listener = (event) => {
    console.log(event.matches ? "It matches!" : "It doesn't match.");
};

mediaQueryList.addEventListener("change", listener);
Enter fullscreen mode Exit fullscreen mode

Removing a listener is also as easy

mediaQueryList.removeEventListener("change", listener);
Enter fullscreen mode Exit fullscreen mode

Internet explorer and Safari < 14 handle those event listeners differently. They use addListener/removeListener instead.

To support those browsers as well, you can conditionally call the correct method.

const listener = (event) => {
    console.log(event.matches ? "It matches!" : "It doesn't match.");
};

if (mediaQueryList.addEventListener) {
  mediaQueryList.addEventListener("change", listener);
} else {
  mediaQueryList.addListener(listener);
}


// For removal

if (mediaQueryList.removeEventListener) {
  mediaQueryList.removeEventListener("change", listener);
} else {
  mediaQueryList.removeListener(listener);
}

Enter fullscreen mode Exit fullscreen mode

You can read more about browser compatibility.

Why use media queries with JavaScript?

When applied through CSS, media queries help create responsive layouts. They also allow hiding certain elements on the web page and loading higher/lower resolution background pictures.

In some scenarios that is not enough. That's when JavaScript can be used to optimize the webpage further.

Imagine a component of your webpage is computationally heavy and you decide you don't need it to display on smaller screens. In CSS, you can easily hide it, but it would still exist as part of your DOM and could still weigh down the performance of your web application.

@media only screen and (max-width: 480px") {
 .component {
   display: none; /* The component is hidden but is still part of the webpage.*/
 }
}
Enter fullscreen mode Exit fullscreen mode

In JavaScript, on the other hand, you could decide whether or not to append the element to the document.

const mediaQueryList = window.matchMedia("only screen and (max-width: 480px");

if (!mediaQueryList.matches) {
 // Create/Append the element here
}

Enter fullscreen mode Exit fullscreen mode

Another use case is loading certain scripts for certain screen sizes. If you application uses large graphing libraries, you can choose to include them only on desktop-size devices, where the graphs are being displayed.

const mediaQueryList = window.matchMedia("only screen and (min-width: 768px");

if (mediaQueryList.matches) {
  const script = document.createElement("script");
  script.src = "path to JavaScript script";
  document.body.appendChild(script);
}

Enter fullscreen mode Exit fullscreen mode

JavaScript can detect screen resolution without using media queries. This can work for one-time checks. Listening to updates with the resize event on the window object can be expensive. Using media queries is much more efficient.

Top comments (7)

Collapse
 
wparad profile image
Warren Parad • Edited

Often you are using a ux framework, and then would prefer to bind to the framework instead of hard coding the breakpoints. In that case, something like this is much better:

gist.github.com/wparad/6687a200ebb...

const xs = document.createElement('div');
xs.setAttribute('class', 'd-inline d-sm-none');

const sm = document.createElement('div');
sm.setAttribute('class', 'd-none d-sm-inline d-md-none');

const md = document.createElement('div');
md.setAttribute('class', 'd-none d-md-inline d-lg-none');

const lg = document.createElement('div');
lg.setAttribute('class', 'd-none d-lg-inline d-xl-none');

const xl = document.createElement('div');
xl.setAttribute('class', 'd-none d-xl-inline');

const breakpoints = { xs, sm, md, lg, xl };

import Vue from 'vue';
const activeBreakpoints = Vue.observable(Object.keys(breakpoints).reduce((a, b) => { a[b] = false; return a; }, {}));

function recalculate() {
  Object.keys(breakpoints).map(bp => {
    const style = window.getComputedStyle(breakpoints[bp]);
    Vue.set(activeBreakpoints, bp, !!style.display && style.display !== 'none');
  });
}

document.addEventListener('DOMContentLoaded', () => {
  try {
    const body = document.querySelector('body');
    const div = document.createElement('div');
    div.setAttribute('class', 'responsive-bootstrap-toolkit');
    Object.values(breakpoints).forEach(bp => { div.appendChild(bp); });
    body.appendChild(div);
    recalculate();
  } catch (error) { /* */ }
});

window.addEventListener('resize', recalculate);
export default activeBreakpoints;
Enter fullscreen mode Exit fullscreen mode
Collapse
 
maroun_baydoun profile image
Maroun Baydoun

Thanks for sharing. There are indeed multiple solutions to this problem.

Collapse
 
elusive241 profile image
elusive24 • Edited

Thanks for the solution, but its probably very inefficient because of multiple 'getComputedStyle' calls.

Collapse
 
wparad profile image
Warren Parad

It's actually very effective without a problem.

Thread Thread
 
elusive241 profile image
elusive24 • Edited

getComputedStyle() forces layout recalculation. While its ok for simple cases, in general, its a perfomance bottleneck.

Might be useful (what forces layout reflow):
gist.github.com/paulirish/5d52fb08...

Collapse
 
Sloan, the sloth mascot
Comment deleted
Collapse
 
andrewbaisden profile image
Andrew Baisden

Nice this is great.

Some comments may only be visible to logged-in visitors. Sign in to view all comments.