CSS media query matching using window.matchMedia()


CSS media query matching in JavaScript using
window.matchMedia()

There may be times when, in addition to CSS, you also need to do
something in JavaScript when a CSS media query is matched. While CSS purists
may cower at the idea of mixing the two when it comes to handling media
queries, at the end of the day, having JavaScript on board just means an
additional tool in our “responsive” arsenal, and a very robust tool at that.

In JavaScript, you can detect for the same CSS media query string defined
in CSS with the method window.matchMedia(). For example, in CSS,
if you have the following CSS media query:

/* #### CSS that’s
applied when the viewing area’s width is 765px or less #### */
@media screen and (max-width: 765px){
 /* CSS definitions here */
}

To detect the same media query in JavaScript, you’d do the following:

var mql = window.matchMedia(“screen and (max-width:
765px)”)

Notice the query string entered in both cases is identical with the
exception of “@media” in the former case. That’s the beauty of
the JavaScript set up- it pretty much mirrors the construct of its CSS
counterpart. window.matchMedia() returns a MediaQueryList
object containing a couple of methods and properties, the most frequently
used being the “matches” property. It’s a Boolean property that
returns true if the media query matches the state of the current window, and
false if not. More on this later.

Browser compatibility wise, window.matchMedia() is supported
in FF6+, IE10+, Chrome/ Safari, and Opera 12+. To test for browser support,
you can simply test for support for the property window.matchMedia.


window.matchMedia()
in detail

When you call window.matchMedia(), it returns a
MediaQueryList
object, which really is just an ordinary JavaScript
object containing some useful methods and properties for us to examine the
outcome of the match. Lets look at the returned properties first.

window.matchMedia() returned properties

Property
Description

matches
Boolean that returns true if the current state of the
window matches the conditions defined in the CSS query string, or false
if not.

media
Returns the serialized media query list. In the case of
the operation:

var mql = window.matchMedia(“screen
and (max-width: 765px)”)

mql.media would return in NON IE browsers:

“screen and (max-width: 765px)”

and
in IE (as of IE11) browsers, the same value, but with no space between a
property and its value:

“screen and (max-width:765px)”

Furthermore, in IE, if a media is not specified (ie: “screen“)
inside the media query list, it returns “all” instead. So
for the following operation:

var mql = window.matchMedia(“(max-width: 765px)”)

The property mql.media returns “(max-width: 765px)
in non IE browsers, but “all and (max-width:765px)” in IE
instead.

Due to these differences, when probing the media
property to determine the type of incoming media query, you should use
regular expressions to equalize the slight differences in the return
value between browsers.

The property you’ll most frequently be referring to is “matches“,
which returns true when our constructed media query matches the current
state of the window. The following easily checks at run time if the user’s
browser width is 800px or more:

var mql = window.matchMedia(“screen and (min-width:
800px)”)
if (mql.matches){ // if media query matches
 alert(“Window is 800px or wider”)
}
else{
 // do something else
}

Now, the above code only runs the desired CSS media query and compares it
to the current window state once at run time, and doesn’t react to any
changes to the later thereafter. At this point the code is hardly
responsive. In order to utilize window.matchMedia() in a
responsive manner, we need to also make use of its methods/ event handlers:

window.matchMedia() returned methods/ event handlers

method
Description

addListener( functionref)
Adds a new listener function, which is executed whenever
the state of the window changes and triggers a re-evaluation of the
defined CSS media query.

removeListener(functionref)
Removes a previously added listener function from
listening in on changes between the current state and the defined CSS
media query.

The key method to befriend is addListener(). By a function
to wrap the code we want to run and enter it into addListener(),
our function will now fire whenever any changes to the current window state
leads to a match against the defined CSS media query. In other words, our
code can now react to not just a CSS media query match at run time, but
whenever the window state changes..

The following code uses addListener() to react to a CSS
media query not just on run time, but also when any changes to the window
state occur:

function mediaqueryresponse(mql){
 if (mql.matches){ // if media query matches
  console.log(“The condition ” + mql.media + ” has been met”)
 }
 else{
  console.log(“Condition not met yet”)
 }
}

var mql = window.matchMedia(“screen and (max-device-width: 480px) and
(orientation: portrait)”)
mediaqueryresponse(mql) // call listener function explicitly at run time
mql.addListener(mediaqueryresponse) // attach listener function to listen in
on state changes

Here we’ve defined a listener function mediaqueryresponse().
Notice how such a function is inexplicitly passed the MediaQueryList
object from the call to window.matchMedia(), allowing our
function to process the result. One curious line in the above code is the
call to our mediaqueryresponse() function, in addition to
passing it into addListener(). This may seem redundant, but
it’s important to note that addListener() only fires when the
state of the window changes, which doesn’t occur when the page first loads.
The explicit call to mediaqueryresponse() ensures that this
function is also executed when the page loads.

Now we have all the pieces in JavaScript to respond to a CSS media query.
One caveat of bringing JavaScript into the equation is often the need to
duplicate the media query twice in your page- once in your CSS @media
rule, and again in JavaScript, depending on what you’re trying to do. This
could lead to maintenance issues, as it’s easy to change one query but
forget to update the same for the other. But that’s really a small price to
pay for the added robustness the language brings to sculpting responsive
pages.

End of Tutorial