Developer of location based application, a frontend specialist for Google Maps API and page optimization.

// via Google Reader

Inhalt abgleichen
Aktualisiert: vor 1 Minute 15 Sekunden

// Building a custom HTML5 video player with CSS3 and jQuery - Opera Developer Community

Introduction

The HTML5 <video> element is already supported by most modern browsers, and even IE has support announced for version 9. There are many advantages of having video embedded natively in the browser (covered in the article Introduction to HTML5 video by Bruce Lawson), so many developers are trying to use it as soon as possible. There are a couple of barriers to this that remain, most notably the problem of which codecs are supported in each browser, with a disagreement between Opera/Firefox and IE/Safari. That might not be a problem for much longer though, with Google recently releasing the VP8 codec, and the WebM project coming into existence. Opera, Firefox, Chrome and IE9 all have support in final builds, developer builds, or at least support announced for this format, and Flash will be able to play VP8. This means that we will soon be able to create a single version of the video that will play in the <video> element in most browsers, and the Flash Player in those that don't support WebM natively.

The other major barrier to consider is building up a custom HTML5 <video> player — this is where a Flash-only solution currently has an advantage, with the powerful Flash IDE providing an easy interface with which to create a customized video player component. IF we want to write a customised player for the HTML5 <video> element we need to handcode all the HTML5, CSS3, JavaScript, and any other open standards we want to use to build a player!

And this is where this article comes in. This is the first of a series in which we will look at building up an easily customizable HTML5 <video> player, including packaging it as a simple jQuery plugin, choosing control types and outputting custom CSS for your own situation. In this article we will look at:

  1. Video controls
  2. Basic markup for controls
  3. Packaging the player as a jQuery plugin
  4. Look and Feel
  5. Themeing the player

We'll use jQuery for easier DOM manipulation, and jQuery UI for the slider controls used for seeking and changing the volume level. To build a scalable solution, we'll wrap everything up in a jQuery plugin.

Video controls

As professional web designers, we want to create a video player that looks consistent across browsers. Each browser however provides its own different look and feel for the player, from the minimal approach of Firefox and Chrome, to the more shiny controls of Opera and Safari (see Figure 1 for the controls in each browser). If we want our controls to look the same across all browsers, and integrate with our own design, we'll have to create our own controls from scratch. This is not as hard as it seems.

Native browser video controls

Figure 1: Native browser video controls across different browsers

All media elements in HTML5 support the media elements API, which we can access using JavaScript and use to easily wire up functions such as play, pause, etc. to any buttons we create. Because the native video player plays nicely with other open web technologies, we can create our controls using HTML, CSS, SVG or whatever else we like.

Basic markup for controls

First, we'll need to create the actual markup for the video controls. We'll need a Play/Pause button, a seek bar, a timer and a volume button and slider. We'll insert the markup for the controls after the <video> element, and wrap them up in another element.

<div class="ghinda-video-controls"> <a class="ghinda-video-play" title="Play/Pause"></a> <div class="ghinda-video-seek"></div> <div class="ghinda-video-timer">00:00</div> <div class="ghinda-volume-box"> <div class="ghinda-volume-slider"></div> <a class="ghinda-volume-button" title="Mute/Unmute"></a> </div> </div>

We've used classes instead of IDs for all elements, to be able to use the same code for multiple video players on the same page.

Packaging the player as a jQuery plugin

After creating the markup we'll have to tie our elements to the media elements API, in order to control the video's behavior. As noted before, we'll package the player as a jQuery plugin, which will also aid reuse on multiple elements.

AUTHOR'S NOTE: I'm going to assume you are familiar with the basic anatomy of a jQuery plugin, and JavaScript, so I'm only briefly going to explain the script. If you need more information on these subjects, consult Craig Buckler's How to develop a jQuery plugin tutorial, and the JavaScript section of the Opera web standards curriculum.

$.fn.gVideo = function(options) { // build main options before element iteration var defaults = { theme: 'simpledark', childtheme: '' }; var options = $.extend(defaults, options); // iterate and reformat each matched element return this.each(function() { var $gVideo = $(this); //create html structure //main wrapper var $video_wrap = $('<div></div>').addClass('ghinda-video-player').addClass(options.theme).addClass(options.childtheme); //controls wraper var $video_controls = $('<div class="ghinda-video-controls"><a class="ghinda-video-play" title="Play/Pause"></a><div class="ghinda-video-seek"></div><div class="ghinda-video-timer">00:00</div><div class="ghinda-volume-box"><div class="ghinda-volume-slider"></div><a class="ghinda-volume-button" title="Mute/Unmute"></a></div></div>'); $gVideo.wrap($video_wrap); $gVideo.after($video_controls);

Here we are using jQuery to create the video player markup dynamically (but not the video player itself), and removing the controls attribute once the script loads. That's because in cases where the user has JavaScript disabled, these controls will be useless, and he/she won't even get the native browser controls to the video element. It makes a lot more sense to start with the controls attribute present in case the script fails to load, and then removing it so the player will use our custom controls only after the script successfully loads.

Next, we'll have to target each of the elements in the controls, in order to be able to add listeners.

//get newly created elements var $video_container = $gVideo.parent('.ghinda-video-player'); var $video_controls = $('.ghinda-video-controls', $video_container); var $ghinda_play_btn = $('.ghinda-video-play', $video_container); var $ghinda_video_seek = $('.ghinda-video-seek', $video_container); var $ghinda_video_timer = $('.ghinda-video-timer', $video_container); var $ghinda_volume = $('.ghinda-volume-slider', $video_container); var $ghinda_volume_btn = $('.ghinda-volume-button', $video_container); $video_controls.hide(); // keep the controls hidden

We're targeting each control by its class; we'll keep the controls hidden until everything is ready.

Now for the Play/Pause controls:

var gPlay = function() { if($gVideo.attr('paused') == false) { $gVideo[0].pause(); } else { $gVideo[0].play(); } }; $ghinda_play_btn.click(gPlay); $gVideo.click(gPlay); $gVideo.bind('play', function() { $ghinda_play_btn.addClass('ghinda-paused-button'); }); $gVideo.bind('pause', function() { $ghinda_play_btn.removeClass('ghinda-paused-button'); }); $gVideo.bind('ended', function() { $ghinda_play_btn.removeClass('ghinda-paused-button'); });

Most browsers provide a secondary set of controls for the video in the right-click (ctrl-click on a Mac) context menu. Because of the way we are putting this together, if a user activated these alternative controls it would break our custom controls. In order to avoid this we're attaching events to the Play/Pause button itself, and the "Play", "Pause" and "Ended" listeners of the video player.

We're also adding and removing classes from our button to change the look of it, depending on the state of the video (Playing or Paused).

For creating the seek slider we'll use the jQuery UI Slider component.

var createSeek = function() { if($gVideo.attr('readyState')) { var video_duration = $gVideo.attr('duration'); $ghinda_video_seek.slider({ value: 0, step: 0.01, orientation: "horizontal", range: "min", max: video_duration, animate: true, slide: function(){ seeksliding = true; }, stop:function(e,ui){ seeksliding = false; $gVideo.attr("currentTime",ui.value); } }); $video_controls.show(); } else { setTimeout(createSeek, 150); } }; createSeek();

As you can see, we're using a recursive function, while reading the readyState of the video. We have to keep polling the video until it is ready, otherwise we can't determine the duration, and can't create the slider. Once the video is ready, we initialize the slider, and also show the controls.

Next we'll create the timer, and attach it to the timeupdate listener of the video element.

var gTimeFormat=function(seconds){ var m=Math.floor(seconds/60)<10?"0"+Math.floor(seconds/60):Math.floor(seconds/60); var s=Math.floor(seconds-(m*60))<10?"0"+Math.floor(seconds-(m*60)):Math.floor(seconds-(m*60)); return m+":"+s; }; var seekUpdate = function() { var currenttime = $gVideo.attr('currentTime'); if(!seeksliding) $ghinda_video_seek.slider('value', currenttime); $ghinda_video_timer.text(gTimeFormat(currenttime)); }; $gVideo.bind('timeupdate', seekUpdate);

Here we're using the seekUpdate function to get the currentTime attribute of the video, and the gTimeFormat function to format the actual value received.

For the volume controls, we'll also use the jQuery UI slider and a custom function on the volume button for muting and un-muting the video.

$ghinda_volume.slider({ value: 1, orientation: "vertical", range: "min", max: 1, step: 0.05, animate: true, slide:function(e,ui){ $gVideo.attr('muted',false); video_volume = ui.value; $gVideo.attr('volume',ui.value); } }); var muteVolume = function() { if($gVideo.attr('muted')==true) { $gVideo.attr('muted', false); $ghinda_volume.slider('value', video_volume); $ghinda_volume_btn.removeClass('ghinda-volume-mute'); } else { $gVideo.attr('muted', true); $ghinda_volume.slider('value', '0'); $ghinda_volume_btn.addClass('ghinda-volume-mute'); }; }; $ghinda_volume_btn.click(muteVolume);

Finally we're going the remove the controls attribute from the <video>, because by this point our own custom controls are set up and we want to use those instead of the browser defaults.

$gVideo.removeAttr('controls');

Now that we have our plugin all done, we can call it on any video element we want, like so.

$('video').gVideo();

This will call the plugin on all the video elements on the page.

Look and Feel

And now for the fun part, the look and feel of the video player. Once the plugin is ready, customizing the controls is really easy with a little bit of CSS. As you've notice we haven't added any styling to the controls. We'll use CSS3 for all the customizations regarding the player.

First, we'll add some style to the main video player container. We'll use this as the main chrome for the player.

.ghinda-video-player { float: left; padding: 10px; border: 5px solid #61625d; -moz-border-radius: 5px; /* FF1+ */ -ms-border-radius: 5px; /* IE future proofing */ -webkit-border-radius: 5px; /* Saf3+, Chrome */ border-radius: 5px; /* Opera 10.5, IE 9 */ background: #000000; background-image: -moz-linear-gradient(top, #313131, #000000); /* FF3.6 */ background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #313131),color-stop(1, #000000)); /* Saf4+, Chrome */ box-shadow: inset 0 15px 35px #535353; }

We've floated it left, to prevent it from expanding to the full width of the player, instead keeping it restrained to the width of the actual video element. We're using gradients and border radius to add polish, plus an inset box shadow to emulate the gradient effect in Opera, as it does not yet support gradients (as of 10.60, the latest version at the time of writing).

Next we'll float all the controls to the left, to align them horizontally. We'll use opacity and transitions on the Play/Pause and Volume Mute/Unmute buttons to create a nice hover effect.

.ghinda-video-play { display: block; width: 22px; height: 22px; margin-right: 15px; background: url(../images/play-icon.png) no-repeat; opacity: 0.7; -moz-transition: all 0.2s ease-in-out; /* Firefox */ -ms-transition: all 0.2s ease-in-out; /* IE future proofing */ -o-transition: all 0.2s ease-in-out; /* Opera */ -webkit-transition: all 0.2s ease-in-out; /* Safari and Chrome */ transition: all 0.2s ease-in-out; } .ghinda-paused-button { background: url(../images/pause-icon.png) no-repeat; } .ghinda-video-play:hover { opacity: 1; }

I'm sure you followed the JavaScript part carefully, and saw that we're adding and removing classes on the Play/Pause button depending on the state of the video(Playing/Paused). That's why the ghida-paused-button class overwrites the background property of the ghinda-video-play class.

Now for the sliders. As you saw before, we're using the jQuery UI slider control for both the seek bar and the volume level. This component has its own styles defined in jQuery UI's stylesheet, but we'll completely overwrite these to make the look of the slider more in keeping with the rest of the player.

.ghinda-video-seek .ui-slider-handle { width: 15px; height: 15px; border: 1px solid #333; top: -4px; -moz-border-radius:10px; -ms-border-radius:10px; -webkit-border-radius:10px; border-radius:10px; background: #e6e6e6; background-image: -moz-linear-gradient(top, #e6e6e6, #d5d5d5); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #e6e6e6),color-stop(1, #d5d5d5)); box-shadow: inset 0 -3px 3px #d5d5d5; } .ghinda-video-seek .ui-slider-handle.ui-state-hover { background: #fff; } .ghinda-video-seek .ui-slider-range { -moz-border-radius:15px; -ms-border-radius:15px; -webkit-border-radius:15px; border-radius:15px; background: #4cbae8; background-image: -moz-linear-gradient(top, #4cbae8, #39a2ce); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #4cbae8),color-stop(1, #39a2ce)); box-shadow: inset 0 -3px 3px #39a2ce; }

Currently the volume slider is also visible at all times, positioned next to the volume button. We'll change this so the slider is hidden by default, and shows up only when we're hovering the Mute/Unmute button, to make it look a bit more dynamic and neater. Again, transitions are our answer here:

.ghinda-volume-box { height: 30px; -moz-transition: all 0.1s ease-in-out; /* Firefox */ -ms-transition: all 0.1s ease-in-out; /* IE future proofing */ -o-transition: all 0.2s ease-in-out; /* Opera */ -webkit-transition: all 0.1s ease-in-out; /* Safari and Chrome */ transition: all 0.1s ease-in-out; } .ghinda-volume-box:hover { height: 135px; padding-top: 5px; } .ghinda-volume-slider { visibility: hidden; opacity: 0; -moz-transition: all 0.1s ease-in-out; /* Firefox */ -ms-transition: all 0.1s ease-in-out; /* IE future proofing */ -o-transition: all 0.1s ease-in-out; /* Opera */ -webkit-transition: all 0.1s ease-in-out; /* Safari and Chrome */ transition: all 0.1s ease-in-out; } .ghinda-volume-box:hover .ghinda-volume-slider { position: relative; visibility: visible; opacity: 1; }

We're hiding the volume slider by default, and giving the volume container a small fixed height that just fits the width of the volume button. We're also assigning transitions to both.

When the volume button is hovered, its height increases via the specified transition; we then use the .ghinda-volume-box:hover .ghinda-volume-slider descendant selector to transition the volume slider into view.

With basic CSS knowledge and some new CSS3 properties, we've already created a nice interface for our player, it looks like Figure 2:

Video player screenshot

Figure 2: Our finished video player.

Themeing the player

As you probably noticed, when creating the jQuery plugin, we've defined a set of default options. These options are theme and childtheme, and can be changed when calling the plugin, allowing us to easily apply custom themes as desired.

A theme represents a completely new set of CSS rules for every single control. A child theme on the other hand is a set of CSS rules that builds upon the rules of an existing theme, adding or overwriting the "parent" theme's style.

We can specify both of there options or only one, when calling the jQuery plugin.

$('video').gVideo({ childtheme:'smalldark' });

In the above example code we are calling the plugin with the smalldark child theme specified. This will apply our default parent theme, and then apply our child theme over the top of it, overwriting a small portion of the rules set by the parent theme. See Figure 3 for the Smalldark theme in action.

Smalldark child theme

Figure 3: the Smalldark child theme in action.

You can check out the final video player example live to see both themes in action, or download the source code (8.5mb, ZIP file) and experiment with it further.

Summary

Building our own custom video player with HTML5 video, JavaScript and CSS3 is fairly easy. By using JavaScript only for the actual functionality of the controls, and CSS3 for everything that involves the look and feel of the player, we get a powerful, easily customizable solution.

Kategorien: Google Reader

// Canto.js: An Improved Canvas API

Javascript author extraordinaire David Flanagan released Canto.js recently, a lightweight wrapper API for canvas, introduced here and documented at the top of the source code. Example:

PLAIN TEXT JAVASCRIPT: canto("canvas_id").moveTo(100,100).lineTo(200,200,100,200).closePath().stroke();
 

Notice three things:

  • canto() returns an abstraction of the canvas - a "Canto" object.
  • As with jQuery and similar libraries, there's method chaining; each method called on a Canto also returns the Canto.
  • lineTo() has been extended to support multiple lines being drawn in a single call.

Instead of setting the ink properties and then painting it, you can do it all in one step:

PLAIN TEXT JAVASCRIPT: canto("canvas_id").moveTo(100,100).lineTo(200,200,100,200).closePath().stroke({lineWidth: 15, strokeStyle: "red"});
 

And plenty more syntactic sugar - check out the API in the source code comments. Sweet!

Thanks @pkeane.

Kategorien: Google Reader

// Do You Know How Slow Your Web Page Is?

The Web Timing draft specification presents a standard set of metrics for measuring web page load time across browsers. We’re happy to announce that in Chrome 6, web developers can now access these new metrics under window.webkitPerformance.
Measuring web page load time is a notoriously tricky but important endeavor. One of the most common challenges is simply getting a true start time. Historically, the earliest a web page could reliably begin measurement is when the browser begins to parse an HTML document (by marking a start time in a <script> block at the top of the document).
Unfortunately, that is too late to include a significant portion of the time web surfers spend waiting for the page: much of the time is spent fetching the page from the web server. To address this shortcoming, some clever web developers work around the problem by storing the navigation start time in a cookie during the previous page’s onbeforeunload handler. However, this doesn’t work for the critical first page load which likely has a cold cache.
Web Timing now gives developers the ability to measure the true page load time by including the time to request, generate, and receive the HTML document. The timeline below illustrates the metrics it provides. The vertical line labeled "Legacy navigation started" is the earliest time a web page can reliably measure without Web Timing. In this case, instead of a misleading 80ms load time, it is now possible to see that the user actually experienced a 274ms time. Including this missing phase will make your measurements appear to increase. It’s not because pages are getting slower – we’re just getting a better view on where the time is actually being spent.

Across other browsers: Web Timing metrics are under window.msPerformance in the third platform preview of Internet Explorer 9 and work is underway to add window.mozPerformance to Firefox. The specification is still being finalized, so expect slight changes before the browser prefixes are dropped. If you’re running a supported browser, please try the Web Timing demonstration and send us feedback.
Posted by Tony Gentilcore, Software Engineer
Kategorien: Google Reader

// Constructorification

... he he, I know the title could not be worst, but after my last post about Arrayfication I have thought: "... hey, the Thing.ify(object) could be more than handy in many occasions such mixins and duck typing ...".

So, let me introduce the Function.prototype method that nobody will ever use:

Function.prototype.ify = function (o) {
for (var
self = this,
p = self.prototype,
// find a fucking way to implement this in ES3
// ... uh wait, there's no way to implement
// this in ES3 ...
// https://bugzilla.mozilla.org/show_bug.cgi?id=518663
m = Object.getOwnPropertyNames(p),
i = m.length, n; i--;
) {
// methods only
m[i] = typeof p[n = m[i]] == "function" ?
"o." + n + "=p." + n + ";" : ""
;
}
m.push("return o");
return (self.ify = Function("p",
"return function " +
(self.name || "anonymous") + "fy(o){" +
m.join("") +
"}"
)(p))(o);
};

Since the function requires a Object.getOwnPropertyNames call for its prototype in order to be able to inject it's properties and methods in whatever object, the ify method is lazily assigned.
This code makes "Function.ification" easy so that precedent post could be summarized via:

Array.ify(
document.getElementsByTagName("*")
).forEach(function (node) {
// do stuff with node
});

and be performed at light speed every other time while it won't cost at all until it's performed on whatever constructor the first time only.

Well, at least the technique may result interesting, unfortunately without ES5 it's not possible to reproduce the same behavior without knowing in advance all possible keys part of the native prototype (it's possible for user defined ones tho)
Kategorien: Google Reader

// Firefox 4 Beta 2 is here – Welcome CSS3 transitions

As we have explained before, Mozilla is now making more frequent updates to our beta program. So here it is, Firefox Beta 2 has just been released, 3 weeks after Beta 1.

Firefox 4 Beta 1 already brought a large amount of new features (see the Beta 1 feature list). So what’s new for web developers in this beta?

Performance & CSS3 Transitions

The two major features for web developers with this release are Performance improvements and CSS3 Transitions on CSS3 Transforms.

This video is hosted by Youtube and uses the HTML5 video tag if you have enabled it (see here). Youtube video here.

Performance: In this new Beta, Firefox comes with a new page building mechanism: Retained Layers. This mechanism provides noticeable faster speed for web pages with dynamic content, and scrolling is much smoother. Also, we’re still experimenting with hardware acceleration: using the GPU to render and build some parts of the web page.

CSS3 Transitions on transforms: The major change for web developers is probably CSS3 Transitions on CSS3 Transformations.

CSS3 Transitions provide a way to animate changes to CSS properties, instead of having the changes take effect instantly. See the documentation for details.

This feature was available in Firefox 4 Beta 1, but in this new Beta, you can use Transitions on Transformation.

A CSS3 Transformation allows you to define a Transformation (scale, translate, skew) on any HTML element. And you can animate this transformation with the transitions.

See this box? Move your mouse over it, and its position transform: rotate(5deg); will transform transform: rotate(350deg) scale(1.4) rotate(-30deg); through a smooth animation. #victim { background-color: yellow; color: black;   transition-duration: 1s; transform: rotate(10deg);   /* Prefixes */   -moz-transition-duration: 1s; -moz-transform: rotate(5deg);   -webkit-transition-duration: 1s; -webkit-transform: rotate(10deg);   -o-transition-duration: 1s; -o-transform: rotate(10deg); } #victim:hover { background-color: red; color: white;   transform: rotate(350deg) scale(1.4) rotate(-30deg);   /* Prefixes */   -moz-transform: rotate(350deg) scale(1.4) rotate(-30deg); -webkit-transform: rotate(350deg) scale(1.4) rotate(-30deg); -o-transform: rotate(350deg) scale(1.4) rotate(-30deg); }

CSS 3 Transitions are supported by Webkit-based browsers (Safari and Chrome), Opera and now Firefox as well. Degradation (if not supported) is graceful (no animation, but the style is still applied). Therefore, you can start using it right away.

Demos

I’ve written a couple of demos to show both CSS3 Transitions on Transforms and hardware acceleration (See the video above for screencasts).

This demo shows 5 videos. The videos are Black&White in the thumbnails (using a SVG Filter) and colorful when real size (click on them). The “whirly” effect is done with CSS Transitions. Move you mouse over the video, you’ll see a question mark (?) button. Click on it to have the details about the video and to see another SVG Filter applied (feGaussianBlur). This page shows 2 videos. The top left video is a round video (thanks to SVG clip-path) with SVG controls. The main video is clickable (magnifies the video). The text on top of the video is clickable as well, to send it to the background using CSS Transitions. This page is a simple list of images, video and canvas elements. Clicking on an element will apply a CSS Transform to the page itself with a transition. White elements are clickable (play video or bring a WebGL object). I encourage you to use a hardware accelerated and a WebGL capable browser. For Firefox on Windows, you should enable Direct2D.

Credits

Creative Commons videos:

The multicolor cloud effect (MIT License)

Kategorien: Google Reader

// Array.prototype.slice VS Arrayfication

One of the most common operations performed on daily basis directly or indirectly via frameworks and libraries is Array.prototype.slice calls over non Array elements such HTMLCollection, NodeList, and Arguments.

Why We Perform Such Operation
The Function.prototype.apply works only with object created through the [[Class]] Array or Arguments.
In latter case we may like to avoid ES3 arguments and named arguments mess when dealing with indexes.
Finally, in most of the case we would like to perform Array operations over ArrayLike objects to filer, map, splice, change, modify, etc etc ...

The Slice Cost
Let's perform over an ArrayLike object of length 2000, 2000 slice calls (change the length if you have such powerful machine):

// create the ArrayLike object
for(var arguments = {length:2000}, i = 0; i < 2000; ++i)
arguments[i] = i;
;

// define the bench function
function testSlice(arguments) {
var t = new Date;
for (var slice = Array.prototype.slice, i = 0, length = arguments.length; i < length; ++i)
slice.call(arguments);
;
return new Date - t;
}

// test it
alert(testSlice(arguments));

The average time in my Atom N270 based Netbook is 1750 ms, and numbers are not that unreasonable.
While a list of 2000 generic values may be not that common, the number of slice.call may be definitively more than 2000 during an application life cycle. All this wasted time to simply transform a generic list into an Array? And maybe just to loop and re-loop over it to filter or change values and indexes?

Alternative One: __proto__
If we modify the __proto__ property of whatever object and in almost all browsers (not IE, of course), we can somehow "promote" the current object to Array, except for the [[Class]] that will still be the generic Object, or Arguments.
The undesired side effect is that the modified object won't find anymore in its prototype its original methods, so we can define this technique really greedy.
But what about performances?

// function to test
function testProto(arguments) {
var t = new Date;
for (var proto = Array.prototype, i = 0, length = arguments.length; i < length; ++i)
arguments.__proto__ = proto;
;
return new Date - t;
}

// the result using precedent arguments object
alert(testProto(arguments));

The average elapsed time in this case is 40 ms but we have to remember is that we are not converting the object into an Array instance, we are simply overwriting it's inherited properties and methods with those part of the Array.prototype object.
This means that push, unshift, splice, forEach, or other operations, will be accessible directly through the object (e.g. arguments.forEach( ... ))
If these methods are the reason we would like to slice the generic object, this solution is definitively preferred.

Alternative Two: Arrayfication
To avoid the undesired side effect obtained via __proto__ assignment, the removal of inherited methods, we may consider to use call or apply directly via Array.prototype.
The imminent side effect of this technique is that potentially every function may like to use one of the Array prototype methods against the same object which is always passed by reference.
A simple solution could be the one to attach directly a method to this object, so that every part of the application will be able to use, as example, a forEach call for this object, without accessing every time the Array.prototype.forEach method.

arguments.forEach = Array.prototype.forEach;
// now use forEach wherever we need
// with arguments object

Since every function may like to use one or more Array methods, how about creating a function able to attach all of them in one shot?

Array.fy = (function () {
// (C) WebReflection - Mit Style License
for (var
m = [
"pop", "push", "reverse", "shift", "sort", "splice", "unshift",
"concat", "join", "slice", "indexOf", "lastIndexOf",
"filter", "forEach", "every", "map", "some", "reduce", "reduceRight"
],
i = m.length; i--;
) {
m[i] = "o." + m[i] + "=p." + m[i];
}
m.push("return o");
return Function("p",
"return function Arrayfy(o){" + m.join(";") + "}"
)(Array.prototype);
}());

With a single call we can attach all current available Array.prototype methods once without getting rid of current inherited properties or methods.
Let's see how much does a call cost:

// test function for Array.fy
function testArrayfy(arguments) {
var t = new Date;
for (var fy = Array.fy, i = 0, length = arguments.length; i < length; ++i)
fy(arguments);
;
return new Date - t;
}

// bench
alert(testArrayfy(arguments));

The average elapsed time for this operation is 3 ms.
After a single call we can consider the generic object an Array duck, preserving its inheritance.
The greedy aspect is about possible overwrites, but I have personally never called a method forEach if this is not exactly representing the Array.forEach method.

Arrayfied Operations
The last benchmark we can do is about common Array operations over our Arrayfied object.
The first consideration to do is that slice, as every other Array operation, seems to be extremely optimized for real Array objects.
In few words if we need to transform because we need many calls to forEach, filter, map, slice, etc, the transformation via slice is probably what we are looking for.
But if we need a generic loop over a generic callback and just few times, Array.fy proposal is probably the most indicated one. Here some extra test:

// slice plus a forEach operation
function testSliceEach(arguments) {
var t = new Date;
for (var slice = Array.prototype.slice, fn = function() {}, i = 0, length = arguments.length; i < length; ++i)
slice.call(arguments).forEach(fn);
;
return new Date - t;
}

// just forEach through Arrayfied object
function testArrayfied(arguments) {
var t = new Date;
Array.fy(arguments);
for (var fn = function() {}, i = 0, length = arguments.length; i < length; ++i)
arguments.forEach(fn);
;
return new Date - t;
}

// just slice through Arrayfied object
function testArrayfiedSliced(arguments) {
var t = new Date;
Array.fy(arguments);
for (var fn = function() {}, i = 0, length = arguments.length; i < length; ++i)
arguments.slice();
;
return new Date - t;
}

// bench

alert(testSliceEach(arguments)); // 3200ms
alert(testArrayfied(arguments)); // 2400ms
alert(testArrayfiedSliced(arguments));// 1700ms

The last test is against a classic slice.call and it costs basically the same.
First and seconds demonstrate that if we slice to use native power we are actually spending more time than using native power directly.
Bear in mind that if the variable is already an Array, slice will cost much less and the average against the last test will be 1350 ms.

IE And Conclusions
I keep saying that IE should simply have normalized Array.prototype, ignoring those person that wrongly rely in for in loops over arrays when it's not necessary, and making this Array.fy portable for IE world as well since the internal proto variable is a pointer to the original Array.prototype then automatically ready for natives standard enhancements.
The nice part is that rather than see Array.prototype.something.call in every piece of code we can easily use one fast call to have them all ... so, you decide :-)

.. last minute Example ...
Just because sometimes we lack of fantasy, here a generic usage for Array.fy:

function $(selector, parentNode) {
return Array.fy((parentNode || document).querySelectorAll(selector));
}

$("div").forEach(function (div, i, nodeList) {
div.innerHTML = "Array.fy rocks!";
});
Kategorien: Google Reader

// YUI 3.2.0 Preview Release 1: Touch Event Support, Gestures, Transitions, CSS Grids, ScrollView, Uploader, and More

The YUI contributor’s team is pleased to announce the first developer preview of the upcoming YUI 3.2.0 release. This preview provides an opportunity for developers and implementers to help test the release for potential regressions and to provide feedback on new features and components. If you have an existing YUI implementation, please exercise YUI 3.2.0pr1 in your development environment and let us know what you find.

There are three ways to get started with the preview release:

  • Use from the CDN: YUI 3.2.0pr1 is available on the CDN via the 3.2.0pr1 version tag — so you can reference preview-release files like http://yui.yahooapis.com/combo?3.2.0pr1/build/yui/yui-min.js. If you switch to this seed file for the preview release, all subsequent use() statements will continue to load YUI 3.2.0pr1.
  • Download the release: Download YUI 3.2.0pr1 from YUILibrary.com, including source code and examples for all components — including those new to this release.
  • Explore the examples: As a convenience, we’ve posted the preview (along with the functioning examples roster) to YUIBlog. Feel free to explore the release there as a prelude to switching your CDN version reference (or downloading the preview) and testing it out in your own environment.
Noteworthy Changes Coming in YUI 3.2.0

As with all YUI development work, you can track our current plans and progress on our YUI 3 tasklist, including a comprehensive list of YUI 3.2.0 (and some upcoming 3.3.0) changes; you can also check in on our progress addressing issues in the bug database. Here are some of the new and updated components featured in the 3.2.0 developer preview:

  • Intrinsic support for touch events has been added (mynode.on("touchstart", function(e) {});). We’ve also added a Gestures module with two bundled gestures — gesture-flick and gesture-move — that work with both touch- and mouse-driven devices. Check out the API docs or the bundled sample page for ideas about how to start using Gestures.
  • YUI’s intrinsic Loader now supports capability-based loading. This allows us to segregate, for example, IE-specific code into separate submodules and allow the Loader to bundle that code only for browsers that require it. We’re leveraging this new feature to avoid shipping IE-specific code in the Dom module to non-IE browsers, a performance/k-weight boost that will benefit all users of modern browsers with no code change required.
  • YUI 3’s animation portfolio now supports transitions via the Transition module, providing browser normalization for this powerful, hardware-accelerated (where available) technique for handling transitions; check out the example for sample code. Animation, in its most basic form, has a streamlined dependency tree for modern browsers, significantly reducing the k-weight for simple animation in better browsers.
  • YUI 3.2.0 will bring with it a new beta version of YUI’s CSS Grids component, and you can begin exploring this new approach to Grids in the preview release. The examples are the best place to start.
  • We worked with Michael Johnston of the Yahoo! Mobile Engineering team to bring a new (beta) ScrollView widget to YUI 3.2.0. ScrollView provides a scrolling pane implementation familiar to users of native Apple iOS applications, emulating the elasticity of the element when scrolled to the beginning or ending limit. You’ll see in the 3.2.0pr1 examples for ScrollView that this component is device neutral, working well with a mouse as well as with touch events on your Android or iOS device.
  • The Uploader component from YUI 2 is now part of the YUI 3 family as well, debuting as a beta in 3.2.0.
  • The History module that debuted with YUI 3.0.0, which was a port of the YUI 2 version, has been deprecated (it remains available in YUI 3.2.0 as history-deprecated). A new beta History utility debuts in 3.2.0, based on Ryan Grove’s History Lite module from the YUI 3 Gallery. A preview-release example from the new component is a good starting reference.
  • The JSONP and YQL Query modules from the YUI 3 Gallery have become canonical components, debuting as beta in this release.
Feedback

The goal of a preview release is to make it as easy as possible for all of us in the community to evaluate progress of the upcoming release and provide feedback. Please take some time to test 3.2.0pr1 and let us know what you find by filing tickets in the YUI 3 bug database marked as “Observed in version” 3.2.0pr1. We’ll do our best to address preview-release questions on the YUI 3 Forums, too.

Kategorien: Google Reader

// JavaScript Minification Part II

Variable naming can be a source of coding angst for humans trying to understand code. Once you’re sure that a human doesn’t need to interpret your JavaScript code, variables simply become generic placeholders for values. Nicholas C. Zakas shows us how to further minify JavaScript by replacing local variable names with the YUI Compressor.contact.us@alistapart.com (Nicholas C. Zakas)166702322456877685611736528485931647586201650999374317979751068689244044986337650476611736287053283011731391817765969451103501049211940809220771206984318013287815167073887160809467060936933914334987521263596504213267338000458366911360432541061966480780391876320995854365075818768000518313665720447865
Kategorien: Google Reader

// SVG with a little help from Raphaël

Want to make fancy, interactive, scalable vector graphics (SVGs) that look beautiful at any resolution and degrade with grace? Brian Suda urges you to consider Raphaël for your SVG heavy lifting.contact.us@alistapart.com ( Brian Suda)094236590523611685521282789693559354686014048597963530591234027585087650165594111574835800734783436717365284859316475862005749301212565817740476611736287053283009552507979975981384120347003627250743400929871196365159880710350104921194080922160092074003788812280730941313983468223003343349680384261950060936933914334987521602820473884831723312635965042132673380088531617041740640290641551774625972819605221790502551658316061966480780391876321714712376642589131800518313665720447865
Kategorien: Google Reader

// An alternative way to addEventListener

I can't believe none of us knew DOM2

This is how a tweet from @SubtleGradient, re-tweeted by @jdalton, has been able to steal my rest tonight ... and this post is the consequence ...

What's new in a nutshell

There is a W3C Recommendation about addEventListener behavior, which clearly specify the second argument as an EventListener.
The new part is that no library I know has ever used a proper EventListener interface, preferring the classic attached callback instead.

PLAIN TEXT JAVASCRIPT:

// this is how it is
document.addEventListener(
    "click",
    function (evt) { /* stuff */ },
    false
);
 
// this is how it could be as well
var listener = {
    handleEvent: function (evt) {

        this === listener; // true

        // and evt === classic event object

    }
};
 
document.addEventListener("click", listener, false);

Benefits

The most common case that may disappear is well explained in this MDC addEventListener page.
Rather than bind inline or add anonymous functions to make our object call context preserved, we can simply add an handleEvent method to whatever object and pass it as EventListener.
Moreover, being close to full ES5 support and "use strict" directive where arguments.callee disappears, it may be more than handy to be able to perform such operation:

PLAIN TEXT JAVASCRIPT: document.addEventListener("click", {
    handleEvent: function (evt) {
        // 1 shot callback event example
        switch (evt.target.nodeType) {
            case 1:
            case 9:
                evt.target.removeEventListener(
                    evt.type,
                    this, // here we are!
                    false
                );
                break;
        }
    }
}, false);
  An opened door for custom listeners

As I have recently posted, custom listeners implementation can be truly handy when we are dealing with events driven applications, but as soon as I have read the tweet, I had to rewrite a fresh new way to create a listener. Please note that following code is assuming that the browser supports both DOM Level 2 and Array extras, which is true for all modern browsers, mobile oriented included.

PLAIN TEXT JAVASCRIPT: function createEventListener() {

    /*! Andrea Giammarchi for Ajaxian - Mit Style */

    // a function declaration reused internally
    function notifyEvent(callback, i, stack) {
        // use DOM Level 0 events strategy
        //  to stop the loop if necessary
        // checking if the result is exactly false
        if (callback.call(
            // the curent object as context
            this,
            // the classic event as first argument
            event,
            // the called callback (life easier)
            callback,
            // again the current context
            // if the callback has been bound
            this
        ) === false) {
            // if false, reassign the current stack ...
            eventListener["@"+event.type] = stack.slice();
            // ... and break the current forEach loop
            // (or, for the record, whatever Array.extras)
            stack.length = 0;
        }
    }

    var
        // local scoped object, reachable internally
        // usable as mixin so instances won't be polluted
        // with all possible event types
        // the type is prefixed in any case
        // so that name clashes should be
        // really rare however we use the object
        eventListener = {

            // we attach to a proper stack
            addEvent: function (type, callback) {
                var
                    // try to retrieve the stack ...
                    stack = (
                        // if already there ...
                        eventListener["@" + type] ||
                        // otherwise we create it once
                        (eventListener["@" + type] = [])
                    ),
                    // as addEventListener, don't attach
                    // the same event twice
                    i = stack.indexOf(callback)
                ;
                // so if it was not there ...
                if (-1 === i) {
                    // FIFO order via stack
                    stack.push(callback);
                }
            },

            // called via addEventListener
            // the "this" reference will be
            // the eventListener object,
            // or the current instance
            // if used as "class" mixin
            // or via Object.create / clone / merge
            handleEvent: function (e) {
                // retrieve the stack
                var stack = eventListener["@" + e.type];
                // and if present ...
                if (stack) {
                    // set temporarily the local event var
                    event = e;
                    // notify all registered callbacks
                    // using current this reference
                    // as forEach context
                    stack.forEach(notifyEvent, this);
                    // let the GC handle the memory later
                    event = null;
                }
            },

            // how we remove the event, if any ...
            removeEvent: function (type, callback) {
                var
                    // try to retrieve the stack ...
                    stack = eventListener["@" + type],
                    // find the index ...
                    i
                ;
                // if the stack is present
                if (stack && ~(
                    i = stack.indexOf(callback)
                )) {
                    // remove it
                    stack.splice(i, 1);
                }
            }
        },

        // I could have called this variable tmp
        // but it's actually the current event
        // once assigned ... so ...
        event
    ;

    // ready to go!
    return eventListener;

}

Here a usage example:

PLAIN TEXT JAVASCRIPT: var lst = createEventListener();

/** mixin example (add a slash before this line to test)

function MyEventListener() {}
MyEventListener.prototype.addEvent = lst.addEvent;
MyEventListener.prototype.handleEvent = lst.handleEvent;
MyEventListener.prototype.removeEvent = lst.removeEvent;

lst = new MyEventListener;

// */

document.addEventListener("click", lst, false);

lst.addEvent("click", function click(e, callback, object){

    alert([
        callback === click, // true
        this === lst,       // true
        this === object,    // true
        e.type === "click"  // true
    ]);

    // test that furthermore this
    // callback won't be fired again
    this.removeEvent("click", callback);

    // add delayed a callback
    // without any valid reason :-)
    setTimeout(function (self) {
        // test addEvent again
        self.addEvent("click", function () {
            alert(2);
        });
    }, 0, this);

    // block the current notification
    return false;
});

// the event fired only the second click
lst.addEvent("click", function () {
    alert(1);
});

/** de-comment the mixin example to test
//   that no @click is attached ;-)
for (var key in lst) {
    alert(key);
}
// */
 

Advantages
  • both evt.stopPropagation() and evt.preventDefault() are not able to break the current notification of all attached listeners, if added to the same node, and while the FIFO order gives to the node "owner" or creator the ability to pollute the event object with some flag such evt.pleaseDontDoAnyOtherActionHere = true, not every library, script, or framework, may respect or understand this flag. With custom events we can adopt better strategies to actually avoid any other operation if this is what we meant, because we arrived before over the node and we may like to be that privileged
  • being custom, we can also decide which argument should be passed for each callback, simplifying most common problems we may have when dealing with listeners
  • we can better decouple DOM and listeners, being able to remove whatever amount of callbacks simply calling once node.removeEventListener(evt.type, this, false); inside any kind of notification
  • being based on standard and modern browsers, we can use native power, in this case provided by forEach and indexOf operations, so that performances will be best possible
  • thanks to automatic context injection, we can still reuse callbacks for different listeners, through bind, or simply considering the current context once called (or in this case the third argument by reference, if the context is different)

Last but not least, if we would like to fire an event we can bypass DOM initialization using handleEvent directly, e.g.

PLAIN TEXT JAVASCRIPT: lst.handleEvent({
    target: document.querySelector("#myid"),
    type: "click",
 
    // custom properties
    pageX: 0,
    pageY: 0,
 
    // stubbed methods
    preventDefault: function () {},
    stopPropagation: function () {}
});
  Compatibility ?

Apparently both W3C behavior and provided examples are compatible with every modern browser with DOM Level 2 support, and I believe this is great.
The only one behind here is IE9 pre 3, but again @jdalton has acted at speed light, thanks!

Kategorien: Google Reader

// Follow Team HTC-Columbia on Google Maps and Then Let Others Follow You

In case you missed it, check out the Official Google Blog’s post about how Team HTC-Columbia is using Android-powered phones and a special version of the My Tracks app with SRM to share real-time data from the Tour de France. Head over to google.com/mytrackstour to follow along during the race and see riders’ real-time location along with their power, heart rate, cadence and speed right on Google Maps.



Want to record some of this data or share your location on Google Maps during your next big ride? Track your rides, runs, or walks in detail with the My Tracks app on your Android-powered phone. From many popular smartphones, you can share your location with friends using Google Latitude and then publish your location to the web with the Google Public Location Badge.

Posted by Chris Nguyen, Google Mobile Team
Kategorien: Google Reader

// Velocity: Forcing Gzip Compression

Tony Gentilcore was my officemate when I first started at Google. I was proud of my heritage as “the YSlow guy”. After all, YSlow was well over 1M downloads. After a few days I found out that Tony was the creator of Fasterfox – topping 11M downloads. Needless to say, we hit it off and had a great year brainstorming techniques for optimizing web performance.

During that time, Tony was working with the Google Search team and discovered something interesting: ~15% of users with gzip-capable browsers were not sending an appropriate Accept-Encoding request header. As a result, they were sent uncompressed responses that were 3x bigger resulting in slower page load times. After some investigation, Tony discovered that intermediaries (proxies and anti-virus software) were stripping or munging the Accept-Encoding header. My blog post Who’s not getting gzip? summarizes the work with links to more information. Read Tony’s chapter in Even Faster Web Sites for all the details.

Tony is now working on Chrome, but the discovery he made has fueled the work of Andy Martone and others on the Google Search team to see if they could improve page load times for users who weren’t getting compressed responses. They had an idea:

For requests with missing or mangled Accept-Encoding headers, inspect the User-Agent to identify browsers that should understand gzip.
Test their ability to decompress gzip.
If successful, send them gzipped content!

This is a valid strategy given that the HTTP spec says that, in the absence of an Accept-Encoding header, the server may send a different content encoding based on additional information (such as the encodings known to be supported by the particular client).

During his presentation at Velocity, Forcing Gzip Compression, Andy describes how Google Search implemented this technique:

  • At the bottom of a page, inject JavaScript to:
    • Check for a cookie.
    • If absent, set a session cookie saying “compression NOT ok”.
    • Write out an iframe element to the page.
  • The browser then makes a request for the iframe contents.
  • The server responds with an HTML document that is always compressed.
  • If the browser understands the compressed response, it executes the inlined JavaScript and sets the session cookie to “compression ok”.
  • On subsequent requests, if the server sees the “compression ok” cookie it can send compressed responses.

The savings are significant. An average Google Search results page is 34 kB, which compresses down to 10 kB. The ability to send a compressed response cuts page load times by ~15% for these affected users.

Andy’s slides contain more details about how to only run the test once, recommended cookie lifetimes, and details on serving the iframe. Since this discovery I’ve talked to folks at other web sites that confirm these mysterious requests that are missing an Accept-Encoding header. Check it out on your web site – 15% is a significant slice of users! If you’d like to improve their page load times, take Andy’s advice and send them a compressed response that is smaller and faster.

Kategorien: Google Reader

// Mobile cache file sizes

Mobile is big, but knowledge about how to make a mobile web site fast is lacking. The state of mobile web performance is in a similar place as desktop was six years ago when I started my performance deep dive. How many of us are happy with the speed of web pages on mobile? (I’m not.) How many of us know the top 10 best practices for building high performance mobile web sites? (I don’t.)

I’ve been focusing more on mobile in order to help build this set of best practices. Browserscope is a valuable resource since it measures all browsers, both mobile and desktop. The Network category for popular mobile browsers shows information about max connections per hostname, parallel script loading, redirect caching, and more. Since Browserscope’s data is crowdsourced it’s easy to get coverage on a wide variety of mobile devices. The table below shows the results from Browserscope for some popular mobile devices.

One thing I’ve wanted to measure on mobile is the browser’s cache. Caching on mobile devices is a cause for concern. In my experience a page I visited just a few minutes ago doesn’t seem to be cached when I visit it again. A few months ago I started creating tests for measuring the browser’s cache.

That’s why I was especially excited to see Ryan Grove’s post on Mobile Browser Cache Limits. I noticed his results were quite different from mine, so I commented on his blog post and invited him to contact me. Which he did! It’s great to find someone to collaborate with, especially when designing tests like this where another pair of eyes is a big help.

Ryan and I created a new test design. He’s deployed his under the name cachetest on GitHub. My implementation is called Max Cache File Size. I’m hosting it so you can run it immediately. I’ve integrated it with Browserscope as a User Test. Anyone who runs my hosted version has the option to post their results (anonymously) to Browserscope and contribute to building a repository for script cache sizes for all browsers.

Run the Test

Here’s a link to the Max Cache File Size results on Browserscope. A summary of the results with some other findings follows:

Browser Max Cached
Script Size
Same Session Cache Cross
Lock/Unlock Cache Cross
Power Cycle Android 2.1 4 MB yes yes Android 2.2 2 MB yes yes iPad 4 MB yes no iPhone 3 4 MB yes no iPhone 4 4 MB yes no Palm Pre 1 MB yes yes

My Max Cache File Size test measures the largest script that’s cached in the same session (going from one page to another page). Many mobile devices reach the maximum size tested – 4MB. It’s interesting to see that in the recent upgrade from Android 2.1 to 2.2, the maximum cached script size drops from 4MB to 2MB. The Palm Pre registers at 320kB – much smaller than the others but large enough to handle many real world situations. Note that these sizes are the script’s uncompressed size.

Knowing the cache size that applies during a single session is valuable, but users often revisit pages after locking and unlocking their device, and some users might even power cycle their device between visits. Ryan and I manually tested a few devices under these scenarios – the results are shown in the previous table. Although results are mixed for the power cycling case, the cached items persist across lock/unlock. For me personally, this is the more typical case. (I only power cycle when I’m on the plane or need to reset the device.)

These results show that the 15 kB and 25 kB size limit warnings for a single resource are no longer a concern for mobile devices. However, even though the test went as high as 4 MB (uncompressed), I dearly hope you’re not even close to that size. (I saw similar results for stylesheets, but removed them from the automated test because stylesheets over ~1 MB cause problems on the iPhone.)

It’s great to have this data, and have it verified by different sources. But this is only testing the maximum size of a single script and stylesheet that can be cached. I believe the bigger issue for mobile is the maximum cache size. A few months ago I wrote a Call to improve browser caching. I wrote that in the context of desktop browsers, where I have visibility into the browser’s cache and available disk space. I think the size of mobile caches is even smaller. If you have information about the size of the browser cache on mobile devices, or tests to determine that, please share them in the comments below.

Finally, please run the Max Cache File Size test and add more data to the results.

Run the Test

Many thanks to Ryan Grove for working on this caching test – check out his updated post: Mobile Browser Cache Limits, Revisited. And thanks to Lindsey Simon for making Browserscope such a great framework for crowdsourcing browser performance data.

Kategorien: Google Reader

// jQuery JavaScript Templates Tutorial: Nesting Templates

In my last post, I presented an intro to how to create a template using the new jQuery Template plugin being developed by the Microsoft Ajax Core team. In this tutorial, I’ll show you how to nest templates to have great control over your layout.

The nesting of a template within another template is a pretty valuable feature as it lets you create a modular structure for your layout. Instead of having to create one enormous template to cover every scenario, you can break the layout apart into individual templates and piece them together. Let’s start by defining some data:

var clientData = [ { name: "Rey Bango", age: 42, id: 1, phone: [ "954-600-1234", "954-355-5555" ] }, { name: "Mark Goldberg", age: 51, id: 2, phone: ["954-600-1234", "954-355-5555"] }, { name: "Jen Statford", age: "25", id: 3, phone: ["954-600-1234", "954-355-5555"] } ];

What I’d like to do is to render the basic information like the name and age in one template and then render the phone numbers for each client in a different template.

Like I showed in my previous post, we first create a template for our layout:

<script id="clientTemplate" type="text/html"> <p><a href="clients/${id}">${name} - Age: ${age}</a></p> </script>

This will display the name and age as a hyperlink included within paragraph tags. Next, we create a new template that will be used to render the phone numbers for each client”

<script id="phoneTemplate" type="text/html"> <ul>{{each phone}}<li>${$value}</li>{{/each}}</ul> </script>

Lastly, we’re going to include the call to “phoneTemplate” in our main template using the “tmpl” tag. This tag is used by the plugin to identify templates and parse them accordingly. Here’s what the call would look like:

{{tmpl($data) "#phoneTemplate"}}

and we’re going to include it into the main template:

<script id="clientTemplate" type="text/html"> <p><a href="clients/${id}">${name} - Age: ${age}</a></p> {{tmpl($data) "#phoneTemplate"}} </script>

This will generate the following results:

There are a couple of key things to understand here. First, let’s look at the following:

{{tmpl($data) "#phoneTemplate"}}

The variable “$data” contains the data for the current record being parsed. We’re passing it to the nested template so that we can work with that record. Let’s move onto the nested template:

<script id="phoneTemplate" type="text/html"> <ul>{{each phone}}<li>${$value}</li>{{/each}}</ul> </script>

Now that the current record has been passed to it, we can access the attribute name, in this case “phone”, and loop through each phone record using the ‘{{each}}‘ plugin template tag. Think of it as similar to jQuery’s $.each() method or a JavaScript “for” loop. The code will loop through each phone record creating a new list item for each one and then return to the main template to get the next main record.

Here’s the whole code for you to work with:

<!DOCTYPE html> <html lang="en"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="robots" content="noindex" /> <title>Template Test</title> <script src="http://code.jquery.com/jquery.min.js" type="text/javascript"></script> <script src="jquery.tmpl.js" type="text/javascript"></script> <script type="text/javascript"> $(document).ready(function() { var clientData = [ { name: "Rey Bango", age: 42, id: 1, phone: [ "954-600-1234", "954-355-5555" ] }, { name: "Mark Goldberg", age: 51, id: 2, phone: ["617-777-1234", "617-222-3333"] }, { name: "Jen Statford", age: "25", id: 3, phone: ["608-555-5647", "608-645-8855"] } ]; $("#clientTemplate").tmpl(clientData).appendTo("div"); }); </script> <script id="clientTemplate" type="text/html"> <p><a href="clients/${id}">${name} - Age: ${age}</a></p> {{tmpl($data) "#phoneTemplate"}} </script> <script id="phoneTemplate" type="text/html"> <ul>{{each phone}}<li>${$value}</li>{{/each}}</ul> </script> </head> <body> <div></div> </body> </html>

Related posts:

  1. Not Using jQuery JavaScript Templates? You’re Really Missing Out. In preparation for my upcoming talk on jQuery Templates, I’ve...
  2. I’m Presenting on jQuery Templates at ThinkVitamin’s jQuery Online Conference This coming Monday (7/12/2010), I’ll be one of the speakers...
  3. Generic Activity Indicator for Ajax Requests Over the weekend I was fiddling was some code to...

Related posts brought to you by Yet Another Related Posts Plugin.

Kategorien: Google Reader

// WEAVE 04.10. – Location Based Trends

Location Based…was bitte?

Was auf den ersten Blick scheint wie der Alptraum jeden Datenschutzbeauftragten, ist der neuste große Trend nach Twitter. Sven Wiesner beleuchtet das Phänomen Location Based Services und zeigt zaghafte Versuche, den Hype zu kommerzialisieren. Dazu seine Linktipps:

Facebook Location Feature von Zuckerberg in London offiziell bestätigt!
“We are finishing designing our application soon and hope to offer it soon.”

“A Foursquare app for avoiding sausage fests”.
Dieses Foursquare Mashup alamiert wenn in der Lieblingsbar das favorisierte Geschlecht in der Überzahl ist.

Manche Menschen möchte man einfach nicht treffen.
Avoidr markiert Foursquare-Freunde und hilft alamiert den Nutzer wo sich diese Störenfriede gerade aufhalten.

Der TV Sender History Channel ist ein echter Social Media Riese und nutzt Foursquare, um den Nutzern historische Orte näher zu bringen.
vimeo.com/12925333
foursquare.com/historychannel

Frisches Geld für Foursquare
Foursquare-Gründer Dennis Crowley holt sich bei einer Investorengruppe eine dicke Geldspritze.

Twitter startet “Places” Feature für Tweets, inklusive Foursquare Integration.
blog.twitter.com/2010/06/twitter-places-more-context-for-your.html

Topguest ist ein LBS Mashup dass es ermöglicht, gleichzeitig bei den Services Brightkite, Gowalla, Foursquare, Twitter, Google Latitude, Yelp und Loopt einzuchecken. Zusätzlich soll es ein eigenes Belohnungssystem geben.

Check.in ist ein weiteres Multi-Checkin-Tool und besonders einfach zu bedienen.
Leider geht dadurch so manches Feature der Dienste verloren.

Dieses clevere Foursquare-Mashup ermöglicht es zu überprüfen, bei welchen Location man kurz davor steht den Mayor Status zu erreichen.
whenwillibemayor.com/

Endlich ist die offizielle Foursquare App für Blackberry Smartphones sowie dem Palm Pre (Public Beta) draußen!
foursquare.com/blackberry/
developer.palm.com/webChannel/index.php?packageid=com.foursquare.foursquare

Kategorien: Google Reader

// Summer Reading Fun

Six months ago, we released a bevy of new articles to help with your coding through the dark winter months. OK, they were not so dark South of the equator, but here in Mountain View, well, it rained a few times. Anyway, now that it’s winter South of the equator, and for all of you developers in the North who can’t go out in the sun, we have released your summer reading list. These articles are hot off the digital presses, so enjoy them while they’re fresh. Fun with MVC Objects This article presents a basic introduction to using MVC objects within V3. You will learn how to use the Maps Javascript API V3 MVC framework to create objects that automatically respond to state changes. You will build a resizable distance widget and by the end, you'll have a greater understanding on what MVC objects are, how to use them, and why they're just so "awesome". Geocoding Strategies Ever wondered whether you should use client-side or server-side geocoding? Actually, if you haven’t, you should and this article is for you. In it, you’ll learn why client-side geocoding is so cool, and when and even if you should ever use server-side geocoding. Using Google Sites to Host Your KML A couple of years ago, we released an article on hosting KML on Google Pages. Well, Pages is no more, and has become Google Sites. This is an article for a beginning developer who just wants to put their KML up on the web. External Article: Google Maps API v3: Developing for Mobile Devices Chad Killingsworth, who presented with me at Google I/O in our Map once, map anywhere session, has a great article up summarizing some of the lessons he has learned about developing Google Maps API applications for V3. We added a link to his article on our articles page for the Google Maps API. So enjoy your summer reading! We’re doing our best to prevent your sun burn. Mano Marks, Geo APIs Team
Kategorien: Google Reader

// Velocity: Top 5 Mistakes of Massive CSS

Nicole Sullivan and Stoyan Stefanov had the #3 highest rated session at Velocity – The Top 5 Mistakes of Massive CSS. Nicole (aka, “stubbornella”) wrote a blog post summarizing their work. The motivator for paying attention to CSS are these stats that show how bad things are across the Alexa Top 1000:

  • 42% don’t GZIP CSS
  • 44% have more than 2 CSS external files
  • 56% serve CSS with cookies
  • 62% don’t minify
  • 21% have greater than 100K of CSS

Many of these problems are measured by YSlow and Page Speed, but the solutions still aren’t widely adopted. Nicole goes on to highlight more best practices for reducing the impact of CSS including minimizing float and using a reset stylesheet.

Checkout the slides and video of Nicole and Stoyan’s talk to learn how to avoid having CSS block your page from rendering.

Choose Your Own Adventure Adam Jacob Opscode TCP and the Lower Bound of Web Performance John Rauser Amazon The Top 5 Mistakes of Massive CSS Nicole Sullivan Consultant Building Performance Into the New Yahoo! Homepage Nicholas Zakas Yahoo! Hidden Scalability Gotchas in Memcached and Friends Neil Gunther Performance Dynamics Company Internet Explorer 9 Jason Weber Microsoft Creating Cultural Change John Rauser Amazon Scalable Internet Architectures Theo Schlossnagle OmniTI Ignite Velocity Andrew Shafer Cloudscaling The Upside of Downtime: How to Turn a Disaster Into an Opportunity Lenny Rachitsky Webmetrics/Neustar Metrics 101: What to Measure on Your Website Sean Power Watching Websites The 90-Minute Optimization Life Cycle: Fast by Default Before Our Eyes? Joshua Bixby Strangeloop Networks Progressive Enhancement: Tools and Techniques Anne Sullivan Google Chrome Fast. Mike Belshe Google
Kategorien: Google Reader

// Top 5 Mistakes of Massive CSS

Last week, Stoyan Stefanov and I spoke at Velocity Conference about optimizing massive CSS. We talked about our experiences optimizing large-scale sites like Facebook and Yahoo!, and we discussed our findings regarding the CSS efficiency of the Alexa Top 1000 websites.

Velocity was kind enough to share videos of the session:

What is the state of the internet regarding CSS performance? Kind of sad. We aren’t getting a lot of the basics right, and when we look at the more advanced techniques, there are some spectacular examples of what-not-to-do. Why do we care about CSS performance? As Stoyan talks about in the beginning of the video, it blocks progressive rendering and it is very difficult to auto-minify.

The Basics

These basic rules, made famous by YSlow, have been around for a long time, and yet our data showed that many sites in the Alexa Top 1000 are still not employing the most basic techniques.

  • 42% Don’t GZIP CSS
  • 44% Have more than 2 CSS external files
  • 56% Serve CSS with cookies (yummy to eat, bad for static content)
  • 62% Don’t minify (check out the YUI Compressor!)
  • 21% Have greater than 100K of CSS

CSS Weight for the Alexa Top 1000 Sites

More Advanced Techniques

I talked about these more advanced tests for the first time at Velocity. Here is how the top 1000 sites stack up and some recommendations for reasonable results.

Occurrences in one page Declaration Max Percentage too many
> 10 Percentage sites *way* too many
> 100 Suggestion float 733 56% 13% If you have a good nestable grids system, you shouldn’t need many floats. The worst offender in the Alexa Top 1000 sites declared the float property more than 700 times! Aim for less than 10. h1-6 511 56% 9% There are only so many usable font sizes on the web. Below 10px in most fonts is legible only by mice and few sites use really large typography as a design element. Imagine that a site chooses to use 24px as their max. That leaves 14 different sizes, however, we need to divide that number by two because most users can’t see subtle differences like a 1px change in font size. That leaves seven different heading sizes, which means 56% of sites in the Alexa top 1000 have too many heading declarations. margin: 0 518 64% 14% Different browsers have different default stylesheets. These stylesheets define how elements should look if you haven’t chosen an alternate style. It is important to get all browsers to the same starting point because it eliminates bugs and time wasted testing simple browser compatibility issues. This is should be accomplished using a reset stylesheet such as the one included in YUI. When a reset stylesheet isn’t used, margin zero tends to be sprinkled throughout the stylesheet as developers try to cope with browser differences in the absense of an abstracted solution. Setting the default margins to zero is the most basic job of a reset stylesheet, which means that 64% of the Alexa Top 1000 sites could benefit from including reset.css. padding: 0 510 62% 10% Excessive declarations of padding zero are similar to margins (see the above description). The worse offender in this case declared padding zero 510 times. font-size 889 78% 23% Headings (h1-6) often get hidden in class names, which can disguise typography efficiency issues. It is helpful to grep "font-size" to get an idea how many hidden headings exist on the site. The same rules apply to font-size that were explained under h1-6, so in fact the problem is much worse than our initial estimate. These figures mean that 78% of the Alexa Top 1000 sites have excessive heading declarations when we consider hidden headings. In addition, 22% of sites may not be getting the SEO benefits of properly using heading elements. !important 526 - 12%* Important overrides all other declarations specificity, so it can be dangerous. If used correctly, only on leaf nodes, it can be a powerful tool for creating typography and spacing mixins that stand outside of the normal cascade. On the other hand, excessive use of important is a sure indication of specificity wars. Specificity wars are what happens when developers start trying to beat each others specificity, rather than having a real solid architecture and code standards. Eventually, like the worst offender in this case with 526 important properties, you can end up in a case where nearly every property is marked important. This means that 12% of the Alexa Top 1000 Sites probably has an internal specificity war in it’s web team.

* >50

Happy perf optimizing!

Kategorien: Google Reader

// 3 reasons to build for Mobile First!

The team at LinkedIn has published video from my Mobile First! talk at the LinkedIn Tech Talk series, where I made the case for designing Web applications for mobile platforms before the desktop in order to take advantage of explosive growth, useful constraints, and innovative capabilities. I also outlined tips for designing with: multiple screen sizes and densities, touch gestures, location awareness, orientation changes, tight audio/video integration, and more.

Official Description

More often than not, the mobile experience for a web application or site is designed and built after the PC version is complete. Learn the three reasons web applications should be designed for mobile first instead: mobile is exploding; mobile forces you to focus; and mobile extends your capabilities.


Thanks to the entire LinkedIn team for having me out!

Kategorien: Google Reader

// The death of the pixel as we know it; The new DPI web

The Web used to be so simple. Browser request goes to server, where you do some work, and return some HTML. Then we got Ajax and finally web apps could have some semblance of UI responsiveness. Now we have richer HTML5 technologies to change expectations of our users once again.

The Web is getting some new DPI love, and the new iPhone 4 display personifies this fact. The new display is fantastic for the consumer, and an opportunity for the design enlightened to build truly beautiful web sites. There is a big difference:

However, how do we as developers deal with this new world?

Aral Balkan has a nice post that goes into detail on the new opportunity and shares samples and ideas.

As with so many things on the Web, some of this has been thought of a loooong time ago. Dave Hyatt wrote about this back in 2006.

Walt Dickinson put together a guide to the retina display and using CSS3 media queries:

PLAIN TEXT CSS: <link rel="stylesheet"
    type="text/css"
    href="/css/retina.css"
    media="only screen and (-webkit-min-device-pixel-ratio: 2)"
/>
 

Aral explains that "in the Retina-specific CSS, he loads in 32x32 icons as background images and specifies their dimensions in CSS pixels as 16x16 using the background-size CSS property."

It is interesting to see a device pixel ratio used rather than specifying a DPI itself.

What else can be done to help folks in this new world?

Aral talks about how the browser could natively help via convention:

I'd like to suggest that browsers adopt the same naming convention that Cocoa Touch uses to find and load high-DPI versions of image and video assets. That is, if I embed an image using the following code…

PLAIN TEXT HTML: <img src="flower.jpg" alt="A beautiful rose"/>
 

… it should load in flower.jpg when the device-pixel-ratio is 1 but it should attempt to find an image called flower@2x.jpg at the same relative path if device-pixel-ratio is 2 (and so on, for higher pixel-ratios), falling back to the original graphic if it can't find a high-resolution version.

(And the same convention could be used to load video assets.)

Maybe there are server side techniques that could be put in place to automatically serve up the most optimized image for a given DPI. This would stop a bunch of 404s, but requires more work on the part of the server monkey.

This is good news for SVG and libraries like Raphael, who are well suited for scaling. When playing with an iPhone 4 it was amazing how quickly you noticed the bitmaps that were too low res... they stick out like a sore thumb. Expectations have changed.

What else can we do?

Kategorien: Google Reader

// My tweets