Skip to content

NCZOnline - Nicholas C. Zakas
Syndicate content
The Official Web Site of Nicholas C. Zakas
Updated: 1 hour 21 min ago

CSS media queries in JavaScript, Part 2

Thu, 01/19/2012 - 17:30

In my previous post[1], I introduced using CSS media queries in JavaScript both through a custom implementation and using the CSSOM Views matchMedia() method. Media queries are incredibly useful, both in CSS and JavaScript, and so I continued with my research to see how best to take advantage of this capability. As it turns out, the matchMedia() method has a few interesting quirks that I didn’t realize when I wrote the first part of this series.

matchMedia() and its quirks

Recall that matchMedia() returns a MediaQueryList object that allows you to determine whether or not the given media type matches the current state of the browser. This is done using the matches property, which returns a boolean. As it turns out, matches is a getter, which requeries the state of the browser each time it’s called:

var mql = window.matchMedia("screen and (max-width:600px)");
console.log(mql.matches);

//resize the browser

console.log(mql.matches);  //requeries

This is actually really useful, because it allows you to keep a reference to a MediaQueryList object and repeatedly check the state of the query against the page.

Chrome and Safari have a weird behavior, though. The initial value for matches is always correct but doesn’t get updated by default unless the page has a media block defined with the same query and at least one rule (hat tip: Rob Flaherty[2]. For instance, in order for a MediaQueryList representing “screen and (max-width:600px)” to update appropriately (including firing events), you must have something like this in your CSS:

@media screen and (max-width:600px) {
    .foo { }
}

There needs to be at least one rule in the media block, but it doesn’t matter if that rule is empty. As long as this exists on the page then the MediaQueryList will be updated appropriately and any listeners added via addListener() will fire when appropriate. Without this media block on the page, the MediaQueryList acts like a snapshot of the page state at its creation time.[3]

You can fix this by adding a new rule using JavaScript:

var style = document.createElement("style");
style.appendChild(document.createTextNode("@media screen and (max-width:600px) { .foo {} }"));
document.head.appendChild(style);    //WebKit supports document.head

Of course, you would need to do that for every media query being accessed using matchMedia(), which is a bit of a pain.

There is also a strange quirk in Firefox’s implementation. In theory, you should be able to assign a handler for when the query state changes and not keep a reference to the MediaQueryList object, such as:

//doesn't quite work in Firefox
window.matchMedia("screen and (max-width:600px)").addListener(function(mql) {
     console.log("Changed!");
});

When this pattern is used in Firefox, the listener may never actually be called even though the media query has become valid. In my tests, it would fire between 0 and 3 times, and then never again. The Firefox team has acknowledged this is a bug[4] and should hopefully be fixed soon. In the meantime, you need to keep the MediaQueryList reference around to ensure your listeners fire:

//fix for Firefox
var mql = window.matchMedia("screen and (max-width:600px)");
mql.addListener(function(mql) {
     console.log("Changed!");
});

The listener here will continue to be called as long as there is a reference to the mql object.

More on listeners

My initial description of the media query listeners in my previous post was incomplete due to a misunderstanding on my part. The listeners are actually trigger in two instances:

  1. When the media query initially becomes valid. So in the previous example, when the screen becomes 600 pixels wide or less.
  2. When the media query initially becomes invalid. For example, when the screen becomes wider than 600 pixels.

This behavior is why the MediaQueryList object is passed into the listener, so you can check matches to determine if the media query just became valid or not. For example:

mql.addListener(function(mql) {
    if (mql.matches) {
        console.log("Matches now!");
    } else {
        console.log("Doesn't match now!");
    }
});

Using code like this, you can monitor when a web application moves into and out of certain states, allowing you to alter the behavior accordingly.

To polyfill or not?

When I first looked at matchMedia(), I did so with the intent of creating a polyfill. Paul Irish[5] implemented a polyfill using a technique similar to the one I described in my last post (and gave me credit for it, thanks Paul!). Paul Hayes then forked[6] his work to create a polyfill with rudimentary listener support based on a very ingenuous use of CSS transitions to detect changes. However, as it relies on CSS transitions, the listener support is limited to browsers with CSS transition support. That, coupled with the fact that calling matches doesn’t requery the browser state, and the bugs in both Firefox and WebKit, led me to believe that building a polyfill wasn’t the right approach. After all, how can you polyfill appropriately when there are such obvious bugs in the real implementations that need fixing?

My approach was to create a facade to wrap this behavior in an API where I could smooth out the issues. Of course, I chose to implement the API as a YUI Gallery module[7] called gallery-media. The API is very simple and consists of two methods. The first is Y.Media.matches(), which takes a media query string and returns true if the media matches and false if not. No need to keep track of any objects, just get the info:

var matches = Y.Media.matches("screen and (max-width:600px)");

The second method is Y.Media.on(), which allows you to specify a media query and a listener to call when the media query becomes valid or invalid. The listener is passed an object with matches and media properties to give you information about the media query. For example:

var handle = Y.Media.on("screen and (max-width:600px)", function(mq) {
    console.log(mq.media + ":" + mq.matches);
});

//detach later
handle.detach();

Instead of using CSS transitions to monitor for changes, I use a simple onresize event handler. On the desktop, the size of the browser window is the main thing that will change (as opposed to mobile devices, where the orientation may also change), so I made this simplifying assumption for older browsers. The API uses the native matchMedia() functionality where available and patches up the differences in WebKit and Chrome so that you get consistent behavior.

Conclusion

CSS media queries in JavaScript are a bit more complicated than I first expected, but still quite useful. I don’t think it’s appropriate to polyfill matchMedia() giving the strange bugs that are still abound, effectively preventing you from even using the native code the same way across browsers. A facade, on the other hand, insulates you from the bugs and changes that are likely to occur going forward. Now go forth and use CSS media queries to their potential…in JavaScript.

References
  1. CSS media queries in JavaScript, Part 1 by me
  2. Rob Flaherty’s tweet
  3. matchMedia() MediaQueryList not updating
  4. matchMedia() listeners lost
  5. matchMedia polyfill by Paul Irish
  6. matchMedia polyfill by Paul Hayes
  7. YUI 3 Gallery Media module by me
Categories: Blogs

Book review: The Tangled Web

Tue, 01/17/2012 - 17:30

The Tangled WebI’m not really sure what I was expecting from The Tangled Web: A Guide to Securing Modern Web Applications. Having learned more about web security in the past year, I suppose I was hoping for a more in-depth treatment of common web application security issues. In my mind, I pictured a chapter on Cross-Site Scripting attacks and mitigation steps, a chapter on Cross-Site Request Forgery and what to do about it, etc. Instead, the book tackles the security problem with an exhaustive and dry examination of all the technologies that make up the web. Though interesting technically, it’s very easy to get lost in these details and end up at the other end unsure of how the description relates to real-world security issues.

For instance, the author goes into how a URL is parsed and the differences between how different browsers parse URLs. That’s interesting information, but I’m still not sure what type of attacks I should look out due to these issues and how to address them if they do occur. The same treatments are given to HTTP itself, HTML, CSS, JavaScript, and other parts of web application stack.

One of the most frustrating aspects of this book is how browser names are frequently thrown around without version numbers. Saying “Internet Explorer” does something leaves me wondering if that was one of the many issues fixed in Internet Explorer 9 and 10 or not. While it’s fine to leave off version numbers when discussing Chrome, Internet Explorer just has far too many differences to make this useful.

I found the code examples to be incredibly terse, and in some cases missing completely. Case in point, a discussion of the sandbox attribute for <iframe> doesn’t have a single code example showing its proper usage. Certainly property usage is part of ensuring security. Other sections of the book suffer from the same code-terseness to its detriment. A lot of the topics could stand more actual examples.

Which brings me to my overall issue with the book: it reads more like it was written by a researcher for a researcher. This really isn’t a book to help you solidify your web application security. In fact, I’m not sure I picked up any new techniques from reading the book at all. My head is now filled with trivia knowledge about web browsers that I’m unable to practically apply to my work, which is frustrating. The only attempt the author makes at giving actionable advice is on the “checklist” at the end of each chapter. The checklist contains way-too-terse descriptions of how to mitigate certain attacks…but without practical code examples, the bullet points are quite lost.

This book seems mostly targeted at amateur security professional who need a good brain dump on all the various flaws in internet protocols and technologies in order to get their feet wet. It’s definitely not for web developers looking to improve their web application security, making the subtitle, “A Guide to Securing Modern Web Applications”, a complete misnomer. If anything, it’s a guide through current web technologies showing you that the internet is a mess and leaving you to wonder how to fix it.

I really, really wanted to like this book, but unfortunately, I just didn’t find it practical enough to recommend it as guide for most web developers. If you don’t understand security issues at all, then this is probably a good book to pick up, but otherwise, you’ll need to go elsewhere to find practical advice.

Categories: Blogs

Now available: Professional JavaScript, 3rd Edition

Mon, 01/09/2012 - 21:48

Professional JavaScript, 3rd EditionI’m very excited to announce that Professional JavaScript for Web Developers, 3rd Edition is now shipping and available in bookstores. Over six years, the first edition was released and it changed my life in ways I never could have anticipated. It was through this book that I ended up at Yahoo! and was invited to speak at conferences for the first time. The first edition was a labor of love and that love has continued over the years through the second edition and into this one, which took over a year to complete.

Those who know me shouldn’t be surprised that this book is more than just an update. The 3rd edition features five completely new chapters covering the new HTML5 APIs such as history state managements, canvas, offline applications, web workers, and more. Throughout the book, I’ve added references to changes in ECMAScript 5, including how strict mode works and how to use the new object-creation APIs. All of the existing chapters were also updated with the latest browser support information including mobile support (sadly, that will always be a bit out-of-date). A special appendix about ECMAScript Harmony is also included to give you a taste of the future.

I’m also incredibly honored to have a foreword written by Rey Bango. Rey had so many kind words about the 2nd edition that I was thrilled when he agreed to write the foreword for this one. And here it is:

I look back at my career (now 20+ years) and in between coming to the realization that my grey hairs
have really sprouted out, I reflect on the technologies and people that have dramatically affected
my professional life and decisions. If I had to choose one technology, though, that has had the single
biggest positive influence on me, it would be JavaScript. Mind you, I wasn’t always a JavaScript believer.
Like many, I looked at it as a play language relegated to doing rotating banners and sprinkling some
interesting effects on pages. I was a server-side developer and we didn’t play with toy languages, damn
it! But then something happened: Ajax.

I’ll never forget hearing the buzzword “Ajax” all over the place and thinking that it was some very cool,
new and innovative technology. I had to check it out and as I read about it, I was floored when I realized
that the toy language I had so readily dismissed was now the technology that was on the lips of every
professional web developer. And suddenly, my perception changed. As I continued to explore past what
Ajax was, I realized that JavaScript was incredibly powerful and I wanted in on all the goodness it had to
offer. So I embraced it wholeheartedly working to understand the language, joining the jQuery project
team and focusing on client-side development. Life was good.

The deeper I became involved in JavaScript, the more developers I met, some whom to this day I still
see as rockstars and mentors. Nicholas Zakas is one of those developers. I remember reading the second edition of this very book and feeling like, despite all of my years of tinkering, I had learned so much.
And the book felt genuine and thoughtful, as if Nicholas understood that his audience’s experience
level would vary and that he needed to manage the tone accordingly. That really stood out in terms of
technical books. Most authors try to go into the deep-dive techno-babble to impress. This was different
and it immediately became my go-to book and the one I recommended to any developer that wanted
to get a solid understanding of JavaScript. I wanted everyone to feel the same way I felt and realize how
valuable a resource it is.

And then, at a jQuery conference, I had the amazing fortune of actually meeting Nicholas in person.
Here was one of top JavaScript developers in the world working on one of the most important web
properties in the world (Yahoo!) and he was one of the nicest people I had ever met. I admit, I was a bit
starstruck when I met him and the great thing is that he was just this incredibly down-to-earth person
who just wanted to help developers be great. So not only did his book change the way I thought about
JavaScript, Nicholas himself was someone that I wanted to continue to work with and get to know.

When Nicholas asked me to write this foreword, I can’t explain how flattered I was. Here I am being the
opening act for the guru. It’s a testament to how cool of a person he is. Most importantly though, it
gives me an opportunity to share with you why I felt this book is so important. I’ve read many JavaScript
books and there are certainly awesome titles out there. This book, though, offers in my opinion the total
package to make you an incredibly proficient and able JavaScript developer. The smooth and thoughtful
transition from introductory topics such as expressions and variable declarations to advanced topics
such as closures and object-oriented development is what sets it apart from other books that are either
too introductory or expect that you’re already building missile guidance systems with JavaScript. It’s
the “every man’s” book that will help you write code that you’ll be proud of and build websites that will
excite and delight.

Rey Bango
Sr. Technical Evanglist, Microsoft Corporation
jQuery Project Team

I hope that Rey, and all of you, enjoy the 3rd edition just as much as (if not more than) the 2nd edition. The book is available for purchase at Amazon and available for download as an ebook from Wrox.

Categories: Blogs