DZone Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world

Snippets has posted 5883 posts at DZone. View Full User Profile

FirstLast (real Classes For Ajaxability)

09.24.2006
| 2055 views |
  • submit to reddit
        I know CSS has the :first-child and :last-child pseudo classes.  But :first-child is a part of CSS 2 and has poor browsers support and :last-child is a part of CSS 3 and that's not worth thinking about using yet.  The idea is sound though, so I worked up this JavaScript method of getting the same effect.

One advantage of doing this in JavaScript rather than using CSS is that the classes will change if you reorder the child nodes with more JavaScript.  In my case I'm using <a href="http://wiki.script.aculo.us/scriptaculous/show/Sortables">Scriptaculous sortables</a>.  Or to be more specific, I'm using a <a href="http://api.rubyonrails.com/">Ruby on Rails</a> helper method to make something sortable through Scriptaculous:
<%= sortable_element 'image-list',
    :constraint => false,
    :url => { :action => 'sort', :issue_id => params[:issue_id] }
-%>

I've chosen to not make this script run unobtrusively.  Most of it can go into a file and be included in the header or added to your own common JavaScript include file.  To get it to run on a page you will need to include something like the below in each page.
<script type="text/javascript">
//<![CDATA[
    Event.onDOMReady(function(){FirstLast.go("image-list")});
    Ajax.Responders.register({
        onComplete: function(){FirstLast.go("image-list");}
   });
//]]>
</script>
I use the <a href="http://www.vivabit.com/bollocks/2006/06/21/a-dom-ready-extension-for-prototype">DOMReady extension</a> for Prototype's Event object, but you can use any loader you want.  The Ajax.Responders.register part reruns the script after a drag-and-drop operation (or any Ajax operation really).

<a href="http://script.aculo.us/downloads">Download Scriptaculous and Prototype.</a>

var FirstLast = {
    go: function(el) {
        el = $(el);

        // Whitespace nodes need to be cleaned to get the intended effect
        var children = Element.cleanWhitespace(el).childNodes;

        // Return if there are not any children
        if (0 == children.length) return
        
        if (1 == children.length) {
            // Cheap shortcut if there is only 1 child node
            children[0].addClassName(this._firstChildClassName);
            children[0].addClassName(this._lastChildClassName);
        } else {
            for (var i = 0; i < children.length; i++) {
                switch (i) {
                    // First child
                    case 0:
                        children[i].addClassName(this._firstChildClassName);
                        children[i].removeClassName(this._lastChildClassName);
                        break;
                    // Last child
                    case children.length - 1:
                        children[i].removeClassName(this._firstChildClassName);
                        children[i].addClassName(this._lastChildClassName);
                        break;
                    // Every child other than the first or last
                    default:
                        children[i].removeClassName(this._firstChildClassName);
                        children[i].removeClassName(this._lastChildClassName);
                        break;  // I know it is unnecessary
                }
            }
        }
    },
    // Pseudo Private methods and attributes
    _firstChildClassName: "first",
    _lastChildClassName: "last"
};