Archive for the 'yui' Category

My online footprint lately

Wednesday, July 23rd, 2008

This is a sort of a catch-up post for listing what I've been up to lately.

  • YUI Blog just published my first article, I'm so proud. It's about loading JavaScript in non-blocking fashion, because JavaScripts, they, you know, like, block downloads. Luckily, there's an easy fix - DOM includes, which I've previously discussed, discussed and discussed.
  • SitePoint published an update to my older article that introduces AJAX, ok, Ajax, by creating a command-line-like interface with PHP on the server side. The updated article features improved code, jQuery example, YUI example, JSON discussion and example. Check it out, bookmark and recommend to your friends that keep asking you "What's this AJAX (they are new, don't know it's now spelled "Ajax") thing? Do you know of a good article?"
  • YDN (developer.yahoo.com) published a video presentation of me and my lovely teammate Nicole Sullivan where we talk about some new and cool front-end performance techniques. So if you wandered how I look and are eager to hear my fabulous Balkan peninsula accent, give it a shot. The talk is called "After YSlow 'A'" and is targeted at those of you who have reached performance nirvana, but are still hungry for more. We talk about preloading components, post-loading, javascript, images, using flush() in PHP to send first byte early on and other fun stuff.
  • Last, not least, I decided to try and find some time to update my JavaScript patterns site. Unfortunately I got sidetracked (yep, I'm easily distracted by shiny objects) and played with a not-so-javascript pattern. The post I published (includes a pretty lame screencast! and) demonstrates how you can use animated background position to indicate loading progress.

Whew, c'est tout pout ce moment, expect a lot more now that the JavaScript book is out of the way. Ah, yep, if you feel like it, join me on Facebook, I created a JS book page.

 

Sancta Simplicitas: minimalistic WordPress theme using YUI CSS

Friday, April 25th, 2008

Sancta Simplicitas Wordpress theme screenshot

Sancta Simplicitas is a WordPress theme that uses YUI CSS utilities: reset, base, fonts, grids. It's very minimalistic in a sense that it's pretty much all white and simple. Hence the name. The theme is probably not usable by itself, but it's a base on top of which you can create your own themes.

When creating a new WordPress theme, people usually take the default Kubrick theme but I personally find Kubrick too much. So Sancta Simplicitas has a very minimal stylesheet and is based on the WordPress classic theme.

The fact that it uses YUI grids makes it trivial to tweak: fixed size vs full width, width of the sidebar, position of the sidebar. Basically right after the <body> there's this:

<div id="doc3" class="yui-t5">

Changing the id doc3 to doc, doc2, doc4 or doc5 will give you different widths of the content. The default doc3 is full width. Then changing the class name yui-t5 will give you different position and width of the sidebar. yui-t1, yui-t2 and yui-t3 put the sidebar on the left hand side, yui-t4, yui-t5 and yui-t6 place it to the right. You can go even crazier from here, nesting grids to get a two-column sidebar and so on, it's really easy with YUI grids and the docs are here.

Download Sancta Simplicitas

» sansim.zip

 

YUI pie chart example

Wednesday, January 16th, 2008

If case you haven't noticed - YUI Charts hit the streets.

As with everything new, it's best shown and understood by example. So here's the simplest example of using a pie chart. Basically I took the example from the YUI page, changed all the paths to point to yahooapis.com (where YUI is hosted for free) and stripped everything that could be stripped (there's even no html or head tags, but turned out the body tag is required). The result is a short html with all dependencies satisfied.

OK, so here's the example, grab, paste, customize:
chart.html

 

YUI anywhere (bookmarklet)

Friday, June 8th, 2007

Hooked on YUI? You can now take it anywhere you go. The thing is Yahoo hosts the libraries publicly, so they are available at any time. Let's say you visit a page and you want to do something with it. Comes the YUI bookmaklet that adds a new script tag to the page pointing to utilities.js that contains all YUI utilities (DOM, Event, DragDrop, Animation, Connection…). Then just open your Firebug console and start messing with the page. The powerful toolset that YUI is, is at your disposal.

The code

The code for the bookmarklet is really simple, just a question of adding a new script tag.

(function(){
    var s = document.createElement('script');
    s.src='http://yui.yahooapis.com/2.2.2/build/utilities/utilities.js';
    document.getElementsByTagName('head')[0].appendChild(s);
})()

Install

Right-click, add to favorites, or drag to bookmarks.

YUI anywhere

Let the fun begin

So you go to any page, click the bookmarklet and for example decide to make the logo on the page draggable. All you need to write in your JS console (or in address bar for IE) is:

new YAHOO.util.DD('logo')

The result is really not bad for a one-liner. But why stop there? Let's make everything on a page draggable.

var all = document.getElementsByTagName('*');for(var i = 0; i < all.length; i++) {new YAHOO.util.DD(all[i])}

Whoa! We can mess up with other people's pages like we've never messed up with other people's pages before! Example:

google-yui-drag.png

And why only other people's pages, what about ours? Imagine you're sitting with a client or boss, showing the new site and they go:
- Hmm, well, you know, I don't know about this spacing between the images here …
And you:
- Hold that thought (clicking bookmarklet, making everything draggable, dragging the offending image). Is this how you prefer it?
Client:
- Well, maybe, or actually it was better before.
You:
- Ah, OK, whatever suits your business needs

Update: Some more one-liners to prevent loading a new page when you attempt to drag a link or a submit button.

Disable links:
YAHOO.util.Event.addListener(window, 'click', function(e){ if (e.target.nodeName.toUpperCase() == 'A'){ YAHOO.util.Event.preventDefault(e) } });

Prompt before unload:
YAHOO.util.Event.addListener(window,'beforeunload',function(e){ e.returnValue = "Sure?" });

 

YUI good for your performance

Sunday, April 1st, 2007

One of the cons of using any of the good and popular third-party JS libraries is the file size of the .js files associated with them. A lot has been done by the library developers to address this issue - providing minified versions, segregating the script files based on what they do and using loading-on-demand, among others. There's more good news - Yahoo is now offering hosting of their YUI library files.

How is it good for you?
- less on the bandwidth bill - you don't need to worry about hosting these files yourself
- high availability - well Yahoo is behind this, so you can rest assured that these files are delivered to your surfer
- small downloads - Yahoo hosts the minified versions and gzips them
- files already cached!

The last one is the topic I had in mind with the this blog's title. Since Yahoo will be using the same locations for the libraries you need and since Yahoo is the most popular site, chances are your visitors have already checked their Y! mail or their Y! finance page and searched or done anything on the Yahoo network of sites. This means they have already requested and (hopefully) cached these .js files. And as proved before, lowering the number of HTTP requests is top 1 performance optimization you can ever do.

Even better is that you can include only libraries you need (less transfer) or you can include several in a batch (less requests).

If you only need Event and DOM, include only yahoo-dom-event.js (8K)
If you need only Event, DOM, AJAX, include yahoo-dom-event.js and connection-min.js (8K + 4K)
If you also need more like Drag and Drop and animation and AJAX, why not include all utility classes at once, utilities.js. After all this file is 22K, probably less than two images that will appear on your page.

Note: all filesizes above are when files are gzipped, which is how Yahoo serves them.

So bottom line, your visitors hit your site and, lo and behold, they have all the JS already cached, resulting in your page loading as fast as a rocket ;) Sweet.

 

Exploring Web1.0 kitsch - YUI snowflakes

Sunday, September 24th, 2006

Kitsch

So what's "kitsch"? Well, something that may look like an art, but is usually a thing of a bad taste, over-ornamented, glittering, too colorful, well, something a bit ugly, but liked by a lot of people. It's sometimes questionable what is kitsch and what is a really valuable piece of art, but anything arty is always a bit questionable, isn't it? More about kitsch on Wikipedia.

Web 1.0 kitsch

Here I don't mean to say that anything that is not web 2.0 is ugly, in fact I cannot say that everything that is not web 2.0 is (bad) web 1.0, because there's never a clear difference. Only because you don't have a tag cloud on your site, that doesn't mean your site is old and second best. There are kitschy web2.0 sites, of course, but here I'm thinking about those Web1.0 "effects", things that are hopefully in the past, we've all seen it, it's the rotating 3D @-signs, the animated gifs, the status bar animations, the things that follow your cursor and, of course, the ever so cool, snowflakes flying around the page.

Motivation

My 3-year-old daughter loves to browse with me, asking me to bring up images of Cinderella, Snow White and other favorites. Having no idea where to find them, I do an image search and then visit the sites that come up, hoping for the best. (BTW, that's not always safe, try "barbie") Most of the sites I get are kind of old, web 1.0. style, and one of them had these hearts flying around the screen. My daughter loved them. So I questioned myself how hard it would be to do something similar with all those new cool JS libraries we have today. I tried the YUI and it turned out it is easy, I believe I was able to do the snowflakes within an hour, most of that time spent on figuring out the "mathematical model" of what I was trying to do.

Not ideal

I'm the first to admit the solution is not ideal, for example my flakes are not of random size in IE (my fault) and the whole animation has little breaks every second or so in Firefox. I guess for the seconds bug it's either that YUI is not the best tool for the job or I'm using it the wrong way. But hey, this is a one-hour project. So let me tell you what I did.

The math

What I decided to do is have my snowflakes appearing on random place from top, left or right, and then disappearing at a random place on the left, right or bottom. Also to have a max of 20 flakes at any time, it's bad enough to have 20 to load you processor, more is just crazy. Then I have 4 random points on the screen for each flake that serve as control points when the flake is doing its curve.

Implementation

Using DOM I create a div that contains an asterisk, this is my snowflake. Then I'm using the Motion along the curve available from YUI in order to animate this div. This motion takes a start point, an end point and some control points, in my case 4. First thing to figure out is how much space we have on the screen. YUI helps with this:

max_x: YAHOO.util.Dom.getViewportWidth() - 20,
max_y: YAHOO.util.Dom.getViewportHeight() - 20,

Then the 4 control points are easy, just 4 random points:

var control = [];
for (i = 0; i < 4; i++) {
    control[i] = [
    Math.floor(Math.random() * this.max_x),
    Math.floor(Math.random() * this.max_y)
    ];
}

The hardest part was the start and end points. As I said, I decided to have start points from either top, left or right, so that gives me an array of three random points:

var start_points = [
    [Math.floor(Math.random() * this.max_x), 0],
    [0, Math.floor(Math.random() * this.max_y)],
    [this.max_x, Math.floor(Math.random() * this.max_y)]
];

First is the top where on the Y-axis I have always 0 and a random value for X, from 0 to max_x. The left is defined as X = 0 and Y is random from 0 to max_y, the right is X = max_x and Y is again random from 0 to max_y.

For the end points it's similar, only there instead of top of the screen I have the bottom. For the bottom Y is max_y and X is random.

var end_points = [
    [0, Math.floor(Math.random() * this.max_y)],
    [this.max_x, Math.floor(Math.random() * this.max_y)],
    [Math.floor(Math.random() * this.max_x), this.max_y]
];

Then I pick a random value from each of the two arrays

var this_start_index = Math.floor(Math.random() * 3);
var this_end_index   = Math.floor(Math.random() * 3);
var this_start = start_points[this_start_index];
var this_end   = end_points[this_end_index];

Once I have start/end and control points, I'm ready to create the new flake div, where the size of the flake and the DIV id are random:

// size of the flake
var flake_size = 10 + Math.floor(Math.random() * 20);

// random ID of the flake
var flake_id = "flake" + 99 + Math.floor(Math.random() * 99999)

// create the markup for the flake (using html2dom.com)
var html2dom_root = document.body;
html2dom_root_1_div = document.createElement("div");
html2dom_root_1_div.setAttribute("id", flake_id);
html2dom_root_1_div.setAttribute("style", "width: 5px; font-size: " + flake_size + "px");
html2dom_root_1_div_1_text = document.createTextNode("*");
html2dom_root_1_div.appendChild(html2dom_root_1_div_1_text);
html2dom_root.appendChild(html2dom_root_1_div);

Now I'm ready to create and setup the YUI animation (motion) instance:

// animation attributes
var attributes = {
   points: {
      to: this_end,
      from: this_start,
      by: [10, 10],
      control: control
   }
};

// setup animation/motion object
var myAnim = new YAHOO.util.Motion(flake_id, attributes);
// no easing
myAnim.method = YAHOO.util.Easing.easeNone;
// random duration
myAnim.duration = 20 + Math.floor(Math.random() * 20);
// on completion remove the flake and make more
myAnim.onComplete.subscribe(this.removeElement);
myAnim.onComplete.subscribe(this.make_flakes);
// go!
myAnim.animate();

The two functions that are "subscribed" on animation completion are responsible for removing the current flake div and for creating another set of flakes. The set of flakes generated is using the logic - generate a random number of new flakes (min 1, max 5) unless you've hit the upper limit of 20.

Demo

Here's the demo where you can see the whole script, I kept it in the HEAD part of the demo page.

 

AJAX MVC (so to speak)

Tuesday, September 19th, 2006

This is sort of a framework thing to create AJAX applications, based on the MVC design pattern. Yep, I have a lot of buzzwords here, I admit, but this shouldn't be taken too seriously. I was doing a bunch of small projects lately and I found myself using something like this little framework, without even thinking about it. Then I thought about it and I found that the scripts and the organization of them may resamble MVC a bit. So how does MVC fit when you mix things like thin and fatter client, HTML, JavaScript, XMLHttpRequest, PHP and CSS?

Usual AJAX app flow

What usually happens in an AJAX application is:

  1. you have an HTML page, styled with CSS
  2. you click on something
  3. JS sends request to the server (to a PHP script)
  4. JS updates the original HTML page

Mapping to the MVC pattern

OK, so what part of this process can be associated with a View, or a Model or a Controller? The Model is easy, it's the business logic, writing to a database and so on. This is the PHP script. The View? Obviously this is the HTML page and the CSS. But I'd like to think also about the JS that updates the page as part of the View. I mean it makes sense, it's updating the presentation part. Sometimes you even use innerHTML in the JS, but even if you use DOM, it becomes part of the HTML anyway. How about the Controller? Well, we have two controllers here. One that is on the server side, a PHP script that receives requests and "asks" the Model for the response. The other controller is on the client side, this is the JavaScript that decides what happens on a click of a button and sends an appropriate AJAX request to the PHP controller. Therefore I would consider any behavioural JS as part of the Controller, including attaching events as well as sending HTTP requests.

Here's an illustration:
AJAX MVC

In action (example)

I went ahead and implemented a very simple application to prove the concept. It's just a blank styled HTML page with a button. The HTML page includes two JavaScripts responsible for behaviours (Controller) and page updates (View). The page also includes a few unrelated helper javascripts, in my case I'm using the YUI library. The JS Controller attaches an event to the button. Then when you click the button, the JS Controller sends a request to the PHP controller. The PHP controller (just a simple switch) figures out what was requested and calls the appropriate object of the business model. In my simplistic case, the abovementioned "model object" is just a simple function, but this can be easily built upon. The Model returns (JSON-encoded) response, in this case it's a list of installed PHP extensions. Now the response is received by the View JS and it updates the page. After that the View calls another function from the JS controller that attaches new events to the new content. (Yep, a little glitch here, maybe it would have been better if the Model's response is handled by the JS controller which in turn calls the JS view updater, but anyway this is easy to fix)

Directory layout

Here's the directory structure:
AJAX MVC dir

One might argue that it's better if you don't mix .js, .css and .php files in the same directory but the whole idea is open to interpretations anyway, it's just an illustration of the idea.

The code for the example

We get to the fun part, the actual implementation. So we start with a simple .html page, the initial part of the view.

This is index.html

<?xml version="1.1" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
  <title>Welcome</title>
  <link rel="stylesheet" href="../view/styles.css" type="text/css" media="all" title="Default styles" />
  <script language="javascript" type="text/javascript" src="../_extras/yui/build/yahoo/yahoo-min.js"></script>
  <script language="javascript" type="text/javascript" src="../_extras/yui/build/event/event-min.js"></script>
  <script language="javascript" type="text/javascript" src="../_extras/yui/build/connection/connection-min.js"></script>
  <script language="javascript" type="text/javascript" src="../view/updates.js"></script>
  <script language="javascript" type="text/javascript" src="../controller/behaviours.js"></script>
</head>
<body>

  Welcome to my app!
  <br />
  <form action="" method="post">
    <input type="button" name="b" id="thebutton" value="I'm a button, click me!" />
  </form>
  <div id="content">&nbsp;</div>

</body>
</html>

As you can see, nothing special, simply including the CSS styles, the YUI "extras" and two other javascripts - one part of the View and one that is part of the Controller.

The Controller JS is responsible for attaching an event listener to the button.

This is an excerpt from the behaviours.js

// the behaviour class
var behaviours = {

    phpcontroller: "../controller/switch.php?request=",

    // more behaviour.methods….
}

// initial page load, attach onload event(s)
YAHOO.util.Event.addListener(
    'thebutton', 'click', behaviours.theButtonClick);

Now when the user clicks the button, the method behaviours.theButtonClick() is executed. It fires a request to the PHP controller switch and says that the request type is "loadSomething":

theButtonClick: function(e) {
  alert('Ouch! nnOK, I'll make a request for ya, buddy!');
  YAHOO.util.Connect.asyncRequest (
      'GET',
      behaviours.phpcontroller + 'loadSomething',
      {success: updates.writeContent}
  );
},

The PHP controller (controller/switch.php) receives the request, does a simple switch to validate the request type and then calls the appropriate (in my case just a simple) function from the business model. Here's the full switch.php code:

<?php
// is this a request?
if (empty($_GET['request'])) {
  die();
}
// get the business logic
include_once '../model/business.php';

// figure out the request
// and call the business logic object
switch ($_GET['request'])
{
  case 'loadSomething':
    echo loadSomething();
    break;
  case 'loadSomeMore': // not used, example
    echo loadSomeMore();
    break;
}
?>

The function loadSomething() from the PHP model gets a list of installed PHP extensions, encodes them into JSON and sends them back. This is a full listing of the ../model/business.php

<?php
function loadSomething() {
  $extensions = get_loaded_extensions();
  return '["'. implode('","', $extensions) . '"]';
}
?>

If you go back and look at the AJAX request, you'll see that on success, I call the updates.writeContent() method. The ../view/updates.js script contains stuff that updates the HTML of the original page, so its place is in the View part of the app. writeContent simply creates an HTML table with the results (the list of PHP extensions). Then I wanted to attach event listeners to this table just to change color, but it can be more than that. Attaching events is a job for the JS Controller, therefore a method of its class is called. Here's a full listing of updates.js:

var updates = {

  writeContent: function (xmlhttp) {
    if (!xmlhttp.responseText) {
      alert("I got nothing from the server");
    }
    var data = eval(xmlhttp.responseText);
    var write_to = document.getElementById('content');
    write_to.innerHTML = ''; // yeah, I know

    var html2dom_root = write_to;
    var table = document.createElement("table");
    var table_1_tbody = document.createElement("tbody");
    for (var i in data) {
      table_1_tbody_2_tr = document.createElement("tr");
      table_1_tbody_2_tr_1_td = document.createElement("td");
      num = 1 + parseInt(i);
      table_1_tbody_2_tr_1_td_1_text = document.createTextNode(num);
      table_1_tbody_2_tr_1_td.appendChild(table_1_tbody_2_tr_1_td_1_text);
      table_1_tbody_2_tr.appendChild(table_1_tbody_2_tr_1_td);
      table_1_tbody_2_tr_2_td = document.createElement("td");
      table_1_tbody_2_tr_2_td_1_text = document.createTextNode(data[i]);
      table_1_tbody_2_tr_2_td.appendChild(table_1_tbody_2_tr_2_td_1_text);
      table_1_tbody_2_tr.appendChild(table_1_tbody_2_tr_2_td);
      table_1_tbody.appendChild(table_1_tbody_2_tr);
    }
    table.appendChild(table_1_tbody);
    html2dom_root.appendChild(table);

    behaviours.updateTableBehaviour();
  }
}

(BTW, for the DOM part I'm used the help from my little tool html2dom to make my life a bit easier)

And finally here's the rest of the JS controller (behaviours.js), the method behaviours.updateTableBehaviour() that adds an event listener to the new table and the trClick() that handles clicks on this table. On click, it justs changes the color of the underlying row.

  trClick: function (e) {
    var target = (e.srcElement) ?
      e.srcElement.parentNode : e.target.parentNode;
    if (target.tagName == 'TR') {
      if (target.className == 'tr-on') {
        target.className = '';
      } else {
        target.className = 'tr-on';
      }
    }
  },

  updateTableBehaviour: function () {
    var el = document.getElementById('content').firstChild;
    YAHOO.util.Event.addListener(
      el, 'click', behaviours.trClick);
  }

Demo and downloads

  • Demo - the live example
  • Zipped demo - all the source code for the example
  • Template - the source code for the example but with the example part commented, so you can use it as a template for your next AJAX project. The only thing you need to do is to drop the YUI in the _extras/yui folder.

Thank you for reading, any comments welcome!

 

Fly Yahoo UI

Tuesday, March 28th, 2006

Here goes the rhyme:
Make your content management application fly
with the Yahoo library of JavaScript UI…

Making fancy UI stuff has never been easier. Google released their JavaScript XSLT library, Prototype is everywhere, others too… now Yahoo! released their UI library. I took a look at Y! stuff, let me show you what I did.

ยป Before you continue, feel free to check the demo at any time

CMS

OK, let the first one who has never coded some sort of Content Management System throw a stone. Yep, that's what I thought. Writing a CMS is like the new Hello World ;)

A very simplistic CMS would have a bunch of data records and options to add new records, to modify existing entries or to delete them. Let's see how to do the deletion fly, web 2.0. style.

The table

The records are stored in a very, very basic table that has its markup to the bare minimum. The table has an ID "records" and every link also has an id like "delete-1", "delete-2", etc, where the numbers are the record IDs from the database table. The beauty of this is that the links go to delete.php, a server side script that will delete the requested record and return something. So this will work even for JS-disabled browsers. For those that are enabled though, we'll make the links not navigate to delete.php, but send a small AJAX request to that same server-side script.

Here's the table markup, as you can see, very minimalist.

<table id="records" align="center">
  <caption>Table with records in it</caption>
  <tbody>
    <tr>
      <th>Last Name</th>
      <th>First Name</th>
      <th>&nbsp;</th>
    </tr>
    <tr>
      <td>Man</td>
      <td>Super</td>
      <td><a href="delete.php?id=1" class="delete" id="delete-1">delete</a></td>
    </tr>
    <tr>
      <td>Woman</td>
      <td>Cat</td>
      <td><a href="delete.php?id=2" class="delete" id="delete-2">delete</a></td>

    </tr>
    <tr>
      <td>Man</td>
      <td>Invisible, The</td>
      <td><a href="delete.php?id=3" class="delete" id="delete-3">delete</a></td>
    </tr>
  </tbody>
</table>

Attach an event

To make the links call a JavaScript function, we'll have to attach an event to them. For this, let's use Y!s event library (event.js). Here's all that's needed:

// add an onclick listener to the records table
YAHOO.util.Event.addListener('records','click',clickHandler);

Very simple, right? Now we've attached an event to the whole table. That's far more convenient than attaching an event to each and every link. With the line above we stated our desire that whenever there is a click somewhere inside the table, the function clickHandler() is executed. Let's see what's in there.

clickHandler()

function clickHandler(e) {
    // make sure the default event (vistiting the link) is not executed
    YAHOO.util.Event.preventDefault(e);
    // which element was clicked (target is in W3C, srcElement is in IE)
    var target = (e.srcElement) ? e.srcElement : e.target;
    // if the target element has class named "delete", that's our dude
    if (target.className == 'delete') {
         if (confirm('Sure you wanna delete?')) { // be polite, ask first
            // figure out which record to delete
            var id_to_delete = target.id.replace('delete-','');
             // the ajax stuff
            YAHOO.util.Connect.initHeader('my_ajax','oh yes it is');
            YAHOO.util.Connect.asyncRequest (
                'GET',
                'delete.php?id=' + id_to_delete,
                {success: removeRow,
                 failure: function (xhr) {
                                alert('Error :(  try later…');
                           },
                 cell_id: target.id
                }
            );
        }
    }
}

As you see, I've put quite a bit of comments to illustrate what's going on at each line. Maybe the AJAX part will still need some more details though.

First, the header setting. Using YAHOO.util.Connect.initHeader we'll send an additional HTTP header, called my_ajax with some funny value. The purpose of this exercise is to tell our server-size delete.php script that this is an AJAX request and it will return a different response once it does its thing.

The actual request is made using YAHOO.util.Connect.asyncRequest. We pass the request method (GET), the URL (delete.php?id=12345) and then we pass an object to tell that if the request was successful, the function removeRow() should be called. On failure, an anonymous function is executed to simply alert that something's wrong.

The success and failure methods form the so-called callback object. You need a callback to instruct the AJAX call what to execute next. In the callback object you can also pass anything you like, like the property cell_id for example.

removeRow()

This function will be executed once the AJAX call completes successfully. In the function we'll:

  1. Check if the server-side returned "ok" response.
  2. Paint the row to be removed red
  3. Animate the row until it fades
  4. Remove the row completely

You may think that part 1 of this plan is funny? Laugh all you want, but if you think about it, it may start making sense ;) I mean you're right, it's AJAX, it should be Asynchronous JAvascript and XML. But we don't need no stinkin' XML for such a minor thing. A simple, friendly 'ok' is all it takes. No <root>s, no getElementsByTagName().item() end so on.

One hiccup in the plan is that the Yahoo UI doesn't yet support color animation. But we can use opacity animation. Good, great. Only that animating the opacity of a table row doesn't work in IE6. Shoot! Solution? Loop though the cells of the row and animate them. It will work.

Once all is animated and done, the last thing is to remove the row. It's easy, but we want to make sure it's done only after the animation is completed. Hence the "subscription" the Y! library provides.

Here's the function in its whole, see the comments.

function removeRow (xhr)
{
    if (xhr.responseText != 'ok') {
      alert('Error, try later…');
      return;
    }
    // now let's remove the row that contained the record
    var row = document.getElementById(this.cell_id).parentNode.parentNode;
    row.style.backgroundColor = '#ff0000';  // make it red

    // will make the removal animated
    var attributes = {opacity:{from:1, to:0}}; // will animate until opacity is 0

    // loop through each cell and animate it
    // animating the opacity of a row (not all cells) is preferable,
    // but it's not happening on IE
    for(var i=0, thecells = row.cells; i < thecells.length; i++) {
        var anim = new YAHOO.util.Anim(
            thecells[i], // animate what
            attributes,  // animate how
            1,           // for how long
            YAHOO.util.Easing.easeOut // and a dynamics touch
        );
         if (i == thecells.length - 1) {
            anim.onComplete.subscribe( // execute on completion
              function(){
                row.parentNode.removeChild(row); // remove the row
              }
            );
        }
        anim.animate();
    }
}

The server-side check

As mentioned earlier, delete.php will delete the row regardless of how it's called - directly from the browser or through the AJAX call. The difference is that the response will be different. How does delete.php know how to respond? Looking at the extra header we sent, of course! Here goes:

<?php
// First off, do the actual record deletion
// check for permissions, etc

// When it comes to reporting,
// respond differently if called with AJAX request or normally
if (!empty($_SERVER['HTTP_MY_AJAX'])) {
    // this is an AJAX request
    // the response can be XML, JSON, whatever
    // for my purposes, a simple status reply is enough
    echo 'ok';
} else { // a normal browser request
    // do something, like header('Location: …') for example
}
?>

Conclusion

So that's it, hope you enjoyed the trip. Once again, the demo is here, the Yahoo UI is here and … have fun with the Y! library! Though despite the excitement of the new toy, please do not forget to sleep daily, it's important (I hear) ;)