JavaScript Best Practices

@ikeif recently tweeted a request for some JavaScript best practices. Rather than simply reply to him, I thought I’d post them here and beat him to the blog-post-punch. I’m not going to expand much on any of these, although any discussion that arises will likely spawn its own post. These are in no particular order and are really nothing more than a brain-dump. I’ve numbered them for easy reference in the comments.

  1. Avoid global variables. When you must use a ‘global’, use your own namespace.
  2. Avoid cluttering the global namespace with functions. Assign your ‘global’ functions to a single namespace (see above).
  3. Discover the JavaScript framework/library that speaks to you and stick with it. Don’t load jQuery and Prototype on the same project. Yes, I know you can run many libraries in noConflict mode now, but think of the additional overhead you are placing on your users. All for some snazzy plugin? Port it!
  4. Use JSLint. I assume you validate your HTML? Use JSLint to validate your JavaScript. If your code is JSLint safe, you can avoid a few browser idiosyncrasies (hasOwnProperty() anyone?). As a bonus, JS-Lint safe code is also JSMin safe so you can minify your scripts without worrying if the minification will affect functionality.
  5. A side effect of using the JSLint validator in Aptana, my IDE of choice for front-end development, is my use of JSLint’s special `/*global */` comment. JSLint will flag any global variables unless they are explicitly listed as dependencies in this comment. This means at the top of all of my scripts, one can easily spot any required dependencies (specific MooTools modules for instance).
  6. Use feature detection not browser sniffing.
  7. Write unobtrusive scripts instead of inline event handlers.
  8. Keep your styles in your CSS! Although most libraries make it easy to manipulate element styles, it’s much better to keep your styling where it belongs- in your CSS. Mixing the two violates separation of concerns and makes your code less maintainable. Instead, add and remove classes as necessary in your scripts.
  9. Make sure your UI elements support proper interaction when JS is disabled. If a link opens a lightbox, set the href to point to the lightbox content so no-JS users can still access the content. Links with `href=”#”` kill kittens.
  10. Any UI elements that *only* support JavaScript interaction (and think carefully about this) should be created by JavaScript. Don’t litter your HTML will dummy elements that are only there for JS events. Your script should create and inject them.
  11. If at all possible, don’t modify libraries or plugins directly. This makes future upgrades a nightmare. It is much better to extend the plugin/library without modifying the original.
  12. Your .js files should be served with HTTP `Content-Type: application/javascript`. BUT the `type` attribute in your HTML `script` element must be `text/javascript` or else Internet Explorer will crap itself.
  13. Language=”JavaScript” was deprecated, like, a zillion years ago. Stop using it.
  14. Don’t pre-optimize your code by using fancy looping structures. It is much better to have readable code. Once your app is running, then you can go back and profile it to eliminate bottlenecks. There is no point in pre-optimizing your scripts when the overhead of your background image is 10x slower.
  15. Don’t return false from an event listener when all you really want is event.preventDefault(). Maybe someone else wants to listen for that click event, too, mmmkay?
  16. Stop using document.write

Okay, that’s my list. I may add more later. Disagree with any of these? What best practices or anti-patterns do you have?

CSS Reset

Why start with a blank slate? After years of web development and hundreds of sites, starting from scratch on each project really turns into a buzz kill. Nobody wants to spend time rehashing the same issues from site to site. So many of us have turned to CSS Resets. As we all know, CSS Resets are designed to fix cross-browser inconsistencies by rebasing all or most default styles to a common state. I’ve always had a problem with these resets. Many of the styles in these resets are never used (how often do you use q, ins, del, and table anymore, really?). Other styles are completely overridden. I would wager that by the end of a long project, one could probably remove the CSS Reset without affecting the design (save maybe the margin/padding rules). Jonathan Snook feels the same way. For these reasons, I’ve generally used the universal margin/padding reset:

* {margin:0; padding:0;}

There is quite a lot of contention around the subject both for and against as well as the reasoned centrist.

So, rather than continue to rail against their futility, performance penalty, or outright boorishness, I thought I’d actually use a CSS reset a few times and report my findings.

Decision Time!

CSS Reset by Eric Meyer or YUI Reset? Well, after watching this video (you should, too), my decision was firmly in the Meyer camp.

First Reactions

Who uses Firebug? Okay, sorry, who doesn’t use Firebug? If you don’t, you should, and if you do, you likely won’t like Meyer’s reset without it first being modified. Ladies and gentlemen of the jury, Exhibit A:

reset_thumb

Due to the first rule in the reset, the font-size property is applied to (nearly) every element. However, font-size is also an inherited property. Which means nearly every element inherits its value from its parent, while simultaneously being reset itself by the same rule it inherited! The first rule of nearly every stylesheet of mine usually includes a set of font properties (font-family, font-size, and line-height). With these properties already being set, there is no reason to have them in my CSS Reset, so let’s remove the offending rule and relieve some of Firebug pressure.

reset3_thumb

Whew, that’s better.

Don’t Lose Your Focus!

The most offending rule in Eric’s reset is his outline rule:

:focus { outline: none; }

Sure, he adds a comment to remind users to be sure to specify proper outlines for keyboard users. But you and I can both count on one hand the number of times a proper outline is reinstated for the :focus pseudo-class. Besides, I subscribe to the belief that frameworks and tools should make it easy to fall into the pit of success rather than making it harder to do things the right way. Luckily, Patrick H. Lauke has outlined (sorry, I couldn’t help it) a method to remove the outline during its less-useful moments, while retaining the outline as necessary for keyboard navigation. In brief, simply:

a:hover, a:active { outline: none; }

This will hide the ugly outline during the click action on a link as well as during the time the page loads (so long as the user doesn’t move their mouse). I think this fits nicely in the 80/20 category.

And Now?

So where does that leave us? I’m not sure. I’m still not entirely convinced of the utility of a CSS Reset. However, I believe my two minor modifications do bring Meyer’s Reset a bit further into the ‘’useful’ category without being a pain or downright harmful. My version of the reset is hosted at GitHub, so if you don’t like it, go fork it!

http://github.com/jasonkarns/css-reset