Learn how to conditionally run JavaScript code depending on a media query.
We all know CSS media queries, which allow us to apply different styles depending on a @media
rule.
But did you know media queries aren't only restricted to CSS? Sometimes it can be useful to run JavaScript code depending on a media query.
How? With the window
API method matchMedia()
.
In short, it's a simple and fast way to write media queries in JavaScript. The concept is the same as in CSS: if the condition of the media query is matched, then run some code. We can pass any of the CSS @media
rules (screen, orientation, min-width, etc) as a string argument to the method, which returns a MediaQueryList
object with
two properties and two methods.
Properties:
matches
: a boolean which is true
if the media query is satisfied or false
otherwise. This is the basic usage and more intuitive property that you will be using in most cases, and is very similar to how we write CSS media queries.media
: the media query itself.Methods:
addlistener()
or addEventListener()
: adds a callback which is called when the media query changes its state.removeListener()
or removeEventListener()
: removes the specified callback previously defined in the addListener()
.The code below will change this paragraph border color depending on the width of your browser viewport. The default border color is the same as the text (black/white), but if you're reading this on a device with a viewport smaller than 960px, then you'll see it in orange.
const exampleElement = document.getElementById('example-static');if (exampleElement &&window.matchMedia('(max-width: 960px)').matches) {exampleElement.style.border = '2px solid orange';}
The code below does the same as the previous one, but thanks to the listener it's responsive to dynamic changes of the viewport. If you are in the browser and change the viewport size manually, you'll see that the color is toggled at viewport width 960px.
const exampleElement = document.getElementById('example-dynamic');function changeBorderColor() {if (exampleElement) {if (exampleElement.style.border === '2px solid var(--color-text)') {exampleElement.style.border = '2px solid #00d9ff';} else {exampleElement.style.border = '2px solid var(--color-text)';}}}const mediaQueryListObject = window.matchMedia('(max-width: 960px)');// attach listener// https://stackoverflow.com/questions/56466261/matchmedia-addlistener-marked-as-deprecated-addeventlistener-equivalent// https://developer.mozilla.org/en-US/docs/Web/API/MediaQueryList/addListenertry {mediaQueryListObject.addEventListener('change', changeBorderColor);} catch {try {mediaQueryListObject.addListener(changeBorderColor);} catch (e2) {alert(e2);}}// first page loadif (exampleElement &&window.matchMedia('(max-width: 960px)').matches) {exampleElement.style.border = '2px solid orange';}
Pretty simple and straightforward implementation right?
In the second code block it can be argued that it would be more optimal to use the traditional window.addEventListener('resize', callbackFunction)
and use window.innerWidth
to
obtain the current viewport width, but the presented approach using matchMedia()
is intended to show how this method lets us detect media query changes in JavaScript,
not to provide the best approach to that specific problem.
However, listening to resize
events will call the callback function on every viewport resize,
which would be less performant than using the matchMedia()
method that only calls the callback when the specified media query is matched.
In addition to this, using the presented method gives us a much broader spectrum of possibilities since it can not only perform traditional operations like listening to changes of the viewport width, but also detect changes on the well-known media queries.
https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia
https://www.w3.org/TR/cssom-view/#dom-window-matchmedia
https://developer.mozilla.org/en-US/docs/Web/API/MediaQueryList
https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Testing_media_queries
📣If you've found something missing, something wrong or something that could be improved in this post, please drop a pull request in the site's repo or email me at contact@davidmitjana.me.