Breakpoint Checking in Javascript with CSS User Values

Tuesday morning, Jeremy Keith posted a very interesting problem on his blog regarding conditionally loaded content. Long story short, he wanted the ability to test for a user-defined declaration in CSS with JavaScript without employing a resize event or use of matchMedia.

Paul Hayes came up with a lovely solution, but it needs to check for a specific media query. In this case, your media queries need to be maintained in two places. If you decide to change a media query in your CSS, then you need to also change it in your JavaScript.

In a perfect world, CSS and JavaScript separation (even in the case of media query detection) should be maintained.

Rob Tarr and I worked to develop our own solution. First off, which CSS property could be used to test this theory? The property needs to accept any value thrown at it. The content property seemed like a great fit. Why not test the value of content from inside a throwaway :before or :after pseudo-element?

Things are going well so far, but we still needed this to fire on an event of some kind. Paul Hayes’ previously mentioned solution had the answer: transition events! 

We created a mashup of these two ideas by checking for content on transition instead of media query size. We used “small,” “medium,” and “large” as the content string to give us a loose coupling between the triggered media query and our goal in JavaScript. With this solution, we can claim that multiple media query breakpoints should still be considered “medium,” which is something that could come in very handy. We also found that we needed to attach the transition to the parent of the pseudo element because the transitionend event never fired when transitioning in elements with a content property defined.

Here’s the gist, so you can see it for yourself. Also, play around on this jsFiddle with your console open. The irony of this solution is that it actually has better browser support than matchMedia! The only caveat is that IE 9 falls between the cracks. IE 9 supports media queries, but it does not support the transitionend event, which this solution depends on. So, IE 9 will need a fallback.

Wrapup

Is this hacky? Yes.

Keep in mind, it's just a proof of concept, but we think it has some legs. We also made a simple demo for you here. Enjoy.

The gist in its entirety:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
<!doctype html>
<html>
<head>
<title>CSS Media Check</title>
<style type="text/css">
#mediaquery{
-webkit-transition: width .001s;
-moz-transition: width .001s;
-ms-transition: width .001s;
-o-transition: width .001s;
transition: width .001s;
}
#mediaquery:after {
content: "small";
display: none;
}
@media all and (min-width: 10px) {
#mediaquery {
width: 50px;
}
#mediaquery:after {
content: "small";
}
}
@media all and (min-width: 600px) {
#mediaquery{
width: 100px;
}
#mediaquery:after {
content: "medium";
}
}
@media all and (min-width: 800px) {
#mediaquery{
width: 200px;
}
#mediaquery:after {
content: "large";
}
}
</style>
</head>
<body><div id="mediaquery"></div>
<script>
var mq = document.getElementById("mediaquery");
function eventCheck() {
console.log(window.getComputedStyle(document.getElementById("mediaquery"),":after").getPropertyValue("content"));
}
// Check for all browsers
mq.addEventListener('webkitTransitionEnd', eventCheck, true);
mq.addEventListener('MSTransitionEnd', eventCheck, true);
mq.addEventListener('oTransitionEnd', eventCheck, true);
mq.addEventListener('transitionend', eventCheck, true);
</script>
</body>
</html>
view raw check.html hosted with ❤ by GitHub