September 27th, 2010
JavaScript is awesome. I think we can all agree on that (or you’re reading the wrong blog, I suppose). The state of JavaScript documentation? Is not so great. Besides that we need a concerted effort to create better documentation resources and tutorials for programmers new to the language, the top hits for search terms like “JavaScript documentation” or “JavaScript tutorial” are all pointing at spammy SEO-optimized websites.
The JavaScript community is taking action now to solve this—please help us by:
1. Join Promote JS! and put up one of their generated-just-for-you banners up on your blog (best in the sidebar or footer!).
This will help getting valuable links for popular JavaScript-related search terms higher up in Google’s results, so that newbies use the Mozilla documentation and not one of the spammy websites.
2. Write posts! Articles about introductory JavaScript topics are in hot demand! Want some extra hits on your blog? High-quality articles about JavaScript are a good way to go and will help the JavaScript community as a whole!
Today’s newbies are the ones that will take over from us JavaScript gurus in the future! Let’s make it as easy as possible for them to actually learn the language and contribute!
September 21st, 2010
Mobile WebKit based browsers (like those on the iPhone/iPad, Android and Palm webOS) provide really powerful DOM, JavaScript and CSS features. So why not use them in a framework, that’s specifically crafted for these modern mobile devices?
The ultimate goal really is to have a ~2k library that handles most basic dredge work for you in a nice API so you can concentrate on getting stuff done.
Say hello to Zepto.js! (grab the raw code).
Zepto.js supports jQuery-like syntax and API(including chaining) so if you used/seen that before, it’s easy to get started:
$('some CSS selector').append('<li>a new li element</li>')
.live('touchstart', function(){ alert(this.innerHTML) });
$.getJSON('/some/service.json', function(obj){ /* do stuff */ });
Supported are basic operations like html, append, prepend, css, event delegation with live, transform-based animations with anim and ajax calls (get, post, getJSON). And all of this, thanks to WebKit’s rich features, in just 52 lines of code.
It weighs in at just half a kilobyte when minified and gzipped.
Because it’s so small, it’s easy to just stick it in a script
tag directly. Of course, it’s certainly not complete yet, but what’s there already works quite nicely.
I’d love if you let me know about your ideas and feature requests about this! Please tweet @zeptojs or email me. And please go ahead and fork as much as you can on GitHub!
September 1st, 2010
Here’s why you shouldn’t be afraid to use proprietary browser features–especially with stuff that’s more experimental and explorative in nature:
First of all, lots of good things come from once-proprietary features. My favorite example perhaps is the .innerHTML
property of DOM elements, that originated on Internet Explorer. This property is not only easier to use than (just stick any HTML string in), but also much faster than classic DOM manipulation. Now all browsers support this, no big surprise there.
Useful features will be carried over into the “browser mainstream”, and are standardized retroactively.
Almost all features available in modern browsers came from a browser vendor to scratch a specific itch, be it CSS Transitions, client-side storage and databases, extensions to the DOM such as CSS Selectors, 2D drawing surfaces, vector graphics, and so on and so forth. The list continues endlessly.
Early adopting of new features you find useful is a good way to see them broadly implemented.
Of course, some proprietary features won’t make it, or only in vastly different form (Google Gears come to mind). But mind you, it’s not that hard to adopt once things change. Client-side storage is client-side storage after all. APIs might also change (mostly in subtle ways) when they make the transition from proprietary to widely-adopted. If you do use these, you’ll probably have to check from time to time if everything works as expected on the latest browser version (if you’re serious about web development, you’re probably doing that anyway, as new browser versions can break existing, well-working code by introducing new bugs).
Most of the time, there are ways to cope with backwards-compatibility in case you need it, by falling back to other technologies (for example, a lack of WebSockets support might be worked around by using Flash). But, don’t concentrate on the problems, think about all the awesomeness you can create. Solutions to problems can always be found.
Participate on open-source wrapper libraries that are useful to more people than just you.
A great way to cope with this issue is to use libraries that wrap the proprietary APIs and can react to changes to the underlying APIs, or even use different underlying frameworks depending on the browser (for example, Raphaël uses either SVG or VML, transparently), and as a team, you can do so much more.
By developing a popular library you might even be asked to participate in the future development or standardization of the feature and contribute your experience with it.
Always watch out for new developments!
There’s always something going on. It pays to check out beta verions and nightly builds of browsers, and try out some of the wonderful features that are coming up (check out WebGL, for example!).
Don’t be shy, and don’t let yourself be told “But! You can’t do that! It’s against the Web-Gods will! No committee approved that!”
With that said, go ahead and create the next amazing thing!
August 28th, 2010
It seems that Google Chrome is getting all the developer luv these days, but there are some facts that people get confused.
Here’s the deal: Google Chrome uses Apple’s WebKit HTML/CSS rendering engine that powers Safari. Google is not the creator of Webkit, Apple is.* What Chrome does is basically adding two components to the mix, Chrome’s user interface and a replacement JavaScript engine (V8, also used by node.js).
But, there are some parts of the Webkit engine that are either proprietary or are not easily ported to operating systems other than Mac OS X (or iOS). First and foremost among those parts is Safari’s ability to hardware-accelerate the compositing of web pages, including 3D CSS transformations.
These transformations and the added speed of the compositing are not available in Google Chrome (but they are on the way by means of a reimplementation). Neither are they available on Android.
Actually, regardless of what Google states in its design document about GPU accelerated compositing in Chrome (a good read otherwise, but they say hardware-acceleration is only available on Safari for Mac), there are several browsers “in the wild” that do support hardware-accelerated compositing (including 3D CSS), some of them cross-platform:
Here is the blurb from Safari’s features page:
Safari supports hardware acceleration on Mac and PC. With hardware acceleration, Safari can tap into graphics processing units to display computing-intensive graphics and animations, so standards like HTML5 and CSS3 can deliver rich, interactive media smoothly in the browser. Safari on Snow Leopard has improved hardware acceleration for plug-ins.
Tweet-sized conclusion: Hardware acceleration in (non-beta) browsers is only available on Safari (all platforms). There’s no hardware-accceleration (yet) for Chrome (or for Android).
(Other browsers will add support for hard-acceleration too, but it seems that all will be Windows-only for now, with both Firefox 4 and Internet Explorer 9 only supporting that platform.)
*WebKit was orginally created as a fork of the KTHML rendering engine. While the main development effort is done by Apple, patches come in from a lot of sources, and companies like Nokia, Google, RIM and Palm are all contributing, mainly to support their specific branches and platforms. Some of the ideas found in Google Chrome, namely the sandboxing of individual tabs, will make their way into WebKit2, a refactoring and reimplementation that is currently underway.
August 27th, 2010
Yarrrr!! Meet Captain Track, the awesome animated CSS3 pirate!
First, grab a WebKit-based browser, like Safari (preferably, because it supports hardware-acceleration!) or Google Chrome and head over to timebemoney.com and say “yarrrr!” to Captain Track!
Note that on the iPad and iPhone, these animations run with a full, super-smooth 60 frames per second, because of all the hardware-accelerated glory in Mobile Safari.
The best thing is that all this glory will eventually come to all browsers, so don’t get left behind and start using this awesomeness now.
Here’s the whole animation dissected:
Arm & Map
The arm & map image is a transparent 24-bit PNG (note the translucent brownish shadow).
This image is absolutely positioned, and rotated around a point on the left border of the image, a bit down from the center, so it appears that the arm is rotated around the elbow of Captain Track.
@-webkit-keyframes arm {
from { -webkit-transform: rotate(0deg); }
70% { -webkit-transform: rotate(9.7deg); }
to { -webkit-transform: rotate(0deg); }
}
#arm {
position:absolute;
left:360px;
bottom:0;
width:246px;
height:210px;
background:url(img/arm.png);
-webkit-transform-origin:0% 60%;
-webkit-animation:arm 3.9s ease-in-out infinite alternate;
}
As far as special CSS3 animation tricksery goes, the -webkit-transform-origin
property can be used to set the point around which to rotate in the animation.
The animation itself is defined through the use of the @-webkit-keyframes
keyword. This defines an animation by providing several keyframe definitions, that specify which CSS properties should be applied at which points in time during the animation.
The magic happens through the -webkit-animation
property that will take a keyframe definition with several options and then smoothly animate the frames between the keyframes, so that in this example with the rotating arm the rotation is smoothly interpolated from 0 degrees to 9.7 degrees.
The -webkit-animation
property takes several arguments:
- Name of the animation’s keyframes definition (“arm”)
- Duration of the animation (3.9 seconds)
- Easing setting (“ease-in-out”). This is set to ease in and out so it looks better with the “alternate” animation style, where the animation goes back and forth
- Iteration count setting (“infinite”)
- Direction setting (“alternate”) – the animation “rocks back and forth” between 0% and 100% of the keyframes definition
As for the specific values to use–please do experiment! The only way to get good-looking animations is to practice making them and trying to tweak settings all over the place. Of utmost importance is perhaps the duration of the animation, followed by tweaks to the keyframe timing. For example, I used a 70% keyframe so that the durations of moving the arm from 0% to 70% and from 70% to 100% differ slightly, which makes the the back-and-forth rocking more appealing (this works really great together with the “alternate” setting).
While rotating, part of the arm is masked out with a simple positioned DIV with a black background, so that only the map part of the image overlaps into the central white background area.
Clock handles
The clock face is animated by using two copies of the same clock handle image, layered on top of each other. Again, 24-bit transparent PNGs are used:
@-webkit-keyframes pointer {
from { -webkit-transform: rotate(0deg) scaleY(1); }
25% { -webkit-transform: rotate(-90deg) scaleY(0.9); }
50% { -webkit-transform: rotate(-180deg) scaleY(1); }
75% { -webkit-transform: rotate(-270deg) scaleY(0.8); }
to { -webkit-transform: rotate(-360deg) scaleY(1); }
}
#pointer1 {
position:absolute;
left:257px;
top:177px;
width:9px;
height:23px;
background:url(img/pointer1.png);
-webkit-transform-origin:5.5px 18px;
-webkit-animation:pointer 4s linear infinite;
-moz-transform-origin:5.5px 18px;
-moz-transform:rotate(55deg);
-o-transform-origin:5.5px 18px;
-o-transform:rotate(55deg);
}
#pointer2 {
position:absolute;
left:257px;
top:177px;
width:9px;
height:23px;
background:url(img/pointer1.png);
-webkit-transform-origin:5.5px 18px;
-webkit-animation:pointer 9s linear infinite;
-moz-transform-origin:5.5px 18px;
-moz-transform:rotate(-45deg);
-o-transform-origin:5.5px 18px;
-o-transform:rotate(-45deg);
}
The interesting part here is that the clock face isn’t really a circle but an ellipse, so I’m scaling the handles down horizontally a tiny bit with scaleY
so it all fits in there.
The -moz-transform
and -o-transform
properties will make the clock handles show up in nicer default positions on Firefox and Opera (hope those browsers will support CSS3 animations soon!).
Eye (blink!)
The Eye is perhaps the most surprising element of the animation because it’s actually using none of the amazing new features, but rather uses a buzzword of the mid-90s: animated GIFs. Yes, you read right. Animated GIFs are widely supported across browsers and a relatively easy to create with tools like Photoshop.
First, here’s the eye in all it’s glory. Just wait for a bit and you will see it blink very briefly!
To create this in Photoshop, just have two layers, one with the eye open, and one with the eye closed, and then use the “Animation” palette to create a timeline-based animation (the darker areas show when the layers are shown). Save with “Save for Web & Devices…” and you’re all done!
This can be used for more complex animations, like the “map path” that shows up further down on the page.
Yarrr! Don’t forget, Captain Track loves to help you free-lance more efficiently!
Want to know more? Learn even more at my HTML5 Mobile Pro Workshop (next one is on October 5, 2010).
August 20th, 2010
A user enters a web site.
User: ‘Ello, I wish to register a complaint.
(The owner does not respond.)
User: ‘Ello, Miss?
Adobe: What do you mean “miss”?
User: I’m sorry, I have a cold. I wish to make a complaint!
Adobe: We’re closin’ for lunch.
User: Never mind that, my lad. I wish to complain about this Flash what I purchased not half an hour ago from this very boutique.
Adobe: Oh yes, the, uh, the 10.1…What’s,uh…What’s wrong with it?
User: I’ll tell you what’s wrong with it, my lad. It’s dead, that’s what’s wrong with it!
Adobe: No, no, it’s uh,…he’s resting.
User: Look, matey, I know a dead plugin when I see one, and I’m looking at one right now.
Adobe: No no it’s not dead, it’s, it’s restin’! Remarkable plugin, the 10.1, idn’it, ay? Beautiful plumage!
User: The plumage don’t enter into it. It’s stone dead.
Adobe: Nononono, no, no! It’s resting!
User: All right then, if it’s restin’, I’ll wake it up! (shouting at the sandbox) ‘Ello, Mister Polly Flash! I’ve got some lovely fresh hardware acceleration for you if you show…
(owner hits the sandbox)
Adobe: There, it moved!
User: No, it didn’t, that was you hitting the sandbox!
Adobe: I never!!
User: Yes, you did!
Adobe: I never, never did anything…
User: (yelling and hitting the sandbox repeatedly) ‘ELLO POLLY!!!!! Testing! Testing! Testing! Testing! This is your nine o’clock alarm call!
(Takes Flash out of the sandbox and thumps its head on the counter. Throws it up in the air and watches it plummet to the floor.)
User: Now that’s what I call a dead plugin.
Adobe: No, no…..No, ‘e’s stunned!
User: STUNNED?!?
Adobe: Yeah! You stunned him, just as he was wakin’ up! Flash stuns easily, major.
User: Um…now look…now look, mate, I’ve definitely ‘ad enough of this. That plugin is definitely deceased, and when I purchased it not ‘alf an hour ago, you assured me that its total lack of movement was due to it bein’ tired and shagged out following a prolonged squawk.
Adobe: Well, he’s…he’s, ah…probably pining for the fjords.
User: PININ’ for the FJORDS?!?!?!? What kind of talk is that?, look, why did he fall flat on his back the moment I got ‘im home?
Adobe: The 10.1 prefers keepin’ on it’s back! Remarkable plugin, id’nit, squire? Lovely plumage!
User: Look, I took the liberty of examining that plugin when I got it home, and I discovered the only reason that it had been sitting on its perch in the first place was that it had been NAILED there.
(pause)
Adobe: Well, o’course it was nailed there! If I hadn’t nailed that plugin down, it would have nuzzled up to those bars, bent ’em apart with its beak, and VOOM! Feeweeweewee!
User: “VOOM”?!? Mate, this plugin wouldn’t “voom” if you put four million volts through it! It’s bleedin’ demised!
Adobe: No no! It’s pining!
User: It’s not pinin’! It’s passed on! This plugin is no more! It’s has ceased to be! It’s expired and gone to meet its maker! It’s a stiff! Bereft of life, it rests in peace! If you hadn’t nailed it to the perch it’d be pushing up the daisies! It’s metabolic processes are now ‘istory! It’s off the twig! It kicked the bucket, it shuffled off its mortal coil, run down the curtain and joined the bleedin’ choir invisibile!! THIS IS AN EX-PLUGIN!!
(pause)
Adobe: Well, I’d better replace it, then. (he takes a quick peek behind the counter) Sorry squire, I’ve had a look ’round the back of the shop, and uh, we’re right out of plugins.
User: I see. I see, I get the picture.
Adobe: I got Flash lite.
(pause)
User: Pray, does it talk?
Adobe: Nnnnot really.
User: WELL IT’S HARDLY A BLOODY REPLACEMENT, IS IT?!!???!!?
Adobe: N-no, I guess not. (gets ashamed, looks at his feet)
User: Well.
(pause)
Adobe: (quietly) D’you…. d’you want to come back to my place?
User: (looks around) Yeah, all right, sure.
August 17th, 2010
Browsers are single-threaded beasts* but your JavaScript can still cause very expensive reflows and rendering if you’re not careful.
The important thing is to always remember that reflowing and rendering HTML is the single most expensive operation browsers do. If your page feels sluggish it’s most likely a problem with rendering. While the easiest way to optimize is to get rid of as many nodes as you can, and trying to have simpler CSS rules, sometimes JavaScript is the culprit.
You ask, what’s so expensive about reflowing? Have a look at this video—it shows how a browser renders a popular page (Wikipedia), and though that page is actually quite simple, you see that a lot is going on:
Now, if you change the CSS style of some element on the page with JavaScript, the browser doesn’t immediately apply this change, but rather waits for either of two things happening:
1. The execution of your JavaScript ends (for example, your event handler is done) or 2. You query something that triggers a reflow. Obviously you can’t do much about the reflows that happen when your JavaScript execution ends, so we’ll look at the second, and sometimes overlooked trigger.
someElement.style.fontSize = "14px";
if(someElement.offsetHeight>100){ /* ... */ }
someElement.style.paddingLeft = "20px";
if(someElement.offsetWidth>100){ /* ... */ }
In this code, the calls to offsetHeight
and offsetWidth
cause expensive reflows. You could write this as:
someElement.style.fontSize = "14px";
someElement.style.paddingLeft = "20px";
if(someElement.offsetHeight>100){ /* ... */ }
if(someElement.offsetWidth>100){ /* ... */ }
And you will trigger only one reflow, which probably doubles the execution speed of this block of code. So what happens here?
Browsers batch apply CSS changes. They wait until either your code exits or they need to recalculate the layout (reflow). In case of the second example, the call to offsetHeight
triggers the reflow, but in the next line, because there haven’t been any style changes, no new reflow is necessary—and querying offsetWidth
happens pretty much instantly.
Of course, style changes are not the only thing the cause reflows, obviously adding or removing stuff from the DOM does too. You can often achieve better performance by batching those operations in one big chunk at the end of the block of code you’re executing.
*some browsers like Chrome isolate tabs, but in the same tab, it’s still a single execution thread
August 16th, 2010
One of the historically more popular optimizations is loop unrolling. Instead of a for
or while
loop, just “unroll” the loop:
// classic for loop
for (var i = 0; i < 100; i++) a++;
// unrolled loop
a++;
a++;
a++;
a++;
a++;
a++;
a++;
a++;
// etc. etc. etc.
// unrolled loop (in 1 statement)
a++, a++, a++, a++, a++, a++, a++, /* ...etc... */ a++;
Try the test on jsPerf yourself.
Note that due to the lack of a goto
statement, loop unrolling has a bit of a limited appeal in JavaScript, but it’s still a useful technique in some cases, though you can use return
to return early from an unrolled loop (if the overhead of a function call is not too much).
…and here are results of the Austrian jury (on a 3.06GHz Core Duo):
Safari 5 (Mac): 3,228,374 ops/sec for the unrolled loop (22% faster than for loop)
Chrome 5 (Mac): 2,688,656 ops/sec for the unrolled loop (31% faster than for loop)
Firefox 3.6 (Mac): 42,890 ops/sec for the unrolled loop (for loop is 30x faster!)
IE 6 (XP): 60,235 ops/sec for the unrolled loop (1 statement version) (5x faster than for loop)
IE 8 (Win 7): 124,878 ops/sec for the unrolled loop (1 statement version) (5x faster than for loop)
The verdict is, that on modern JavaScript engines, unrolled loops give you a modest performance increase, and on Internet Explorer you can expect a major boost. On Firefox, normal for or while loops are better.
Mind you, NEVER OPTIMIZE PREMATURELY. Only optimize if you have to—but if so, this technique is actually quite useful, especially if you deal with iterative algorithms like in visualizations (something I sometimes have to deal with).
Find this technique and more in JavaScript Performance Rocks!, my ebook on JavaScript Peformance.
August 14th, 2010
When working on performance optimization fine-tuning, it’s great to have some help in the form of benchmarks of various strategies to achieve your code goals. Mathias Bynens has come up with a slick site to do just that, meet jsPerf!
First take a look at the various test cases that are already in there.
Here’s a test case I wrote out of interest comparing various strategies to test if a given property exists in an object (view it on jsPerf):
// for a given object
var obj = {
a: 1
};
// test which is fastest
// variant 1: in operator
if ('a' in obj) {};
if ('b' in obj) {};
// variant 2: boolean value (might yield wrong results with certain values!)
if (obj.a) {};
if (obj.b) {};
// variant 3: hasOwnProperty
if (obj.hasOwnProperty('a')) {};
if (obj.hasOwnProperty('b')) {};
Perhaps not surprisingly, variant 2 is fastest (at least on Safari 5.0.1). Note that this particular way to test for properties might yield wrong results (a property with a value of false wouldn’t be detected, for example).
JsPerf is double-awesome! While it is of course great to test the performance of code, most importantly your findings are shared with other developers (and they can suggest new solutions!). Be sure to browse through the test cases—a great way to learn about alternative ways to code JavaScript.
August 12th, 2010
I use WordPress, and I use Twitter, so here’s how to add the awesome new offical Tweet Button to your WordPress blog:
Inside your Main Index template (index.php), add the first line inside the posts loops (above/below/whereever you want it!). You can add additional parameters, and don’t forget to change the data-via
attribute!
Then, add the script
line at the end of your page (for example in the footer!)—you only need to include the script
tag once!
That’s it, cute little Tweet Buttons like the one right below this article (try it out!).