On jQuery's live()

January 22nd, 2010

Since the release of 1.4, there's been a lot of discussion about live(). Now that it's almost as fully featured as bind(), it's getting quite a workout.

One of the larger calls to action is to introduce $.live(selector,eventType,handler). There are two primary reasons I've seen behind this proposal:

  1. There is a performance hit for the initial selection in $('a.whatever').live(.. that isn't neccessary
  2. live() cannot work on any arbitrary collection of DOM elements in jQuery object, as a side effect of a very different internal operation, thus providing an inconsistent developer experience than all other $.fn.foo methods.

$.live for performance

Sizzle is used to find any matching elements before the live method discards them an only uses the jQobj.selector property and context. It's a waste, I agree. But you can avoid it. Zachleat posted this technique first. Then I stole a code snippet from @janl to show off in my jQuery Performance talk for jQCon. This inspired furf's jQuery.live() patch. And now jmar777 has resurrected that discussion.

I do not think the performance gain in $.live is worth adding it to jQuery core, by any means. Let's take an example.

Let's say we're dealing with $(selector).live('click',fn)

On every click on the page, the event is caught at the context.. the document. But evt.target points to the top-most element that was clicked on.
jQuery then traverses from evt.target through each ancestor of that node all the way up to document checking if (jQuery(elem).is(selector)).[source]

I think a reasonable estimated depth of evt.targets is like 8 (From the document to HTML element to BODY and so on.. a depth of 20 is not uncommon by any means). And lets say we have an average 5 clicks on a page.
That's 40 total checks against the selector (plus the original one done at bind time).
So in my opinion a 1/41 speed gain isn't worth changing the API.

Scoping the delegation a context, however has a much larger impact on performance: $(selector,elem).live('click',fn) Here, only clicks within the context are caught, so not only is the parent traversal depth much more shallow, but that entire sequence is avoided for all events occurring outside that area.

The current API to take advantage of this optimization requires code like $('a.awesome', $('#box')[0]).live(… Yuck! So I wrote a patch to deliver this same optimization to any ID-rooted selectors. (The perk being that everyone who learns #id-rooted selectors are fast for selecting get a related perf boost here). But as Justin Meyer pointed out in the comments, it's not as flexible as live() currently is.

$.fn.live cannot operate on any jQuery-wrapped collection of elements

$('a.foo').filter('.bar').live(... // fails
$('a.link').parent().nextAll().live(... // fails
$(this).live(... // fails

Quite simply, $.fn.live needs to be at the top of a chain to guarantee the .selector and .context properties of the jQuery object represent that set. Unlike other $.fn methods, live utilizes those properties and not the actual collection of elements that result from selection, traversing, and filtering.

$.live() and let $.die()

What are the possible solutions?

  1. Turn on a dirty flag in traversal methods, so if you try and live() that collection, it will throw an error.
  2. Deprecate $.fn.live and use $.live(selector[,context],type,handler)
  3. Use $.live to create a "live jQuery object" that defaults to live-bindings: $.live(selector,context).click(fn).mouseenter(fn).unbind("click") [source]
  4. Change $.fn.live to operate from a context: $(context).live('selector', event, fn) This bakes the performance benefit from above right in.

Any others? What do you guys think? Is there a solution that preserves backwards-compatibility while solving the issue?

btw- thx to jmar777, yehuda katz, ben alman, ajpiano, brandon aaron and julian aubourg for their great thoughts on the issue.

The people in this discussion got together and hashed out what would make sense. John Resig then landed a new delegate/undelegate API. It's good. :)

Updates from all around - Dec 2009

December 19th, 2009

A lot of the work I'm doing doesn't turn into new posts so here's a summary of what's been going on:

yayQuery

Since it was announced here, we've come a long way. We just put out our 7th episode (now in HD!). The show format has tightened up and we're putting a lot of time into making it even better. Follow us on twitter or subscribe to catch all the goodness. We also put out an entire episode devoted to what's coming in jQuery 1.4 (aka 1.MOAR), so peep it if you're curious.

jQuery Singalong plugin

I was a finalist at Boston's Music Hack Day with the jQuery Singalong plugin. It essentially allows you to add timed annotations to the HTML5 audio and video elements. (Also it was the first time I legitimately used javascript's Infinity :) In addition, I put out the (very!) alpha jQuery Bouncing Ball plugin. Check out the demos to get a taste

Modernizr updates

Faruk and I pushed out Modernizr 1.1, which can now detect what audio/video formats your browser supports, all sorts of HTML5 forms goodness, and some other ones like localStorage and applicationCache. We're delighted to be part of Mark Pilgrim's Dive into HTML5 book and pumped to help push the edge of progressive enhancement.

@font-face and webfonts

It's a rapidly changing landscape, so I've been keeping all my font-y posts up to date:

@font-face feature detection

I've updated the existing feature detection script to use a smaller file. (Thanks to the Font Squirrel for the help). You would use this script to detect if support is present or not; perhaps implement a Cufon fallback. These new improvements will make their way into Modernizr 1.2.

I've also added on a isFontFaceSupported browser sniffing alternative. The main benefit here is that you're guaranteed accurate results synchronously. (The true feature detection can't do that, though the new small size is much faster in Firefox.)

Other bits and bobs

The nice guys at that other jQuery Podcast had me on the show in November. I talked about, well.. basically all of the stuff above. :) It's a good show.

I tweeted about a new quick approach to a for loop:

for (var i = -1, len = arr.length; ++i < len; ) // the cool guy loop

The fun part is that the counting expression (the third part) is empty. It's faster than your standard loops, but reverse while() is still quicker. @jdalton beat me to this one, for the record.

Lastly, along with my writers, I've been keeping my mp3 blog Aurgasm fresh with new music you'll love. I've got a lot more things in the pipeline, so right after my Christmas and New Years jaunt around Germany, Denmark, and Scotland, you'll see them soon. Happy December ya'll.

Memorable hex colors

December 4th, 2009

Hopefully this will save you a trip back to Photoshop's color picker.

  • #b00b00
  • #de1e7e
  • #e1e100
  • #BADA55
  • #F0FEAF
  • #ac1d1c
  • #facade

These ones are memorable, but not terribly color-appropriate:

  • #c0ffee
  • #defec8
  • #deface
  • #0ff1ce
  • #a55

Thx for the awesome suggestions: @mrspeaker, @8centsaday, @wilto, @lrbabe, @twalve
If you have any good ones to add, holler in the comments!

I guess I have a thing for hex colors.. ;)

Announcing the Type Rendering project

November 30th, 2009

A short while ago, I pulled together some bright minds in the emerging web fonts scene: Tim Brown of Nice Web Type, Zoltan Hawryluk, and Ethan Dunham of Font Squirrel. I thought it would be wise to share notes and begin talking about cross-platform/browser type rendering (that lasagna of complexity), in order to clarify the entire process for ourselves and others and find ways to make web type look better.

Basically, I want @font-face text to look damn good.

Now we've set some goals and would like to open up the dialogue. We'll need your participation, and lots of expert advice, to fulfill these intentions. Follow Type Rendering on Twitter.

Check out How to Detect Font-Smoothing Using JavaScript to learn about an algorithm Zoltan has developed for determining whether a visitor's web browser uses font smoothing technology when rendering type.

If you want to see rich typography succeed, tell folks you care about how type looks on the web.

We'll be exploring all the layers involved in making sure web fonts are implemented in the best possible way. We'll need your help, too.

Infinite Scroll 1.4 is out. Twitter-style now supported!

November 30th, 2009

I just released version 1.4 of the jQuery Infinite Scroll plugin. Along with that comes a new release of the wordpres plugin. A few small bugs were fixed in the wordpress code, improving compatibility with other plugins and themes.

I've also added the ability to manually trigger loading in new content. This allows you to do a twitter or facebook style of interaction where the user must click an element for the new data to come in.

A demo showing that functionality. The documentation of how to use this style is on the documentation page.

Introducing yayQuery - A jQuery podcast

November 6th, 2009

A few friends of mine got together to put together a new podcast we're calling…

yayQuery!

2009.11.13. Now visit the podcast at:
http://yayquery.com

In our inaugural episode I'm joined by Rebecca Murphey, Alex Sexton (of goto.js fame), and Adam Sontag (ajpiano). We talk about…

  • Underscore.js - a great utility belt (very handy for Ruby/Python folks), comes with John Resig's microtemplating script and lots more
  • Thickbox - Rest in peace. Also alternatives: Colorbox, jQuery UI Dialog
  • jQuery on mobile. Phonegap, XUI, jQTouch, going it alone
  • Anti-Pattern of the week: css(key,val)
  • $var and Hungarian notation
  • … and dancing

Moar?

We hope to do this again (and again) and make things better. We're also working on proper podcast feeds and a site.

But really this is an experiment, for now. We'd love any and all feedback.

… and follow us on twitter to get your fix!! @yayquery

Fighting the @font-face FOUT - Quicken the load time

October 7th, 2009

FOUT is what I'm calling the flash of unstyled text that you get while using @font-face in Firefox and Opera.

In June, Remy Sharp documented the how a browser progressively renders a page using @font-face. Things work differently between browsers natch:

Here's how in Firefox; basically the text is in a default webfont until the custom font is ready:

Webkit takes a very different approach, and very intentionally. They believe it's better to keep the text invisible until the font is ready. This way, there is no moment where the text flashes into its newly upgraded self. (This moment should be familiar to you if you've used sIFR)

I really don't like the text upgrade FOUT, so I personally prefer webkit's technique. But either way, we want the font loaded ASAP, so let's speed it up!

Best practices for serving fonts:

  • Minimize the overall kilobyte size of your font file. You can reduce the size of your font file by subsetting it (more on this later).
  • Heavy caching via a far-future expires header.
  • Gzip? Well, no; you can't gzip a font file, though you can gzip a css file that holds the data-uri representation, but you don't get much gain there. It'd primarily be an obfuscation technique.Stoyan Stefanov has done some excellent research into @font-face and gzip. Summary: It's possible! 40-45% savings. Do It!

When exactly do browsers download the font file?

Garrick at Kernest tipped me off to IE's interesting behavior here.

After some research we can see when the asset download is initiated:

  • IE will download the .eot file immediately when it encounters the @font-face declaration.
  • No browsers download the font file when they find a css rule that references the @font-face font.
  • Gecko, Webkit, and Opera all wait until they encounter HTML that matches a CSS rule with a fontstack including the @font-face font.

I've put up a test page where you can experiment and watch your dev tools to see when the file is grabbed.

In what cases will you get a FOUT
  • Will: Downloading and displaying a remote ttf/otf/woff
  • Will: Displaying a cached ttf/otf/woff
  • Will: Downloading and displaying a data-uri ttf/otf/woff
  • Will: Displaying a cached data-uri ttf/otf/woff
  • Will not: Displaying a font that is already installed and named in your traditional font stack
  • Will not: Displaying a font that is installed and named using the local() location

Let's get the font ASAP

The sooner the better, so let's prioritize getting this font before everything else.

We'll set up our @font-face declaration:

    /* chunk will load immediately in IE at this declaration*/
    @font-face {
    	font-family: 'ChunkFive Regular';
    	src: url('fonts/Chunkfive.eot');
    	src: local('ChunkFive Regular'), local('ChunkFive-Regular'), url('fonts/Chunkfive.otf') format('opentype');
    }
    /* define a class that uses this font */
    .chunk { font-family:'ChunkFive Regular'}

And inside the <head>, we'll include this:

(function(className){
    // quit early if we're in IE, no need to do any of this.
    if (/*@cc_on!@*/0) return;
 
    var f = document.createElement('fontdl');
    f.innerHTML = 'fontdl';
    // associate with the @font-face declaration and hide it
    f.className = className;
    f.style.cssText = 'position:absolute; visibility:hidden'
    // it's still off-DOM so it doesn't download yet
 
    // document.body doesnt exist yet so we'll add it onto the HTML tag
    document.documentElement.appendChild(f);
    // font download initiated Now.
    // let's clean up after ourselves (opera needs a timeout > 0)
    setTimeout(function(){f.parentNode && f.parentNode.removeChild(f)},100)  
})('chunk'); // <== pass in the class here.
2009.10.10: Based on a tip from my colleague Adam McIntyre, we have a more elegant solution than the above javascript. It, along with more fontstack research, is as follows…

Rather than creating an element on the fly that uses the font, we do the same with HTML:
Adam postulated the we could do the same trick by classing the HTML tag:
(e.g. <html class="chunk">)

  • The font-family style won't actually cascade, so no worries about it being inherited by your content
  • This works in Gecko and Opera, but not Webkit

Alternatively, we add an applicable element to the head:
<b class="chunk" style="position:absolute; visibility:hidden">download please</b>

  • Works in webkit, gecko, opera. Waa hoo!
  • Obviously this doesn't validate. FYI, The element will be thrown into the <body> on page load.

Can I pre-load all the font assets?

So let's say we have more than one @font-face declaration:

@font-face { font-family: 'ChunkFive Regular'; src: local('ChunkFive Regular'), url('fonts/Chunkfive.otf') format('opentype'); }
 
@font-face { font-family: 'League Gothic'; src: local('league gothic'), url('fonts/LeagueGothic.otf') format('opentype'); }

If we set a new class that references both new fonts in a single fontstack, and then pass use that class for our above techniques:

  • Gecko retrieves all webfonts mentioned in the font stack.
  • Opera retrieves all webfonts mentioned in the font stack.
  • IE, as mentioned, retrieves them when they're declared, not used.
  • Webkit retrieves each font mentioned sequentially until it finds a working one.
    • 404s and invalid files are considered non-working, of couse.

So with some CSS like so:

    .chunk { font-family:'ChunkFive Regular'}
    .league { font-family:'League Gothic'}
    .allfonts { font-family: 'ChunkFive Regular', 'League Gothic'; }
<!-- preloads both fonts in gecko and opera, webkit only gets the first -->
<b class="allfonts" style="position:absolute; visibility:hidden">download please</b>
 
<!-- preloads all the fonts in the fontstack in gecko, opera, and webkit -->
<b class="chunk" style="position:absolute; visibility:hidden">download please</b>
<b class="league" style="position:absolute; visibility:hidden">download please</b>

Is this the end-all be-all solution to quick load and FOUT?

Nope. As Jonathan Snook pointed out in the comments, these won't elimate seeing the fallback font FOUT in Gecko and Opera, they only prioritize the load of those fonts. As we know, browsers have a limit of concurrent connections, so we're using these tricks to get the fonts first in line.

Also, This is really only for the initial time, because after that, your far-futures expire header means the ttf stays cached locally, no more requests needed.

I'm not sure if we'll be able to use webkit's transparent font load in Gecko in any graceful way. (It's possible with a sniff and polling, but that seems like overkill) I'm also not sure if we'll get Gecko's load technique in Webkit, which would be optimal for slow/mobile connections. For the time being your time is best served getting the font size very small, gzipping it, prioritizing it first, and caching it for a while.

Defeat the Firefox FOUT entirely

A little bit ago, Typotheque posted a technique aiming to avoid the FOUT. Using jQuery, they hide the body on dom ready, and then reveal it at the window load event.

The posted technique doesn't work as:

  • It targets all users, but we should only tweaks things for Firefox 3.5+ users.
  • Users will actually see the text before it's hidden during at dom ready.
  • As was mentioned earlier, fonts are downloaded when text appears in the page that the font will apply to. Therefore, anything hidden with display:none will not request the font file.
  • Not everyone has jQuery, so let's go with something more general

The one serious caveat to this technique is: The page will not be visible until all content, iframes, remote scripts, fonts, and images are downloaded. for a maximum of three seconds (I added a three second bailout condition, read below.)

This should run in the <head> somwhere:

(function(){
  // if firefox 3.5+, hide content till load (or 3 seconds) to prevent FOUT
  var d = document, e = d.documentElement, s = d.createElement('style');
  if (e.style.MozTransform === ''){ // gecko 1.9.1 inference
    s.textContent = 'body{visibility:hidden}';
    e.firstChild.appendChild(s);
    function f(){ s.parentNode && s.parentNode.removeChild(s); }
    addEventListener('load',f,false);
    setTimeout(f,3000); 
  }
})();

First, we are detecting if we're firefox 3.5+ by seeing if -moz-transform is supported, which was added at the same time. We use visibility:hidden instead of display:none, so that the font will actually be requested, and we remove that style once the page has finished loading. We're hinging on window load to be our re-entry point, because as Steve Souders pointed out, "font files block the window’s onload event from firing in IE and Firefox, but not Safari nor Chrome."

I've also added a 3 second bailout condition; this means if the page has not completely loaded in three seconds, we're going to show it anyway. It's possible the font won't be ready, but unlikely, I believe. This aims to solve the issue Remy found with the Standards.next site. I wouldn't recommend it, but you can disable this behavior by commenting out the setTimeout line.

2009.11.07. Added the Defeat the Firefox FOUT section.

2009.11.08. Tweaked defeat FOUT code to have a 3 second bailout.

2009.12.14. Added the In what cases will you get a FOUT section.

Chrome and @font-face: It's here!

September 25th, 2009
January 25, 2010: It is now in the stable Chrome release! The wait is over, everyone. :) [release notes]

Today, Zeldman issued an ultimatum that we've all been feeling recently:
Chrome needs to support @font-face immediately.

It's true, every major browser supports @font-face:

  • Internet Explorer: since IE5
  • Firefox: since FF3.5
  • Safari: since Safari 3.2
  • Opera: since Opera 10

But..
Google Chrome currently does not enable @font-face linking to ttf and otf.

It actually does support SVG fonts in a @font-face declaration. View this demo in Chrome or Chromium to see svg fonts in action. Divya has some great research around fonts and SVG if you're interested in more.

You can turn it on, at will

You can enable web fonts with an executable switch: −−enable-remote-fonts.

On Windows, you can tweak the shortcut path, like so.
With Chromium on OS X, you use Terminal to launch:

/Applications/Chromium.app/Contents/MacOS/Chromium --enable-remote-fonts

With proper Google Chrome on OS X, you need to escape the spaces:

/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --enable-remote-fonts

With Chromium on Linux[1]:

chromium-browser --enable-plugins --enable-remote-fonts %U

Once you enable it, all @font-face stuff works just as you'd expect, including the bulletproof @font-face syntax [demo] and the Nice Web Type demos.

But we need web fonts working by default

Wait, so why is this wonderful feature disabled by default? Security review. Ian Fette, the program manager of Chrome, indicated that they need to explore how do webfonts in a "reasonably safe manner". So that's the holdup.
This ticket on the Chromium bug tracker tracks the feature being enabled by default.

Okay, so when do we get our toy? Soon, in fact!

Chrome is my default browser, so my patience on this issue ran dry recently. I emailed Takuya, an engineer at Google Japan, who is working on this and he said:

We are almost done. The code is under review internally within Google. I'm expecting to make it public no later than a week or so.

So I expect we'll be seeing @font-face support enabled by default in the Chrome dev builds soon. Based on their release schedule I think it'll be near the end of the year when we see it in Chrome stable. That's good news; let's hope we see it sooner.

2009.10.19 : The ticket tracking @font-face on by default gained the Milestone-4 label, indicating it will be included in Chrome 4 Stable.

2009.11.04: The Chromium team has released ots - an OpenType sanitizer library meant to clean any security concerns from a font file included via @font-face. They have also filed a bug upstream with WebKit to integrate this code at the Webkit level. Finally we see the fruit of their security review.

2009.11.18: Remote fonts are now enabled by default!!! This should only be in the dev builds for now. I'll update when it makes it to beta builds and eventually the stable. But right now it still looks like we'll see this in version 4 stable. (Nov 21: confirmed its in dev builds.. starting with version 4.0.249.4 [blog post], [rev 32300])

2009.12.09: @font-face support is now in the Chrome Beta of both Windows and Mac.

2010.01.25: @font-face support is now in the Windows Chrome Stable release! [release notes]

Avoiding the FOUC v3.0

September 23rd, 2009

FOUC is an unwelcome guest to your soirée of intertube funtimes. He comes in and distracts users eyes with things they shouldn't be seeing, and then departs ever so quickly. We don't like him.

So you've likely seen code like this:

<body>
  <script>document.body.className += 'js';</script>

No good. Scripts in the body block rendering so we can do better[1]:

<head>
  <script>document.documentElement.className += 'js';</script>

Here we'll be adding the class to the HTML element instead of the body, which we have access while we're in the HEAD. (Naturally CSS works just fine with a html.js selector, though this doesn't validate in HTML4)

But here's my big hang-up:
I prefer to write unique css for the no-javascript user. I don't want to be writing .js in front of every selector for my basic accordion/carousel/etc widgets. It's terribly tedious. I really just want a .no-js hook.

My solution:

<html class="no-js">
<head>
  <script>(function(H){H.className=H.className.replace(/\bno-js\b/,'js')})(document.documentElement)</script>

The markup has a no-js class on HTML by default but we'll very safely change that to 'js' inside the head. That compressed line of javascript is basically:

document.documentElement.className = document.documentElement.className.replace(/\bno-js\b/,'js');

But I make it small as it's one of the only scripts that should run in your head. Because everyone has their scripts near their </body> tag, right?

We use the same approach in Modernizr, because we want the classes to be all set on the HTML element right when the BODY content starts loading in. Fight the FOUC!

jQuery Conference 2009

September 13th, 2009

Currently rounding up the last day of #jqcon and it's one of the best events I've been too. Crazy to see jQuery's annual meetup double in size each year and scale with talent and organization just as nicely. I somehow tricked the attendees into voting two talk proposals up (thanks!), so I got to discuss two things this weekend.


I'd love to any feedback you may have and plan to write on these topics in more detail, but for now.. my slides:

jQuery Anti-Patterns for Performance & Compression



Employing Custom Fonts… Now!