planet.jquery.com

RSS Feed
$('.blog[@tag=jquery]').read();

Doctorate on jQuery.SerialScroll

Posted August 06, 2008 by Ariel Flesler

Introduction

After replying to a large amount of comments and emails about jQuery.SerialScroll , I decided to comment some more about this plugin, also to publish some snippets, to achieve commonly desired effects or behaviors.

This should save you (and me :)) some time. It might also show you some additions, that you haven't considered.

I'll also try to show some more model calls to the plugin, so you can unattach yourself from those in the demo.



A little bit of theory

Before the snippets, I'll go through the basics, to refresh your mind.

Calls to the plugin
It can be done on two different kind of elements
  • Element to be scrolled: This is what you normally do when you want one single instance of SerialScroll in a page.
    $
    ('#pane'
    ).serialScroll({
    //...
    
    });
    
    
    
  • Container: You might want to create many "SerialScrolls" in the page, you don't need to call it many times, just put the option 'target' to work.
    $
    ('div.container'
    ).serialScroll({
    //...
    
    target:'div.pane'
    ,
    //...
    
    });
    
    
    
    
    
    This will allow you to have many divs with class container, which contain a scrollable div with class pane. They don't need to be divs .

    When doing this, the selectos for the arrows also become relative to the container, instead of absolute.
  • Global call: If by chance, you want to scroll the window, you'll need to use this approach.

    $
    .serialScroll({
    //...
    
    });
    
    
    
    If, for some reason, you need to retrieve this implicit element, call:
    $
    .scrollTo.window();
    
    This will return you the element, don't use window or document.
onBefore
This setting, which is a callback, will empower some snippets, so you better learn about it.
$
('div.container'
).serialScroll({
//...

onBefore:function
( e, elem, $pane, $items, pos ){
//...

},
//...

});







Note that:
  • The 'this' is the element that triggered the event.
  • e is the event object.
  • elem is the element we'll be scrolling to.
  • $pane is the element being scrolled.
  • $items is the items collection at this moment.
  • pos is the position of elem in the collection.
  • if it returns false, the event will be ignored.
  • Those arguments with $ are jqueryfied.
The onAfter, only receives 'elem' as the first argument, and the 'this' is the element being scrolled ($pane but not jqueryfied).

The snippets

Now, what you really want, the code required to do all those fancy things, that the plugin doesn't do natively.

One note, all the snippets are wrapped with a document ready and they use symbolic ids. Needless to say, you don't need to copy the ids, or even USE ids.

Only the relevant settings are specified, so you will see "..." reminding that it's incomplete code.

You configure the selectors and the rest of the settings, according to your needs.



  • Use constant auto-scrolling. get new
  • Hide the arrows when the limits are met. get
  • Stop the auto scrolling on hover. get
  • Scroll from right to left (or bottom to top). get
  • Manipulate the widget using the keyboard. get
  • Generate a pager based on the items. get


Concluding

I hope this helped and saved you some time, I also hope you learnt something new.

I plan to add more snippets as they come up from requests.

If you have any doubt, don't hesitate to ask.



Update 6/25/08
Added the constant scrolling snippet

jQuery Documentation Alternatives

Posted July 29, 2008 by Karl Swedberg

As many of you have discovered by now, jquery.com and its subdomains have been offline intermittently over the past several weeks. On behalf of the jQuery Project Team, I apologize for any inconvenience this has caused.

John Resig is aware of the problem and is working with the hosting company to get things resolved as quickly as possible. In the meantime, Remy Sharp, who runs jQuery for Designers , has graciously provided a few alternatives for API documentation:

He also has the raw XML file of the jQuery documentation in an SVN repository hosted at googlecode.com.

Jörn Zaefferer also has a jQuery API Browser that lets you browse by category as well as alphabetically. Also, it can be downloaded as a zip file .

Many thanks to Remy and Jörn for these great resources.

Moving demos to demos.flesler.com

Posted July 29, 2008 by Ariel Flesler

New host

I just wanted to point out that I'm slowly migrating the demos from freewebs.com to demos.flesler.com.

I bought the domain a week ago or so, but didn't have the time to move them all.

I'm also "PHPizing" them a bit, so it takes some extra time.



Moved Demos

I'll update existing links on the blog and will redirect the demos from freewebs(eventually).

If you want to know which ones are ready, here's a list

jQuery Plugins
Regular Scripts

What else ?

I also plan to have the blog on blog.flesler.com.

I tried for 5 minutes, but something went wrong in the middle. The redirection didn't work, so I reverted. I will try soon, after I do some research.



I'll try to be very careful not to break the demos while I add some more to the mix, if you see a failure please let me know. You can fall back to freewebs for now.

Don't forget to update your bookmarks :D .

Image Fade Revisited

Posted July 28, 2008 by Remy
This episode is revisiting the image cross fade effect, in particular Dragon Interactive has a beautiful little transition for their navigation that some readers have been requesting. Greg Johnson takes it one step further to implement this method using jQuery and the methods shown here. Watch the image fade revisited screencast (alternative flash version) QuickTime version [...]

jQuery.Listen 1.0 released

Posted July 26, 2008 by Ariel Flesler
I updated jQuery.Listen and it got to it's first stable version. This release includes mostly features. A few optimizations and a couple of structural changes.

As specified in the change list, the licensing changed from GPL to GPL+MIT.



Optimizations

  • Reduced the code size, this release (with all its features) is smaller than the previous one.
  • Added many optimizations for minified code size.
  • Improved the cleaning done on window.onload to avoid memory leaks.

Fixes

  • The [].splice.call(arguments,..) wasn't working in my Opera.

Changes

  • $.listen is not used for stopping/restarting the indexers.

    use $(..).indexer(...).(start|stop)() instead.
  • Changed the licensing from GPL to GPL+MIT.

Features

  • Added Jörn Zaefferer's approach of focusin/focusout. You can now safely listen for blur and focus events.
  • Added $(..).indexer( event_name ) to get access to the indexers of the first matched element and $.indexer(..) to the global indexer.
  • $.listen.fixes is a hash that maps event/fixedEvent. If you add a fix at $.event.special, add it to this hash and it will work automatically.
  • It's possible to instruct indexers to emulate the natural bubbling like this: $(..).indexer( ... ).bubbles = true, use with discretion, will hit on perfomance.
  • Added support for multiple events separated with spaces.
Kudos to Jörn Zaefferer for lending me the code that now allows listening for focus and blur!

Links

Downloads

I really advice using the minified versions. The code is optimized to those releases. Source versions should only serve to learn.

jQuery.Preload

Posted July 26, 2008 by Ariel Flesler

Introduction

This is an advanced multi-functional preloader, that has 4 modes integrated. Each mode suits a different, common situation.



Modes and settings

The modes are:
  • URL
    Preload a JS array of static URLs.

     related settings :
    •  base: This string is prepended to the URLs.
    •  ext: This string is appended to the end of each URL in the array.
  • Rollover
    Preload based on DOM images, slightly modifying their src.  Can be used for rollovers, or for image-thumb.

     related settings :
    •  find: String or regex that matches part of the srcs.
    •  replace: Replacement to the matched part, to generate the alternate URL.
  • Placeholder
    Take regular images and set a placeholder image while they load. Show each original image when fully loaded. Allows sequential loading with a threshold.

     related settings :
    •  placeholder: URL of the temporal image shown while loading.
    •  notFound: Optional image to show if a given image failed to load.
  • Link
    Preload images that appear in the href matched links.

     related settings : none.
There's also a threshold setting, that determines how many images are preloaded simultaneously, it is 2 by default.



Placeholder+Rollover Mode
Since 1.0.6, you can combine these 2 modes, for another common use.

If you have many images in a page, you might want to load a lighter version of them first, and then sequentially load and replace the real images.

To achieve this, use the light versions in the html, then preload the heavy ones as if they were rollover images(find+replace).

If you set 'placeholder' to true, each preloaded image will be set instead once it loads.



Callbacks

The hash of settings can also contain callback functions for 3 important moments of the preloading proccess.

They receive a hash of data, with information about the related image and the overall process.

The callbacks are:
  • onRequest
    Called when requesting a new image.
  • onComplete
    Called when a request is complete.
  • onFinish
    Called when all images are fully preloaded.

Hash of data for the callbacks

The first argument of the callbacks will be a hash.

It contains the following information:
  • loaded
    how many images were preloaded successfully.
  • failed
    how many images failed the preloading.
  • next
    0-based index of the next image to preload.
  • done
    amount of preloaded images ( loaded + failed ).
  • found
    whether the last image could be preloaded or not.
  • total
    amount of images to preload overall.
  • image
    URL of the related image.
  • original
    The original source related to this image.

Troubleshooting

Since 1.0.5, this plugin works well on Safari 2.

If you preload +15 images with jQuery, IE throws an "stack overflow" error.

I worked around this limitation of IE on 1.0.6.

If you still get this alert, you'll need to modify $.preload.gap , which is 14 by default. Reduce this number by 1 until it works. You need to exit the site each time, to be sure it works.

Try not to touch the original code, modify it from the outside.



Links

Downloads

Update 3/6/08
Added 1.0.5, patched the bug of Safari 2

Update 3/10/08
Added 1.0.6, patched IE's stack overflows and more.

Added placeholder+rollover mode and troubleshooting.


Update 3/12/08
Added 1.0.7, onAbort is checked too.

jQuery.SerialScroll

Posted July 26, 2008 by Ariel Flesler

Introduction

This plugin allows you to easily animate any series of elements, by sequentially scrolling them. It uses jQuery.ScrollTo to achieve the scrolling animation. It is a very unrestricted plugin, that lets you customize pretty much everything from outside. You can use horizontal or vertical scroll, also combined.



What's it for ?

jQuery.SerialScroll doesn't have one definite purpose. It's generic and adaptable. You can certainly use it as a screen slider . That is, to sequentially navigate a group of screens.

This plugin can also animate a text scroller in no time.

It can definitely handle slideshows , the high customizability of the scrolling effect lets you create beautiful animations.

You can even build an automatic news ticker !

Three of these uses are exemplified in the demo.

Remember, it's not restricted to these situations. It will take care of any collection of html elements that you want to scroll consecutively.



Settings and customization

jQuery.SerialScroll gives you access to a lot of options.

These are:
  • target
    The element to scroll, it's relative to the matched element.

    If you don't specify this option, the scrolled element is the one you called serialScroll on.
  • event
    on which event to react (click by default).
  • start
    first element of the series (zero-based index, 0 by default).
  • step
    how many elements to scroll each time. Use a negative number to go on the other way.
  • lock
    if true(default), the plugin will ignore events if already animating. Then animations can't be queued.
  • cycle
    if true, the first element will be shown after going over the last, and the other way around.
  • stop
    if true, the plugin will stop any previous animations of the element, to avoid queuing.
  • force
    if true, an initial scroll will be forced on start.
  • jump
    if true, the specified event can be triggered on the items, and the container will scroll to them.
  • items
    selector to the items(relative to the scrolled element).
  • prev
    (optional)selector to the 'previous' button.
  • next
    (optional)selector to the 'next' button.
  • lazy
    if true, the items are collected each time, allowing dynamic content(AJAX, AHAH, jQuery manipulation, etc).
  • interval
    If you specify a number, the plugin will add auto scrolling with that interval.
  • constant
    Should the speed remain constant, no matter how many items we scroll at once ? (true by default).
  • navigation
    Optionally, a selector to a group of elements, that allow scrolling to specific elements by index. Can be less than the amount of items.
  • excludenew
    If you want the plugin, to stop scrolling before the actual last element, set this to a number, and that amount of items is ignored counting from the end.

    This is useful if you show many items simultaneously, in that case, you probably want to set this option to the amount of visible items - 1.
  • onBefore
    A function to be called before each scrolling. It receives the following arguments: event object, targeted element, element to be scrolled, collection of items and position of the targeted element in the collection.

    The scope(this) will point to the element that got the event. If the function returns false, the event is ignored.
Also, you can use jQuery.ScrollTo's settings!

Check its demo to see all of them.



The option 'target'
This option is a new addition, included since 1.2.0.

Before, you needed to call the plugin once for each scrolled element.

When this option is specified, the matched elements are no longer the scrolled elements, but a container.

In this case, the selectors of prev, next, navigation and target will be relative to this container, allowing you to call SerialScroll on many elements at once.



External manipulation, event triggering

jQuery.SerialScroll automatically binds 3 events to the containers.

These are:
  • prev.serialScroll
    Scrolls to the previous element.
  • next.serialScroll
    Scrolls to the next element.
  • goto.serialScroll
    Scrolls to the specified index, starts with 0.
  • start.serialScroll
    (Re)starts autoscrolling.
  • stop.serialScroll
    Stops the autoscrolling.
  • notify.serialScroll
    Updates the active item.
This looks confusing, but it's not. You use it like this:
$
(container).trigger( 'prev'
 );
$
(container).trigger( 'next'
 );
$
(container).trigger( 'goto'
, [
 3
 ]
 );
$
(container).trigger( 'start'
 );
$
(container).trigger( 'stop'
 );
$
(container).trigger( 'notify'
, [
 4
 ]
 );
'notify' also accepts a DOM element(item), or any of its descendants.

$(container) is the element that gets scrolled each time. If you specified a 'target', then that element, else, the element you called the plugin on.

Note that to use 'start' and 'stop' you need to use the option 'auto' first.

If your container element already has any of these event names bound(odd!), then just add the namespace when you trigger.

You probably won't need to deal with these events, but if so, this is how.



What makes jQuery.SerialScroll so special ?

This plugin has many positive features, of course, it won't fit everyone's needs. That's impossible.
  • Small Footprint
    This plugin is tiny, as said before, it requires jQuery.ScrollTo. Both plugins together, take less than 3.5kb minified .

    If by chance, you decide to include jQuery.LocalScroll , the 3 of them require less than 5kb . Including this plugin is not a bad idea, it can be used, instead of the option 'navigation' to build a widget with sequential and random scrolling.
  • Highly customizable
    This plugin has many settings to customize, in addition, it can use jQuery.ScrollTo's settings. That makes 27 different options!

    If you take a while to analyze them all, you can make your work really unique.
  • Accessible, degrades gracefully
    Probably many will automatically skip this part, shame on you!

    If you make sure non-javascript users will see the scrollbars, then they can perfectly navigate your content. You can show the scrollbars only for these few users, easily, using css/js.

    This is one of the main differences with many similar scripts, they generate the content and the styling using javascript.
  • Adaptable
    jQuery.SerialScroll won't alter the html or styles at all .

    You are in control of the styles and content of your collections. You don't need the plugin to decide what html to use, or how many items to show simultaneously, and you can safely change that yourself, the plugin will always work.

    The items don't need to have fixed size, nor to be aligned . SerialScroll will scroll from one to the other, no matter what.

    If you want a plugin with premade styles or automatic generation of html, then you should consider any of jQuery carousels.
  • Generic and reusable
    Finally, as mentioned before, this plugin can be used for many different situations and doesn't have one specific application.

Links

Downloads

I really advice using the minified versions. The code is optimized to those releases. Source versions should only serve to learn.



Update 3/7/08
Added 1.1.2, start and stop events. Allows negative step.
Update 3/10/08
Added 1.2.0, major release, updated everything.
Update 3/20/08
Added 1.2.1, added 'exclude' and enhancements.
Update 4/28/08
Added a link to "Doctorate on jQuery.SerialScroll".

jQuery.SerialScroll 1.1 released

Posted July 26, 2008 by Ariel Flesler
I added a major release of jQuery.SerialScroll. It doesn't have that many changes, but I really feel it made one step ahead.

Optimizations

  • The animation is skipped if a bad position was received or it's the same as the actual. Saving some overhead.

Changes

  • Changed the licensing from GPL to GPL+MIT.

Features

  • The plugin binds 3 events to the container to allow external manipulation. They are clearly explained in the main post, please check it.
  • Added 2 more arguments to the onBefore callback: actual item collection, index of the actual item in the collection.
  • Added option 'interval', can be a number specifying the amount of milliseconds for autoscrolling.
I upgraded the main post and it has extensive and detailed documentation. Also added some more text and options to the demo.

Links

Downloads

I really advice using the minified versions. The code is optimized for those releases. Source versions should only serve to learn.

jQuery.LocalScroll 1.2 released

Posted July 26, 2008 by Ariel Flesler
A new major update of jQuery.LocalScroll has seen the light.

Two minor releases were added after it and is now at 1.2.2 . I'll detail them all together:

Optimizations

  • Replaced a $('[name='+name+']') for a document.getElementsByName(name) to critically improve perfomance.
  • Small improvements to make the code shorter.

Fixes

  • The last argument received by onBefore when scrolling the window, is no more $(window) but the real element being scrolled.

Changes

  • Renamed the option 'persistent' to 'lazy', the latter seemed more adequate. Using 'persistent' will still work (backwards compatibilty)

Features

  • Added the option 'stop', if true (default), each event will stop any previous animations of the target.
  • Added the option 'lock', if true, the plugin will ignore events if already animating.
  • Added $.localScroll.hash( settings ); which will scroll to the given element if the URL contains a valid hash.
  • Removed the option 'cancel' that wasn't working well, and added the option 'hash'. It does what 'cancel' was meant to do, but in a different way.

    After a scroll, the hash( #some_id ) of the link is added to the URL.

    Note: This setting is not compatible with options like offset and margin, as the browser will natively scroll in the end.

    If you use the option 'target'(to scroll an overflowed element) and the window has overflow, setting the hash will scroll the window as well. So my advice is:

    only use 'hash' when scrolling the window.
jQuery.ScrollTo is now at 1.3.2 , it has a new option called 'over', check its demo to see it in action.

jQuery.LocalScroll 1.2.x requires jQuery.ScrollTo 1.3.1 or higher.



Links

Downloads

I really advice using the minified versions. The code is optimized for those releases. Source versions should only be used to learn.

jQuery Tip - Getting Select List Values

Posted July 22, 2008 by Marc Grabanski

Chris Hartjes asked me a simple question today, but it is worth noting because I have asked the same before, "How do you get the current value from a select list?"

To get the current value is very simple using val() .

$('#selectList').val();

But sometimes you may need to get the selected option's text. This is not as straight forward. First, we get the selected option with :selected selector. Then once we have the option, we can get the text with the function, text() .

$('#selectList :selected').text()

View Demo:

Note on July 23 @9:14AM: HoyaSaxa93 wrote in to ask how to get values from select multiples. I will create the demo and code below. This would be how to set a select multiple to an array called, "foo".

var foo = [];
$('#multiple :selected').each(function(i, selected){
	foo[i] = $(selected).text();
});

DOM DocumentFragments

Posted July 21, 2008 by John Resig

I was playing around with DOM DocumentFragments recently, in JavaScript, seeing what I could make with them. Roughly speaking, a DocumentFragment is a lightweight container that can hold DOM nodes. It's part of the DOM 1 specification and is supported in all modern browsers (it was added to Internet Explorer in version 6).

In reading up on them I came across an interesting point, from the specification:

Furthermore, various operations -- such as inserting nodes as children of another Node -- may take DocumentFragment objects as arguments; this results in all the child nodes of the DocumentFragment being moved to the child list of this node.

This means that if you take a bunch of DOM nodes and append them to a fragment then you can simply append the fragment to the document, instead (and achieve the same result - as if you had appended each node individually). I instantly smelled a possible performance improvement here. I investigated a little bit further and noticed that DocumentFragments support the cloneNode method, as well. This provides all the functionality that you need to highly-optimize your DOM insertion code.

I set up a simple demo to test the theory.

Let's take the situation where you have a bunch of DOM nodes that you need to append into the document (in the demo it's 12 nodes - 8 at the top level - against a whole mess of divs).

var elems = [

        document.createElement ( "hr" ) ,

        text( document.createElement ( "b" ) , "Links:" ) ,

        document.createTextNode ( " " ) ,

        text( document.createElement ( "a" ) , "Link A" ) ,

        document.createTextNode ( " | " ) ,

        text( document.createElement ( "a" ) , "Link B" ) ,

        document.createTextNode ( " | " ) ,

        text( document.createElement ( "a" ) , "Link C" )

] ;

function text( node, txt) {

        node.appendChild ( document.createTextNode ( txt) ) ;

        return node;

}

Normal Append

If we wanted to append these nodes into the document we would probably do it in this traditional manner: Looping through the nodes and cloning them individually (so that we can continue to append them all throughout the document).

var div = document.getElementsByTagName ( "div" ) ;

for ( var i = 0 ; i < div.length ; i++ ) {

        for ( var e = 0 ; e < elems.length ; e++ ) {

                div[ i] .appendChild ( elems[ e] .cloneNode ( true ) ) ;

        }

}

DocumentFragment Append

However, when we bring DocumentFragments into the picture we can immediately see a different structure. To start we append all our nodes into the fragment itself (built using the createDocumentFragment method).

But the interesting point comes when it's time to actually insert the nodes into the document: We only have to call appendChild and cloneNode once for all the nodes!

var div = document.getElementsByTagName ( "div" ) ;

var fragment = document.createDocumentFragment ( ) ;

for ( var e = 0 ; e < elems.length ; e++ ) {

        fragment.appendChild ( elems[ e] ) ;

}

for ( var i = 0 ; i < div.length ; i++ ) {

        div[ i] .appendChild ( fragment.cloneNode ( true ) ) ;

}

Setting some time stamps we can see our results pay off in spades:

Browser Normal (ms) Fragment (ms)
Firefox 3.0.1 90 47
Safari 3.1.2 156 44
Opera 9.51 208 95
IE 6 401 140
IE 7 230 61
IE 8b1 120 40

As it turns out: A method that is largely ignored in modern web development can provide some serious (2-3x) performance improvements to your DOM manipulation.

Offset Performance Teaser

Posted July 19, 2008 by Brandon Aaron

The offset method in jQuery is a bit slow … okay … really slow. :) I’ve been working on a new version of the offset method from the ground up largely focusing on performance. I’ve made some good progress and just had to share the results thus far!

The test case is simple. I have a div within a div and both divs have margin, border, padding. I first run the original offset method from jQuery 1.2.6 200 times. Then I run my new version 200 times. The original offset method took 260.823ms to run and the new offset method only took 56.222ms! Not to mention that as it stands right now I’ve cut the code in half from over 90 lines to just over 40 lines. Here is a screen grab of the Firebug profiler.

offset stats

I can’t wait to get this method finished up and into the core!

Make a "Scroll To Next Article" Button with jQuery

Posted July 18, 2008 by Marc Grabanski

If you view my articles page, you will see a, "next" button on the bottom left of the browser window that looks like this:

Click the arrow, and it will take you to the next article on the page.

To create this, first lets setup the CSS.  The key in here is the position: fixed; bottom: 2%; left: 2%; statement.  This pins the arrow to the bottom left of the browser window.  Since position fixed doesn't work in IE6, I just hide the div with the * html hack - I know I'm bad ;)  Since Apple dropped support for IE6, I might as well for the advanced features.

#next_arrow {
	width: 50px;
	padding-top: 50px;
	background: url(../img/structure/backgrounds/next_arrow.gif) no-repeat top left;
	text-align: center;
	position: fixed;
	bottom: 2%;
	left: 2%;
	z-index: 999;
}
* html #next_arrow {
	display: none;
}
#next_arrow:hover {
	cursor: pointer;
}

Using the scrollTo plugin , this is a fairly straight forward task to make the window scroll to the next article.  Here is the code I am using:

jQuery(function($){
		
	$('<div id="next_arrow">Next</div>') 
		.prependTo("body") //append the Next arrow div to the bottom of the document
		.click(function(){ 
			scrollTop = $(window).scrollTop();
			$('#content h2').each(function(i, h2){ // loop through article headings
				h2top = $(h2).offset().top; // get article heading top
				if (scrollTop < h2top) { // compare if document is below heading
					$.scrollTo(h2, 800); // scroll to in .8 of a second
					return false; // exit function
				}
			});
		});
	
});

If you read my comments in the code, you should be able to know what's going on here. The only thing you have to worry about is changing the selector #content h2 to your article heading selector.

That's it, now you have a skip to next article button!

Firebuggin'

Posted July 17, 2008 by John Resig

I've got a mini-announcement. Starting this week about half of my time at Mozilla is going to be spent driving the direction of the brand-new Mozilla Firebug team. I'm, understandably, quite excited about this proposition. Like all web developers I've found Firebug to be an invaluable tool for web development .

We have a great team forming - I'm going to be joined by:

We're in a very primordial stage right now - we're meeting at the Firefox Summit at the end of the month and again at the beginning of August for the Firebug Working Group . We'll be setting some major goals for post-Firebug 1.2 development. I highly suspect that we'll be doing some exploratory Firebug extension development as well.

In the meantime we'll be hanging out in the Firebug IRC channel, which can be found here:

Server: irc.mozilla.org

Room: #firebug

We're going to love to hear any suggestions for feature development - I'm sure you've got tons of ideas - and we do as well. I'm quite excited about all of this. Here's to a bright Firebug future!

JavaScript Micro-Templating

Posted July 16, 2008 by John Resig

I've had a little utility that I've been kicking around for some time now that I've found to be quite useful in my JavaScript application-building endeavors. It's a super-simple templating function that is fast, caches quickly, and is easy to use. I have a couple tricks that I use to make it real fun to mess with.

Here's the source code to the templating function (a more-refined version of this code will be in my upcoming book Secrets of the JavaScript Ninja ):

// Simple JavaScript Templating

// John Resig - http://ejohn.org/ - MIT Licensed

( function ( ) {

  var cache = { } ;

 

  this .tmpl = function tmpl( str, data) {

    // Figure out if we're getting a template, or if we need to

    // load the template - and be sure to cache the result.

    var fn = !/\W/ .test ( str) ?

      cache[ str] = cache[ str] ||

        tmpl( document.getElementById ( str) .innerHTML ) :

     

      // Generate a reusable function that will serve as a template

      // generator (and which will be cached).

      new Function ( "obj" ,

        "var p=[],print=function(){p.push.apply(p,arguments);};" +

       

        // Introduce the data as local variables using with(){}

        "with(obj){p.push('" +

       

        // Convert the template into pure JavaScript

        str

          .replace ( /[ \r\t\n] /g , " " )

          .split ( "<%" ) .join ( "\t " )

          .replace ( /( ( ^|%>) [ ^\t] *) '/g, "$1\r ")

          .replace(/\t =(.*?)%>/g, "'
,$1 ,'")

          .split("\t ").join("'
) ;")

          .split("
%>").join(" p.push ( '")

          .split("\r ").join("\\ '
")

      + "
');}return p.join(' ');");

   

    // Provide some basic currying to the user

    return data ? fn( data ) : fn;

  };

})();

You would use it against templates written like this (it doesn't have to be in this particular manner - but it's a style that I enjoy):

<script type ="text/html" id ="item_tmpl" >

  <div id ="<%=id%> " class="<%=(i % 2 == 1 ? " even" : " ")%> ">

    <div class=" grid_1 alpha right">

      <img class=" righted" src=" <%=profile_image_url%>
"/>

    </div>

    <div class ="grid_6 omega contents" >

      <p> <b> <a href ="/<%=from_user%> "><%=from_user%> </a> :</b> <%=text%> </p>

    </div>

  </div>

</script>

You can also inline script:

<script type ="text/html" id ="user_tmpl" >

  <% for ( var i = 0 ; i < users.length; i++ ) { %>

    <li> <a href ="<%=users[i].url%> "><%=users[i].name%> </a> </li>

  <% } %>

</script>

Quick tip: Embedding scripts in your page that have a unknown content-type (such is the case here - the browser doesn't know how to execute a text/html script) are simply ignored by the browser - and by search engines and screenreaders. It's a perfect cloaking device for sneaking templates into your page. I like to use this technique for quick-and-dirty cases where I just need a little template or two on the page and want something light and fast.

and you would use it from script like so:

var results = document.getElementById ( "results" ) ;

results.innerHTML = tmpl( "item_tmpl" , dataObject) ;

You could pre-compile the results for later use. If you call the templating function with only an ID (or a template code) then it'll return a pre-compiled function that you can execute later:

var show_user = tmpl( "item_tmpl" ) , html = "" ;

for ( var i = 0 ; i < users.length ; i++ ) {

  html += show_user( users[ i] ) ;

}

The biggest falling-down of the method, at this point, is the parsing/conversion code - it could probably use a little love. It does use one technique that I enjoy, though: If you're searching and replacing through a string with a static search and a static replace it's faster to perform the action with .split("match").join("replace") - which seems counter-intuitive but it manages to work that way in most modern browsers. (There are changes going in place to grossly improve the performance of .replace(/match/g, "replace") in the next version of Firefox - so the previous statement won't be the case for long.)

Feel free to have fun with it - I'd be very curious to see what mutations occur with the script. Since it's so simple it seems like there's a lot that can still be done with it.

Tutorial - CakePHP Ajax "Quick Save" with jQuery

Posted July 15, 2008 by Marc Grabanski

When you are in an administration panel, sometimes you want a "quick save" feature that allows you to save without leaving the page.  Here is how to accomplish this with CakePHP and jQuery.

To start, download jQuery and the jQuery Form Plugin JavaScript .  Include them in your view with the JavaScript helper:

$javascript->link(array('jquery', 'form'), false);

Include the RequestHandler in your controller detect an Ajax save attempt.  Also include the JavaScript helper if you haven't already.

var $helpers = array('Javascript');
var $components = array('RequestHandler');

Next we want to override our save function with the ajax quick save.  Put this right before your $this->Model->save($this->data) in your save action.

if ($this->RequestHandler->isAjax()) {
	if ($this->Article->save($this->data)) {
		echo 'success';
	}
	Configure::write('debug', 0);
	$this->autoRender = false;
	exit();
}

This detects if the request is ajax, then saves the data.  Then it sends back a simple, "success" message to let you know things went fine.  It also writes debug to 0 and doesn't render anything, then exits.

Lastly, lets create and include a JavaScript file that performs the quick save.

jQuery(function($){
 	$('<input type="button" value="Quick Save" />')
 		.click(function(){
 			$(this).parents("form:first").ajaxSubmit({
 				success: function(responseText, responseCode) {
 					$('#ajax-save-message').hide().html(responseText).fadeIn();
 					setTimeout(function(){ 
 						$('#ajax-save-message').fadeOut(); 
 					}, 5000);
 				}
 			});
 			return false;
 		})
 		.appendTo('form div.submit'); 
});

This adds a button called, "Quick Save" to each form on the page where a div with class="submit" exists (you may want to switch this to the id of the form you want to add quick save to). Then It also attaches a click event to the button that submits the form via the jQuery Form Plugin .

In a few simple steps, we've created a quick save feature that saves your data whenever you want without leaving the page.

jQuery UI 1.5.2

Posted July 14, 2008 by Paul

About 4 days ago, many have noticed that we had uploaded another minor bugfix release to our Google Code account. While there’s, again, no new API introduced, more than 30 issues have been cleared and the codebase is growing more stable every day.

The full changelog is available here if you want to find out if a specific issue has been addressed. As with 1.5.1, updating to this version is highly recommended and likely not to break anything in your written code.

You can grab the latest release as always via the downloader or as developer package at http://ui.jquery.com/download or if you prefer, get it as latest tag from Subversion.

As a last comment, this is probably the last minor release before 1.6, which we will announce before the end of July, so watch out for a couple of awesome new components and enhancements soon!

Have a nice day,

Paul Bakaus & the jQuery UI Team

Engine Yard Raises $15MM

Posted July 14, 2008 by wycats

When I started at Engine Yard in January, my employee number was in the low 20’s. Today, just half a year later, Engine Yard employs around 80 people and we’ve just announced that we’ve raised another $15 million, which should help us move our kick-ass Ruby projects along even faster.

Some things that have happened since I joined Engine Yard in January:

  • Rubinius runs Rails
  • Apple uses SproutCore, which is built on Merb
  • Serious Merb apps getting millions of hits have been launched and are running rock-solid on Engine Yard hardware
  • We announced Vertebra and have begun work in earnest. Over the past few months, we’ve begun to see working prototypes of parts of the project already usable for internal Engine Yard use
  • We began work on a Control Panel (I head up the project, code-named “ninja”), which we launched internally last week. Without any exaggeration whatsoever, getting to this point would not have been possible without Corey (atmos). If you’re an Engine Yard customer, buy him a beer.
  • DataMapper 0.9 was released, and Engine Yard is using it heavily internally, meaning that the Merb/DataMapper stack has gone from being a pipe-dream to being a real, viable-for-production, and used-in-production stack. In LinkedIn’s post-RailsConf post, they talk about Merb/DM as the future Ruby web stack.

It would seem as though a list like that should take a years, but all of that has happened in the last six months. It just goes to show how far a little bit of confidence from some savvy investors can go toward progress in the Ruby ecosystem.

Congrats fellow Engine Yarders!

HTML 5 data- Attributes

Posted July 13, 2008 by John Resig

A new feature being introduced in HTML 5 is the addition of custom data attributes . This is a, seemingly, bizarre addition to the specification - but actually provides a number of useful benefits.

Simply, the specification for custom data attributes states that any attribute that starts with "data-" will be treated as a storage area for private data (private in the sense that the end user can't see it - it doesn't affect layout or presentation).

This allows you to write valid HTML markup (passing an HTML 5 validator ) while, simultaneously, embedding data within your page. A quick example:

<li class ="user" data-name ="John Resig" data-city="Boston"

     data-lang ="js" data-food="Bacon" >


  <b> John says:</b> <span> Hello, how are you?</span>

</li>

The above will be perfectly valid HTML 5. This should be a welcome addition to nearly every JavaScript developer. The question of the best means of attaching raw data to HTML elements - in a valid manner - has been a long-lingering question. Frameworks have tried to deal with this in different manners, two solutions being:

  1. Using HTML, but with a custom DTD.
  2. Using XHTML, with a specific namespace.

The addition of this prefix completely routes around both issues (including any extra markup for validation or needing to be valid XHTML) with this effective addition.

On top of this a simple JavaScript API is presented to access these attribute values (in addition to the normal get/setAttribute):

var user = document.getElementsByTagName ( "li" ) [ 0 ] ;

var pos = 0 , span = user.getElementsByTagName ( "span" ) [ 0 ] ;

var phrases = [

  { name : "city" , prefix: "I am from " } ,

  { name : "food" , prefix: "I like to eat " } ,

  { name : "lang" , prefix: "I like to program in " }

] ;

user.addEventListener ( "click" , function ( ) {

  var phrase = phrases[ pos++ ] ;

  // Use the .dataset property

  span.innerHTML = phrase.prefix + user.dataset [ phrase.name ] ;

} , false ) ;

The .dataset property behaves very similarly to the the .attributes property (but it only works as a map of key-value pairs). While no browsers have implemented this exact DOM property, it's not hugely needed - the above code could be done with the critical line replaced with:

span.innerHTML = phrase.prefix +

  user.getAttribute ( "data-" + phrase.name ) ;

I think what is most enticing about this whole specification is that you don't have to wait for any browser to implement anything in order to begin using it. By starting to use data- prefixes on your HTML metadata today you'll be safe in knowing that it'll continue to work well into the future. The time at which the HTML 5 validator is integrated into the full W3C validator your site will already be compliant (assuming, of course, you're already valid HTML 5 and using the HTML 5 Doctype ).

Implementing a Selectors API Test Suite

Posted July 10, 2008 by John Resig

This week I've been busy working on implementing a test suite for the Selectors API specification. I picked up a new microphone recently so I decided to do a quick walkthrough of the work that I've been doing and how I've been going about it. You can view the the video below:



Implementing a Selectors API Test Suite

You can run the test suite for yourself here (it's still very much in flux - there are various parts that may still be wrong):

http://ejohn.org/apps/selectortest/

Here's a quick break down of a test run that was done earlier:

- Special Firefox 3.1 Build (73.8% - More details )

- Safari 3.1 (49.3% - No Fragment or Namespace support)

- WebKit Nightly (51% - No Fragment or Namespace support)

- Opera Gogi - "ACID3 Build" (76.7% - No Fragment support)

- IE 8 (Can't run - the file is proper XHTML so it tries to download it.)

- Firefox 3, Opera 9.5, IE 7 (0%)

The work that is being done to implement the specification in Firefox can be seen on its associated Bugzilla bug . I'm shooting very hard to make sure that everything is in place so that this makes it in to the upcoming Firefox 3.1 release (the first alpha of which is due out in a couple weeks). The benefits that this will have for both JavaScript libraries and their users will be tremendous.

Three Quick Ways to Avoid Widows

Posted July 08, 2008 by Karl Swedberg

A few months ago I threw together a quick redesign of the Learning jQuery site. It's nothing fancy, mind you, but I was itching to retire the thin veil covering the tired old WordPress Kubrick theme, so something had to be done.

Almost immediately upon changing the font-family and font-size of the blog post titles, I noticed a few unsightly widows (just to clarify, we're talking about typographical widows . My mother already suspects me of avoiding her; I don't want to add to her anxiety. ;) ).

Here is an example of one such widow:

widow

See how the last word, "plugin," appears on its own line? According to a couple designerly friends of mine, that's a no-no. So, I considered for half a minute how to get that title to look more like this:

no widow

The lowly yet lovely non-breaking space (&nbsp;) would do the trick, but how to replace it for the regular, breaking space? I certainly wasn't about to manually edit all of the entries' titles. Not only would it take too long, but it would also pollute the markup with something that really shouldn't be there. No, what I needed was a little JavaScript.

Selecting the Titles

On this site, entry titles are wrapped in <h2><a href="foo"></a></h2> , which can be selected in jQuery with $('h2 a') . Easy. Now, because I want to manipulate the text of each title independently, I'll need to use the .each() method, which is basically a chainable for loop. Inside the .each() is where I substitute the last breaking space with the non-breaking variety. Here are three ways I came up with to achieve this.

Array

The first approach was to convert the title string into an array of words and then stitch the array items back together, dealing with the last one specially.

JavaScript:
  1. $( document) .ready ( function ( ) {
  2.   var h2Text = '' ;
  3.   $( 'h2 a' ) .each ( function ( ) {
  4.     var h2Array = $( this ) .text ( ) .split ( ' ' ) ,
  5.       h2Last = h2Array.pop ( ) ;
  6.     h2Text = h2Array.join ( ' ' ) + '&nbsp;' + h2Last;
  7.     $( this ) .html ( h2Text) ;
  8.   } )
  9. } ) ;

A couple things to note about line 5 above: (a) the variable is actually being declared (with a "var") because it's separated from the previous variable declaration by a comma rather than a statement-ending semicolon. (b) The JavaScript .pop() array method "pops" the last item off the array and returns it; so it's no longer part of h2Array , but its value is stored in the h2Last variable. This is especially handy for us, because we don't want the last word to appear twice.

Line 6 joins the remaining array items with a space between them and then appends the non-breaking space and the popped last item. Line 7 dumps that concatenated string back into the title, inside <h2><a></a></h2> .

String

The next approach involved working solely with strings, using the slice and lastIndexOf methods to split the the string into two pieces — one leading up to the last space, and one immediately following the last space.

JavaScript:
  1. $( document) .ready ( function ( ) {
  2.   var h2all, h2a, h2b;
  3.   $( 'h2 a' ) .each ( function ( ) {
  4.     h2all = $( this ) .text ( ) ;
  5.     h2a = h2all.slice ( 0 , h2all.lastIndexOf ( ' ' ) ) ;
  6.     h2b = '&nbsp;' + h2all.slice ( h2all.lastIndexOf ( ' ' ) +1 ) ;
  7.     $( this ) .html ( h2a + h2b) ;
  8.   } ) ;
  9. } ) ;

As line 7 demonstrates, the two sliced strings are stitched back together to keep the last two words on the same line.

Regular Expression

The final technique is the one I ended up sticking with, partly because it's the tersest and partly because I have a fondness for regular expressions:

JavaScript:
  1. $( document) .ready ( function ( ) {
  2.   var h2Text = '' ;
  3.   $( 'h2 a' ) .each ( function ( ) {
  4.     h2Text  = $( this ) .text ( ) .replace ( / ( \w+) $/ ,'&nbsp;$1' ) ;
  5.     $( this ) .html ( h2Text) ;
  6.   } ) ;
  7. } ) ;

The distinguishing feature here is line 4, which uses the replace regular-expression method. This method takes two arguments, a pattern to match against and a replacement value. For the pattern, which appears between the two slashes, we first match a space and then match one or more "word characters" (letters, numerals, or underscores). The parentheses capture all but that initials space, and the "$" at the end ensures that the match appears at the end of the string. The replacement argument starts with the non-breaking space and follows with $1 , which refers to the first (and in our case, only) parenthetical "capture group." (Please forgive me if I've provided too much detail about the regular expression. I'm never quite sure how much of this stuff is worth mentioning, but since this entry is targeting beginners, I suppose it's better to err on the side of too much.)

Update

A few people pointed out in the comments that my regular expression could be improved, and I agree. In particular, as noted by Art Lawry, the \w can be changed to \S (that's an uppercase S) to match any non-whitespace character. That way it'll match characters such as ö and ç as well.

By the way, all three of these code examples can be reduced in length quite a bit. For example, the regular expression example can be pared down from 7 to 5 lines if we don't bother with the h2Text variable and instead do something like this:

$(this).html($(this).text().replace(/ (\w+)$/,' $1'));

However, the code is usually easier to read and maintain (for me, at least) if the value is first stored in a variable.

Any suggestions for improvement here? Any other approaches that you would recommend instead? Leave a comment.

jQuery LiveSearch

Posted July 08, 2008 by John Resig

A fun blog post popped up yesterday in which John Nunemaker ported a Quicksilver-style Live Search to jQuery . Taking a look at his code, I decided to have a little fun and re-port it to jQuery - trying to use the functional style that jQuery promotes. I think the end result is quite simple and elegant.

The final code - compare with John's port :

jQuery.fn .liveUpdate = function ( list) {

  list = jQuery( list) ;

  if ( list.length ) {

    var rows = list.children ( 'li' ) ,

      cache = rows.map ( function ( ) {

        return this .innerHTML .toLowerCase ( ) ;

      } ) ;

     

    this

      .keyup ( filter) .keyup ( )

      .parents ( 'form' ) .submit ( function ( ) {

        return false ;

      } ) ;

  }

   

  return this ;

   

  function filter( ) {

    var term = jQuery.trim ( jQuery( this ) .val ( ) .toLowerCase ( ) ) , scores = [ ] ;

   

    if ( !term ) {

      rows.show ( ) ;

    } else {

      rows.hide ( ) ;

      cache.each ( function ( i) {

        var score = this .score ( term) ;

        if ( score > 0 ) { scores.push ( [ score, i] ) ; }

      } ) ;

      jQuery.each ( scores.sort ( function ( a, b) { return b[ 0 ] - a[ 0 ] ;} ) , function ( ) {

        jQuery( rows[ this [ 1 ] ] ) .show ( ) ;

      } ) ;

    }

  }

} ;

A couple points to note:

  • .liveUpdate() no longer takes an element ID - it now accepts any jQuery selector (this is the only notable API change that I made).
  • All state is stored in simple variables and accessed via closures, as opposed to as properties of an instance object.
  • Only one function is used - and that's stored away within the function itself (greatly simplifying the resulting code).
  • DOM queries are only done once and cached up front.
  • .map() is used to simplify the creation of new arrays of information.

Published on LearningjQuery.com - Introduction to jQuery UI

Posted July 02, 2008 by Marc Grabanski

I published an article on learningjquery.com titled, "Introduction to jQuery UI " - I hope you enjoy the article!

As a member of the jQuery UI team, I felt compelled to write this introduction because I didn't see any tutorials starting from the ground level.  I'm hoping the article will help people get their feet wet with jQuery UI.

Introduction to jQuery UI

Posted July 02, 2008 by Marc Grabanski

After many months of stellar work, the jQuery UI team has released version 1.5 of their flagship suite of user-interface widgets, components, and effects. This release was focused on bringing you a standardized development API across all of the components, allowing for a more seamless experience when working with the jQuery UI library.

A very exciting CSS theming application was also released with jQuery UI 1.5, called ThemeRoller . ThemeRoller is an amazing way to customize the style and colors across all of the jQuery UI components. It comes with a few preset styles, as well as allowing you to create your own. Once you are done, it packages your theme into a zip file that contains all of the images and CSS you need.

Brief Overview of the jQuery UI Project

The jQuery UI project was originally created to bring you a set of "official" jQuery plugins. Mature components from the plugins repository were pulled together to form the first release of jQuery UI. But since each of these plugins had its own style, having been written by different authors, the first release of the library felt a bit cumbersome when packaged together. With that in mind, the focus of UI 1.5 was on achieving a coherent, standardized API to eliminate much of the differences between the components. Through much time and effort, many bugs and feature requests were addressed along the way as well.

Inside Look at jQuery UI Version 1.5

Before starting, I want to make sure you know where the jQuery UI Documentation is located. You may also want to head to the download page to grab the library for yourself. Note that the development bundle is the easiest to get started with.

First, let's start by including the necessary files for jQuery UI: jQuery latest js file, the Flora theme complete stylesheet, and the core UI file. Each of the components is built on top of these files. Here is how to include them:

HTML:
  1. <link rel ="stylesheet" href ="themes/flora/flora.all.css" type ="text/css" media ="screen" title ="Flora (Default)" >
  2. <script src ="http://code.jquery.com/jquery-latest.js" > </script>