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. Enjoy.
<!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>