Guest Lecture: JavaScript, Ajax & DOM Manipulation

Zach has asked me to give a JavaScript guest lecture in his Web Programming class. We’ll cover: JS basics, node manipulation, DOM traversal, AJAX, JavaScript libraries, and myPlymouth implementations of those topics. Here are the slides (get the .ppt here):

A few resources that are mentioned:

Note: this is an updated talk on the workshop I led two years ago

Book Review: Learning jQuery

I’m a huge fan and avid user of jQuery and have been extremely impressed by the documentation provided on the jQuery website. The one thing that documentation lacks, however, is really contextual examples that drive home some bare essentials of JavaScripting with the jQuery library. Learning jQuery – a book by Jonathan Chaffer and Karl Swedberg – is an excellent introductory book for those that are thinking about using (or struggling with) jQuery.

jQuery, while a fairly high level JavaScript library is a beautiful thing but can be very daunting to a developer that is new to JavaScripting or is coming from a dissimilar library, being thrust full bore into a ‘new’ way of doing old tricks. (Which jQuery is great at by the way…it makes the new ways super sexy, sleek, and easy). The authors do a great job of explaining what jQuery is and why it is such a powerful tool.

Throughout the book are examples on traversing and manipulating the DOM, event handling, leveraging jQuery’s JS effect capabilities, AJAX, etc; many of which are built off of previously detailed examples, allowing the reader to easily grasp what is going on and why a chunk of code was used.

While I feel this book is primarily an excellent introductory source for diving into the world of JavaScript development with jQuery, the fairly seasoned jQuery user (like myself) may find a trick or two that they hadn’t quite thought of… As I read through, I found a few choice bits that allowed me to make my own code more efficient!

My only real complaint with the book is the index at the back. There are a number of jQuery functions that are discussed within the chapters yet weren’t referenced in the index. A small nitpick, I know, but I’m a sucker for a good index :)

So. Overall, I think the book is a good thing to keep on the bookshelf, whether a you are a jQuery n00b or not. There’s always a co-worker/friend/programming buddy that will want to learn jQuery and what better way to get them rolling on there own than a link to the jQuery docs and a sweet book?

You can get the book here.

jQuery 1.1.3: Speed Improvements and Bug Fixes

After a long wait, jQuery 1.1.3 has been released! (Download it at the jQuery site) When I first adopted jQuery a year ago, the library boasted both faster speeds and smaller size than any other JavaScript Ajax/DOM tool. With the release of jQuery’s version 1.1.2, a number of jQuery’s operations became very slow and inefficiencient, as evidenced by MooTool’s SlickSpeed CSS Selector Test (found via Ajaxian) which crept up a few weeks ago.

This new release boasts an 800% speed improvement with a number of its selectors along with various enhancements across the board! The selector speed boost makes me one happy camper. Check out the enhancements as it compares to 1.1.2:

Browser jQuery 1.1.2 jQuery 1.1.3 % Improvement
IE 6 4890ms 661ms 740%
Firefox 2 5629ms 567ms 993%
Safari 2 3575ms 475ms 753%
Opera 9.1 3196ms 326ms 980%
Average improvement: 867%

And here’s how it now stacks up against the SlickSpeed test:

Browser Prototype jQuery Mootools Ext Dojo
IE 6 1476ms 661ms 1238ms 672ms 738ms
Firefox 2 219ms 567ms 220ms 951ms 440ms
Safari 2 1568ms 475ms 909ms 417ms 527ms
Opera 9.1 220ms 326ms 217ms 296ms 220ms

In addition to the speed enhancements, there were several other notable things:

  • Unicode Selectors: Yup…now you can use fancy non-english characters.
  • Escape Selectors: This is awesome. Now, if you use weird characters (i.e. punctuation) in a class/id name, you can now escape those characters within the selector syntax. E.g. $(“div#foo\.bar”)
  • Inequality Selector: You can now select elements where their attributes do not match a specific string of characters. E.g. $(“div[@id!=test]“)
  • :nth-child() improvements: jQuery has supported selectors like :nth-child(1) and :nth-child(odd) since the beginning of jQuery, now they’ve added advanced :nth-child selectors, such as:
    • $(“div:nth-child(2n)”)
    • $(“div:nth-child(2n+1)”)
    • $(“div:nth-child(n)”)
  • Space-separated attributes: After being removed in jQuery 1.0, this selector has now been brought back by popular demand. It allows you to locate individual items in a space-separated attribute (such as a class or rel attribute). E.g. $(“a[@rel~=test]“)
  • Animation Improvements: Animations are now significantly faster and smoother. Additionally, you can run more simultaneous animations without incurring any speed hits.
  • DOM Event Listeners: Internally, the jQuery Event system has been overhauled to use the DOM Event system, rather than the classical “onclick” style of binding event handlers. This improvement allows you to be more unobtrusive in your use of the library (not affecting the flow of other libraries around it). Additionally, it helped to resolve some of the outstanding issues that existed with binding event listeners to IFrames.
  • Event Normalization: Some great steps have been taken to normalize keyboard and mouse events. You can now access the event.which property to get most details about the specific key or button that was pressed.
  • Multiple .is(): The .is() method can now take multiple selectors, separated by a comma. This allows you to test your jQuery set against multiple selectors. E.g. $(“div”).is(“:visible, :first”)
  • Browser Version: A commonly requested feature, by plugin authors, was a way to determine what browser version their users were using. We now expose an extra property through which this information can be accessed. E.g. jQuery.browser.version

Additionally, the jQuery team has addressed 80+ bugs and has roadmapped out the next two releases (v1.1.4 and v1.2). To check out the full jQuery 1.2 roadmap, go here.

JSMin: Javascript Compression

While at The Ajax Experience in October, I attended a presentation who spoke of the 3 C’s (Combine, Compress, Cache) for Ajax development. In the Compress section I was introduced to the beauty of JSMin!

What is it? Well, shut up and I’ll tell you.

Quoting Douglas Crockford (the creator of JSMin):

JSMin is a filter which removes comments and unnecessary whitespace from JavaScript files. It typically reduces filesize by half, resulting in faster downloads. It also encourages a more expressive programming style because it eliminates the download cost of clean, literate self-documentation.

As I’ve been creating more complex Javascript applications, the file size has been increasing (although the size is greatly reduced, thanks to jQuery). And, as a careful programmer should, I place comments all over my Javascript code so I don’t draw too many blanks while updating/debugging…well, those comments tend to bloat the file size, as does the whitespace. The stripping of those elements alone has dropped the file-size of a number of my scripts by 40-50%! Thats huge. What previously was an 8K file goes down to 4K. Awesome.

If you’re curious what JSMin does to your code, here’s an example that Douglas gives:
Before

// is.js

// (c) 2001 Douglas Crockford
// 2001 June 3


// is

// The -is- object is used to identify the browser.  Every browser edition
// identifies itself, but there is no standard way of doing it, and some of
// the identification is deceptive. This is because the authors of web
// browsers are liars. For example, Microsoft's IE browsers claim to be
// Mozilla 4. Netscape 6 claims to be version 5.

var is = {
    ie:      navigator.appName == 'Microsoft Internet Explorer',
    java:    navigator.javaEnabled(),
    ns:      navigator.appName == 'Netscape',
    ua:      navigator.userAgent.toLowerCase(),
    version: parseFloat(navigator.appVersion.substr(21)) ||
             parseFloat(navigator.appVersion),
    win:     navigator.platform == 'Win32'
}
is.mac = is.ua.indexOf('mac') >= 0;
if (is.ua.indexOf('opera') >= 0) {
    is.ie = is.ns = false;
    is.opera = true;
}
if (is.ua.indexOf('gecko') >= 0) {
    is.ie = is.ns = false;
    is.gecko = true;
}

After

var is={ie:navigator.appName=='Microsoft Internet Explorer',java:navigator.javaEnabled(),ns:navigator.appName=='Netscape',ua:navigator.userAgent.toLowerCase(),version:parseFloat(navigator.appVersion.substr(21))||parseFloat(navigator.appVersion),win:navigator.platform=='Win32'}
is.mac=is.ua.indexOf('mac')>=0;if(is.ua.indexOf('opera')>=0){is.ie=is.ns=false;is.opera=true;}
if(is.ua.indexOf('gecko')>=0){is.ie=is.ns=false;is.gecko=true;}

As you can see…the result is not easily read so you’ll want to keep your original script around in the event that editing is needed. Super cool. So how do you get it? Well…there’s a number of languages that JSMin logic has been ported to: zip file containing
an MS-DOS.exe file
, or you can get the C source code and build it yourself. Now in C# and Java and JavaScript and Perl and PHP and Python and Ruby.

If you haven’t thought about Javascript compression yet, you might want to start. Try it out…you’ll be happy you did. Oh, and if you are curious: the Javascript implementation of JSMin is my favorite as it gives some excellent feedback and comment options on compression.

Remote JavaScripting Example – Part I

[[innerindex]]At Plymouth State we work in a multi-server environment and often wish to display dynamic content from one server in an Ajax-like fashion on another server’s website. My co-worker, Zach Tirrell, and I have drummed up a solution that works to keep our layout and logic separate, while still serving our end users in a smooth, seamless, non-iFramed manner.

I’ll walk through the creation of a simple search ‘widget’ that relies on dynamic data to populate a drop-down box.

The Tools

- PHP
- JavaScript
- XTemplate
- RemoteJSOutput: a simple script by Matthew Batchelder (me) and Zach Tirrell

Step 1: create your template

This widget is fairly simple and thus has a simple template. The template syntax is simply XTemplate syntax.

I’ll name it widget.tpl and store it in a templates directory.

<!-- BEGIN: main -->
<div id="sweet_borkweb_widget" style="background:#eee;border:1px solid #ccc;text-align:center;">
  <div style="color:#000;">A Couple JavaScript Posts at BorkWeb</div>
  <select id="borkweb_posts">
    <option value=""></option>
  <!-- BEGIN: post -->
    <option value="{post.url}">{post.title}</option>
  <!-- END: post -->
  </select>
</div>
<!-- END: main -->

Step 2: simple template output

First we’ll just spit out the template. Lets create a PHP file to do this. We’ll call it widget.php

< ?php
include('includes/xtemplate.php');
$xtpl = new XTemplate('templates/widget.tpl');

$xtpl->parse('main');
$xtpl->out('main');
?>

Check out your widget so far. Its plain. Lets change that.

Step 3: add dynamic content

We want the drop down list to hold BorkWeb articles. We’ll add that in now.

Note: this is where you would normally pull from a database, an RSS feed, or some other source. For simplicty, we’ll be using a simple PHP array. Whoop-de-doo. Here it is:

< ?php
include('includes/xtemplate.php');
$xtpl = new XTemplate('templates/widget.tpl');

//get list of articles
$articles=array(
  array('title'=>'Ajax, More Than A Buzz Word','url'=>'http://borkweb.com/story/ajax-more-than-a-buzz-word'),
  array('title'=>'Ajax; Templating; and the Separation of Layout and Logic','url'=>'http://borkweb.com/story/ajax-templating-and-the-separation-of-layout-and-logic'),
  array('title'=>'Deleting the Internet','url'=>'http://borkweb.com/story/deleting-the-internet'),
  array('title'=>'Node Manipulation in the DOM','url'=>'http://borkweb.com/story/node-manipulation-in-the-dom'),
  array('title'=>'Prototype Cheat Sheets','url'=>'http://borkweb.com/story/prototype-cheat-sheets'),
  array('title'=>'Script.aculo.us Is My New Best Friend','url'=>'http://borkweb.com/story/scriptaculous-is-my-new-best-friend'),
  array('title'=>'The Case For JSON: What Is It and Why Use It?','url'=>'http://borkweb.com/story/the-case-for-json-what-is-it-and-why-use-it')
);

//loop over articles and place in template
foreach($articles as $article)
{
  $xtpl->assign('post',$article);
  $xtpl->parse('main.post');
}//end foreach

$xtpl->parse('main');
$xtpl->out('main');
?>

Check out your widget now. It has been populated with some data! w00t! Now to make it work, we return to our beloved widget.tpl template file.

Step 4: make the template work

We’re going to add in some javascript. Lets make the dropdown list redirect the user to the selected article when the drop-down box changes.

I’m going to add this on our select box:

 onchange="if(document.getElementById('borkweb_posts').value!='') document.location=document.getElementById('borkweb_posts').value;"

The template should now look like this:

<!-- BEGIN: main -->
<div id="sweet_borkweb_widget" style="background:#eee;border:1px solid #ccc;text-align:center;">
  <div style="color:#000;">A Couple JavaScript Posts at BorkWeb</div>
  <select id="borkweb_posts" onchange="if(document.getElementById('borkweb_posts').value!='') document.location=document.getElementById('borkweb_posts').value;">
    <option value=""></option>
  <!-- BEGIN: post -->
    <option value="{post.url}">{post.title}</option>
  <!-- END: post -->
  </select>
</div>
<!-- END: main -->

Step 5: prepare script for remoting

Now that our lovely widget is functioning, lets prepare it for inclusion in other locations. This is simple using RemoteJSOutput (a simple script written by Zach Tirrell and myself).

We’ll include that PHP class and use it as follows:

< ?php
include('includes/RemoteJSOutput.class.php');
$remoteOutput=new RemoteJSOutput();

//start output buffering via 
$remoteOutput->start();

include('includes/xtemplate.php');
$xtpl = new XTemplate('templates/widget.tpl');

//get list of articles
$articles=array(
  array('title'=>'Ajax, More Than A Buzz Word','url'=>'http://borkweb.com/story/ajax-more-than-a-buzz-word'),
  array('title'=>'Ajax; Templating; and the Separation of Layout and Logic','url'=>'http://borkweb.com/story/ajax-templating-and-the-separation-of-layout-and-logic'),
  array('title'=>'Deleting the Internet','url'=>'http://borkweb.com/story/deleting-the-internet'),
  array('title'=>'Node Manipulation in the DOM','url'=>'http://borkweb.com/story/node-manipulation-in-the-dom'),
  array('title'=>'Prototype Cheat Sheets','url'=>'http://borkweb.com/story/prototype-cheat-sheets'),
  array('title'=>'Script.aculo.us Is My New Best Friend','url'=>'http://borkweb.com/story/scriptaculous-is-my-new-best-friend'),
  array('title'=>'The Case For JSON: What Is It and Why Use It?','url'=>'http://borkweb.com/story/the-case-for-json-what-is-it-and-why-use-it')
);

//loop over articles and place in template
foreach($articles as $article)
{
  $xtpl->assign('post',$article);
  $xtpl->parse('main.post');
}//end foreach

$xtpl->parse('main');
$xtpl->out('main');

//spit out script encased in JS
$remoteOutput->puke();
?>

Now check out your widget. Ugly, huh? Well, don’t worry, thats the way its supposed to look for now.

Step 6: place your widget somewhere

Now that we have that beautiful chunk of code in operation, place it on a webpage somewhere.

Put this:

<script type="text/javascript" src="http://url.to/your/widget.php"></script>

on any page you wish to test your widget!

Optional Step 7: create a wrapper

If, like me, you aren’t a fan of including RemoteJSOutput.class.php all over hell’s half acre, you can create a wrapper. This not only allows you to minimize the code you have to repeatedly place everywhere, but it also keeps your widget.php script functional on its own!

Here’s an example wrapper I’ll call get_script.php which will display wrapper_widget.php (step 3′s PHP file, renamed):

< ?php
include('includes/RemoteJSOutput.class.php');
$remoteOutput=new RemoteJSOutput();

//start output buffering via 
$remoteOutput->start();

//create a list of allowable widgets/scripts/yadda yadda
$allow=array('wrapper_widget');

//does the passed variable exist in the allowable widgets?
if(in_array($_GET['script'],$allow))
{
  //include the wrapper
  include($_GET['script'].'.php');
}//end if

//spit out script encased in JS
$remoteOutput->puke();
?>

So rather than using the script tag that Step 6 suggests, we’d use:

<script type="text/javascript" src="http://url.to/your/get_script.php?script=wrapper_widget"></script>

Conclusion

This method is simplistic and is simply touching on the topic of widgetization. I will be following up with dynamic user interactions in Part II, and finally discuss an open-standard widget library with xml definitions. Stay tuned :)

Download

Oh, and here’s the code used in this tutorial all zipped up.

Script.aculo.us v1.6 Released

Great news over at Mir.aculo.us. It appears as if version 1.6 of my favorite DOM manipulation library, Script.aculo.us has been released! Thankfully the new version of Script.aculo.us uses Prototype v1.5.

This is sexy because now I can revert to downloading both Prototype and Script.aculo.us right from the Script.aculo.us site. (Hooray for being lazy)

Here’s an excerpt from the original post:

script.aculo.us 1.6 marks the switch over to Prototype 1.5 (1.4 will no longer be supported), which has really great new features and has undergone some refactoring to take advantage of the Prototype 1.5 niceties.

Other new stuff includes window-scrolling while dragging, performance optimizations and various bug fixes.

They are urging people to test out 1.6. Get on it!