CSS media queries in JavaScript, Part 2
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 quirksRecall 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 listenersMy 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:
- When the media query initially becomes valid. So in the previous example, when the screen becomes 600 pixels wide or less.
- 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.
ConclusionCSS 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- CSS media queries in JavaScript, Part 1 by me
- Rob Flaherty’s tweet
- matchMedia() MediaQueryList not updating
- matchMedia() listeners lost
- matchMedia polyfill by Paul Irish
- matchMedia polyfill by Paul Hayes
- YUI 3 Gallery Media module by me
Book review: The Tangled Web
I’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.
Now available: Professional JavaScript, 3rd Edition
I’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.
CSS Lint v0.9.2 now available
A new version of CSS Lint is now available both on csslint.net and through npm for NodeJS. Version 0.9.2 focused on improving validation support (full support is planned for v1.0.0) and stability. As part of that, 0.9.1 and 0.9.2 were quickly rolled out after 0.9.0 to address some flaws in the validation logic.
Other changes for this release:
- Dino Chiesa submitted a Windows Script Host (WSH) CLI.
- There were a couple of parser compatibility bugs that were fixed.
- Various rules regarding vendor prefixed properties were updated to reflect Internet Explorer 10.
- New Rule: The fallback-colors rule was suggested by Dustin Cass and warns when a CSS3 color is used without a CSS2 fallback (rule documentation)
- New Rule: The duplicate-background-images was created by Hans-Peter Buniat and warns when a background image is used more than once ((rule documentation)
Thanks once again to the CSS Lint community for continuing to file bugs and make feature requests. We’re rapidly approaching and very stable v1.0.0 release due to your participation and feedback. Keep it coming!
Proposal: Scripting detection using CSS media queries
I’ve been doing a lot of thinking about CSS media queries lately. I’m a big fan of media queries, as I think they bring a sense of sanity to feature detection. That’s a big reason why I was investigating CSS media queries in JavaScript[1] and will continue to do so. I think we’re only scraping the surface of what can be done with media queries on the web. As part of my pondering over the holiday break, I scribbled down a few notes of ways I’d like to use media queries. I just proposed the first one to the CSS working group.
The ProposalYesterday, I sent an email[2] to the CSS working group with my proposal. The basic idea is to allow you to determine if scripting is enabled in the browser using a media query. The proposal can be summed up with a few examples:
@media screen and (script) {
/* styles to apply only when scripting is enabled */
}
@media screen and not (script) {
/* styles to apply only when scripting is disabled */
}
So, just like you currently use device-width, orientation, and so on to detect features of the device, you could also use script in the same way.
RationaleIn the realm of progressive enhancement, you don’t want to show page elements that can’t be used. This may be as simple as an arrow next to link indicating a dropdown menu is available. If JavaScript is disabled, you want the link to act like a regular link and not confuse people by having an arrow that means nothing. So you want to apply the style that shows the arrow only if JavaScript is enabled.
The most common approach to this problem is to add a class to the <html> element via JavaScript. So somewhere on the page, you put:
<script> document.documentElement.className += " js-enabled"; </script>
This adds the class js-enabled via JavaScript. Of course, this only gets executed when JavaScript is enabled. You can then define CSS rules such as:
.arrow {
/* empty */
}
.js-enabled .arrow {
background: url(image.png) no-repeat;
}
It’s a bit of a hack, but this basic technique is in use by large sites such Twitter and the Yahoo! homepage, as well as being done automatically by Modernizr and YUI.
While this technique works, it has two downsides. First, you need to include that little JavaScript snippet (or a supporting library) to ensure the class ends up being added. Second, it alters the specificity of your rules, which can adversely affects the cascade.
ClarificationsI’m a big believer that common patterns should be codified and standardized so that the development community can move on to more interesting challenges[3]. As such, it seems that the community has spoken that we want to define different styles when JavaScript is enabled, and CSS media queries seem like the right approach.
The CSS Media Queries specification[4] states:
A media query consists of a media type and zero or more expressions that check for the conditions of particular media features. Among the media features that can be used in media queries are ‘width’, ‘height’, and ‘color’. By using media queries, presentations can be tailored to a specific range of output devices without changing the content itself.
The term media feature is key. When I was first debating myself over whether scripting support is appropriate for a CSS media query, I went and read the specification. Script support is just as much a media feature as color depth and orientation. It’s a capability of that particular device at the time your page is loaded. Given that, I felt comfortable proposing the inclusion of script as another media feature to test.
To be clear, my proposal’s goal is to easily indicate whether or not scripting is enabled in a browser. Think of it as a relative of the <noscript> element. So instead of doing something like this:
<noscript>
<style>
.foo {
color: red;
}
</style>
</noscript>
You could do this:
@media screen and not (script) {
.foo {
color: red;
}
}
Of course, by omitting not, you could also apply changes when scripting is enabled.
Some non-goals of this proposal are:
- Replacing JavaScript feature detection. You will still be checking, in JavaScript, if certain features are available. In short: I’m not looking to propose implementing media query features for all possible JavaScript APIs. If you want that, you should use Modernizr.
- Enabling JavaScript in CSS. I have no desire to have JavaScript in CSS in any way, shape, or form.
- Be JavaScript-centric in detection. Actually, the intent is to indicate if scripting is enabled, not just JavaScript. It would probably be easy to extend the syntax, such as (script:"text/javascript"), but I’m not sure that’s necessary at this point.
And as I always like to remind people: no one would force you to use this feature if it’s implemented. If you don’t like it, you can always leave it to those who do.
ConclusionI think CSS media queries are one of the best things to happen to the web, and I look forward to using them in new and interesting ways. Adding feature detection for scripting seems like a logic step towards standardizing a fairly common practice. The good news is that Florian Rivoal, one of the editors of the CSS Media Queries specification has agreed[5] to write it up as a proposal for inclusion in CSS Level 4 Media Queries. I hope the proposal is able to move forward quickly.
References- CSS media queries in JavaScript, Part 1 by me
- Proposal: Detecting JavaScript with media queries by me
- When web standards fail us by me
- CSS Level 3 Media Queries
- Re: Proposal: Detecting JavaScript with media queries by Florian Rivoal
CSS media queries in JavaScript, Part 1
Early in 2011, I was working on a project where I was doing some feature detection in JavaScript. Some tinkering led to the thought that using a CSS media query would work much better and so I spent some time coming up with a function to use CSS media queries in JavaScript. My thought process was simple: if I’m only applying certain CSS based on a media query, I also only want to run certain JavaScript based on a media query. The result was the following function, which I first published as a Gist[1] last March:
var isMedia = (function(){
var div;
return function(query){
//if the <div> doesn't exist, create it and make sure it's hidden
if (!div){
div = document.createElement("div");
div.id = "ncz1";
div.style.cssText = "position:absolute;top:-1000px";
document.body.insertBefore(div, document.body.firstChild);
}
div.innerHTML = "_<style media=\"" + query + "\"> #ncz1 { width: 1px; }</style>";
div.removeChild(div.firstChild);
return div.offsetWidth == 1;
};
})();
The idea behind this function is pretty simple. I create a <style> node with a media attribute equal to the one I’m testing. Inside, there’s a CSS rule applied to a <div> and all I have to do is check to see if the style has been applied. I wanted to avoid browser detection, so instead of using currentStyle and getComputedStyle(), I decided to just change the width of an element and check it using offsetWidth.
Very quickly, I had a version of this function that worked in almost all browsers. The exceptions, as you may have guessed, were Internet Explorer 6 and 7. In those browsers, the <style> element is considered a NoScope element[2]. NoScope elements were a horrid exception to what happens when HTML is injected into a page using innerHTML or any other means. All NoScope elements are effectively dropped if they are the first element added as an HTML string. In order to use a NoScope element, you must be sure that it’s not the first part of an HTML string. Thus, I put the underscore in before the <style> element and then remove it – tricking Internet Explorer 6 and 7 into applying the element as it should. Other browsers don’t have this NoScope element issue, but using this technique doesn’t negatively effect them (as I said before, I was trying to avoid browser detection).
In the end, you can use the function like this:
if (isMedia("screen and (max-width:800px)"){
//do something for the screen
}
if (isMedia("all and (orientation:portrait)")){
//react to portrait mode
}
The isMedia() worked great in all browsers I tested (back to Internet Explorer 6) in that it accurately detects whether the browser thinks the media query is valid. So passing in an unsupported query to any browser always returns false. Internet Explorer 6, for instance, returns true if you use “screen”, but anything more complex and it returns false. I thought this was acceptable because any CSS in other media queries wouldn’t be applied in that browser anyway.
CSSOM ViewThe CSS Object Model (CSSOM) Views specification[3] adds native support for CSS media queries in JavaScript by adding a method, matchMedia(), to the window object. You pass in a CSS media query and receive back a MediaQueryList object. The object contains two properties: matches, which is a boolean value indicating if the CSS media query matches the current view state, and media, which is the same string that was passed in. For example:
var match = window.matchMedia("screen and (max-width:800px)");
console.log(match.media); //"screen and (max-width:800px)"
console.log(match.matches); //true or false
So far, this API doesn’t provide much more than my Gist. You may be wondering, though, why does matchMedia() return an object? After all, if the media doesn’t match, of what use is it? The answer is in two methods: addListener() and removeListener().
These two methods allow you to interact with view state changes based on CSS media queries. For instance, maybe you want to be alerted when a tablet is switched to portrait mode. You could do something like this:
var match = window.matchMedia("(orientation:portrait)");
match.addListener(function(match){
if (match.media == "(orientation:portrait)") {
//do something
}
});
This code adds a listener for a media query. When the query becomes true for the current view state, the listener is executed and the corresponding MediaQueryList object is passed in. In this way, you can have your JavaScript be just as responsive as your layout without polling. So unlike my Gist, this API allows you to monitor the changing view state and adapt the interface behavior accordingly.
The matchMedia() method is available in Chrome, Safari 5.1+, Firefox 9+, and Safari for iOS 5+. These represent the browsers that I have access to and can verify. Internet Explorer and Opera still don’t support matchMedia() as of their latest versions.
Note: The WebKit implementation is a bit buggy, so matches doesn’t update after the MediaQueryList object is created and query listeners don’t fire. Hopefully this will be fixed soon.
ConclusionCSS media queries bring a simple feature detection syntax to both CSS and JavaScript. I expect that media queries will become a big part of JavaScript coding in the future, alerting developers as to when significant interface changes occur. There is no reason that the behavior of a web application shouldn’t be just as responsive as the layout, and CSS media queries give us that power today.
References- A function for detecting if the browser is in a given media mode
- MSDN: innerHTML Property
- CSS Object Model View
- matchMedia() MediaQueryList is not updating
Introducing Props2Js
One of my principles of Maintainable JavaScript[1] is to separate your configuration data from your application logic. Configuration data is hardcoded information that your JavaScript uses to work properly. This could be anything such as a URL or a UI string. For example:
function validate(value) {
if (!value) {
alert("Invalid value");
location.href = "/errors/invalid.php";
}
}
function toggleSelected(element) {
if (hasClass(element, "selected")) {
removeClass(element, "selected");
} else {
addClass(element, "selected");
}
}
There are three pieces of configuration data in this code. The first is the string, “Invalid value”, which is displayed to the user. As a UI string, there’s a high chance that it will change frequently. The second is the URL “/errors/invalid.php”. URLs tend to change as development progresses due to architectural decisions. The third is the CSS class name “selected”. This class name is used three times, meaning that a class name change requires changes in three different places, increasing the likelihood that one will be missed.
Configuration data is best extracted from the core application logic, such as:
//Configuration data externalized
var config = {
MSG_INVALID_VALUE: "Invalid value",
URL_INVALID: "/errors/invalid.php",
CSS_SELECTED: "selected"
};
function validate(value) {
if (!value) {
alert(config.MSG_INVALID_VALUE);
location.href = config.URL_INVALID;
}
}
function toggleSelected(element) {
if (hasClass(element, config.CSS_SELECTED)) {
removeClass(element, config.CSS_SELECTED);
} else {
addClass(element, config.CSS_SELECTED);
}
}
This example stores all of the configuration data in the config object. Each property of config holds a single piece of data, and each property name has a prefix indicating the type of data (MSG for a UI message, URL for a URL, and CSS for a class name). The naming convention is, of course, a matter of preference. The important part of this code is that all of the configuration data has been removed from the functions, replaced with placeholders from the config object.
Externalizing the configuration data means that anyone can go in and make a change without fear of introducing an error in the application logic. It also means that the entire config object can be moved into its own file, so edits are made far away from the code that uses the data.
Having an external object managing your configuration data is a good start, but I’m not a fan of storing configuration data directly in JavaScript code. Because such data changes frequently, I prefer to keep it in a simpler file format – one that’s free from worries about missing a semicolon or comma. And that’s when I turned to the Java properties file[2].
Java properties files are incredibly simple. One name-value pair per line and comments begin with a #. It’s really hard to mess up this format. Here’s what the previous example’s configuration data looks like in a Java properties file:
# UI Strings MSG_INVALID_VALUE = Invalid value # URLs URL_INVALID = /errors/invalid.php # CSS Classes CSS_SELECTED = selected
Even though I had my configuration data in a Java properties file, I had no easy way of making this data available to JavaScript.
This is why I created Props2Js[3], a simple tool that does just one thing: reads a Java properties file and outputs it in a format that JavaScript can use. Actually, it’s capable of outputting the data into three formats that JavaScript can use: JSON, JSONP, and regular JavaScript.
java -jar props2js-0.1.0.jar --to jsonp --name myfunc --output result.js source.properties
The --to option specifies the output format, either “js”, “json”, or “jsonp”. The --name option specifies either the variable name (for “js”) or the function name (for “jsonp”); this option is ignored for “json”. The --output option specifies the file to write the data into. So this line takes the Java properties file named source.properties and outputs JSONP with a callback function of myfunc to a file named result.js.
Props2Js outputs the properties file mentioned above into JSON format:
{"MSG_INVALID_VALUE":"Invalid value","URL_INVALID":"/errors/invalid.php",
"CSS_SELECTED":"selected"}
Here’s the JSONP output:
myfunc({"MSG_INVALID_VALUE":"Invalid value","URL_INVALID":"/errors/invalid.php",
"CSS_SELECTED":"selected"});
And here’s the plain JavaScript option with --name config:
var config={"MSG_INVALID_VALUE":"Invalid value","URL_INVALID":"/errors/invalid.php",
"CSS_SELECTED":"selected"};
Props2Js is also smart enough to know that you’re assigning to an object property if you include a dot in in the --name option. In that case, it omits the var.
Props2Js is available under an MIT License and is hosted at GitHub[3].
References- Maintainable JavaScript 2011 by Nicholas C. Zakas
- .properties by Wikipedia
- Props2Js
Book review: HTML & CSS
It had been a while since I’d read a book that didn’t have to do with JavaScript or something very computer-sciency, so when I was asked to review HTML & CSS: Design and Build Websites by Jon Duckett, I was interested to see how these books have changed. I learned HTML back in 1996, and honestly haven’t picked up another book on the subject since that time.
My first impression of the book is that it’s beautiful. The text is large and the pages are colorful, making it very easy to thumb through when in a hurry. When I wasn’t in a hurry and sat down to read it, I found that the book almost told the entire story through pictures. The words are there and technically correct, but it’s the visuals in the book that really communicate information to the reader.
I admired Duckett’s approach to this book. He completely dispels with the buzzwords that glitter so many books these days. There’s mention of HTML5 and CSS3, for sure, but it’s done in such a way that it doesn’t seem gimmicky or hyped. The title of the book itself is evidence of this. Duckett clearly doesn’t want you thinking about HTML 4 vs. HTML5 or CSS 2 vs. CSS3. Instead, he wants you to understand the concepts that link together web technology and good design. Some of that is done with HTML 4 and CSS 2 while some is done with HTML5 and CSS3.
This book is really targeted at beginners without a technical background, and it does an exceptional job in serving this audience. The approach is perhaps the gentlest introduction to the concept of web programming that I’ve ever encountered. So gentle, in fact, I think that almost anyone could pick up this book and start to make a simple web page relatively quickly. It takes you right from creating your HTML file with a text editor, through learning HTML and CSS, all the way to deploying your file and adding Google Analytics.
Sprinkled throughout the book are useful tidbits about typography, contrast, design concepts, and even how multimedia plugins such as Flash work in conjunction with a web page. The very visual nature of the book makes picking up these concepts easy, as every piece of code is accompanied with a diagram, figure, or screenshot showing the result.
If you’re an experienced web developer, you’ll probably want to pass on this book since it will be far too basic. However, if you’re looking for a good book to introduce web development to an inexperienced web developer, or even someone who has no experience, then this book is a great place to start.
Timer resolution in browsers
Timer resolution refers to how frequently a clock is updated. For most of their history, web browsers used the default system timer for functionality such as setTimeout() and setInterval(). This meant browsers could only schedule code to run as frequently as the system timer would fire. Internet Explorer also used the system clock for seeding values in Date object, so dates could only be created with differences equivalent to the timer resolution.
A brief historyWindows machines have a timer resolution of 10-15.6ms by default (most at 15.6ms), which meant that browsers using the system timer were stuck to this resolution. Of course, 10-15.6ms is a lifetime when you have a CPU running as fast as today’s processors do. It probably doesn’t surprise you that Internet Explorer through version 8 exclusively used system timers and so led to John Resig writing about how timer resolution affects benchmarks[1]. On OS X, browser timers were much more accurate than on Windows.
Until recently, the other browsers on Window also used the system timer and so were all stuck at 15.6ms timer resolution. This was true for Firefox, Safari, and Opera. Chrome may have been the first Windows browser to switch to a higher-resolution timer[2], and their experiments led to some interesting results.
The original idea was for Chrome to have sub-millisecond timers, but this was abandoned in favor of a one millisecond timer resolution. They decided to use the Windows multimedia timer API, which allows you to specify a timer with a resolution as small a one millisecond and use that instead of the system timer. This is the same timer used by plugins such as Flash and Quicktime.
Chrome 1.0 beta had a one millisecond timer resolution. That seemed okay, but then the team started having bug reports. It turns out that timers cause the CPU to spin, and when the CPU is spinning, more power is being consumed because it can’t go into sleep (low power) mode.[3] That caused Chrome to push its timer resolution to 4ms.
The 4ms delay was codified in HTML5 as part of the Timer section[4], where it states that the minimum resolution for setTimeout() should be 4ms. The minimum resolution for setInterval() is specified as 10ms.
Timer resolution todayInternet Explorer 9, Firefox 5, Safari 5.1, and Opera 11 all feature a 4ms timer resolution, following Chrome’s lead. Prior to that, Firefox 4 and earlier and Safari 5 and earlier had a timer resolution of 10ms (apparently, this was hardcoded in WebKit). Mobile Safari on iOS 5 also has a 4ms timer resolution. Silk on the Kindle Fire has a 10ms timer resolution, potentially indicating it was built off an older version of WebKit. However, just because today’s browsers have a timer resolution of 4ms, it doesn’t mean that’s the resolution you’ll be getting.
Most browsers also do some sort of timer throttling based on different conditions. The intent is to save battery at opportune times – times when, theoretically, you either won’t notice the difference or would gladly trade for improved battery life on a laptop or mobile device. Here are some circumstances where timer resolution changes:
- Chrome and Internet Explorer 9+ switch back to the system timer when a laptop is running on battery power. When plugged in, the browser switches back to the 4ms timer resolution.
- Firefox 5+, Chrome 11+, and Internet Explorer 10+ change timer resolution in inactive tabs to 1000 milliseconds.[5]
- Mobile Safari on iOS5 and Silk on the Kindle Fire freeze the timer completely when you switch to a different app. The timer restarts when you switch back to the browser.
Browsers will likely continue to make adjustments to timer resolution as it pertains to power consumption on battery-powered devices. The HTML5 spec leaves room for browser vendors to make such changes.
Conclusion
There has been a silent timer resolution evolution going on as browsers have developed over the past few years. Timer resolution isn’t one of those topics that gets discussed frequently, but if you’re using setTimeout() and setInterval(), it pays to have a deeper understanding of the functionality. We’re getting closer to the point of having per-millisecond control of the browser. When someone figures out how to manage timers without CPU interrupts, we’re likely to see timer resolution drop again. Until then, keep 4ms in mind, but remember that you still won’t always get that.
Update (15 Dec 2011): Updated with information about Date object.
References- Accuracy of JavaScript Time by John Resig
- Chrome: Cranking up the clock by Mike Belshe
- CPU Power Utilization on Intel® Architectures by Karthik Krishnan
- Timers in HTML5
- Clamp setTimeout/setInterval to something higher than 10ms in inactive tabs
- Timer Resolution Test by Ryan Grove