logo

A Lesson For All Would-Be JavaScript Hackers

Here's a hacker's lesson I learnt the hard way — if you're going to make any changes or additions to a JavaScript library don't do it directly in the source code for that library.

Remember my Drag-n-Drop Sorting of Documents article from this time last year (Rough Cut? Huh!)? Here's a demo. It uses the scriptaculous drag and drop to convert a simple <ul> element in to a sortable list. Re-sorting this list sends a POST message to Domino to tell it the new order of the documents. To get this to work with Domino I had to hack the code a little..

The scriptaculous Sortable object has a method called serialize:

The Sortable object also provides a function to serialize the Sortable in a format suitable for HTTP GET or POST requests. This can be used to submit the order of the Sortable via an Ajax call. See Sortable.serialize

So, given a UL element with an id of "SortOrder" and three child LI items the serialize method will produce a string like:

SortOrder[]=IDofLI1&SortOrder[]=IDofLI2&SortOrder[]=IDofLI3

This can then be POSTed to the server for processing. Domino inerprets POST data with repeated keys as a multi-value item. The use of []s is a Rails/PHP convention which confuses Domino and so I had to get rid of them, so that the string would look like:

SortOrder=IDofLI1&SortOrder=IDofLI2&SortOrder=IDofLI3

To do this I found the serialize method in scriptaculous's dragdrop.js file and removed the [] from the line of code with the comment shown below.

var Sortable = {
  serialize = function(element) {
    element = $(element);
    var options = Object.extend(Sortable.options(element), arguments1 || {});
    var name = encodeURIComponent(
      (arguments1 && arguments1.name) ? arguments1.name : element.id);
    
    if (options.tree) {
      return Sortable.tree(element, arguments1).children.map( function (item) {
        return name + Sortable._constructIndex(item) + "[id]=" + 
          encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
      }).flatten().join('&');
    } else {
      return Sortable.sequence(element, arguments[1).map( function(item) {
        return name + "[]=" + encodeURIComponent(item); //removed the [] from here!
      }).join('&');
    }
  }
}

This change I made in the source code itself. Cue one year later and I'm working on a new version of an application for a customer, which uses this sorting feature. This seems like a good time to upgrade the versions of Prototype and Scriptaculous in use to the most current, so I do. Expecting problems due to the upgrade, I test it all out, only to find the sorting feature is broken. After some messing about I found the extraneous [] characters and it dawned on me what I'd done wrong — the upgrade had over-written the old JS file and so my hack went with it.

To fix this the easiest option would have been to re-edit the scriptaculous dragdrop.js file and make the same change again. However, this would be stupid and have meant I'd learnt nothing from having wasted an hour of time scratching my head. What I needed to do was make the change, but not in the dragdrop.js file, so that any future upgrades wouldn't break the application.

What I did was leave the source code alone and then place the following snippet of code in my own "global.js" file:

Sortable.serialize = function(element) {
    element = $(element);
    var options = Object.extend(Sortable.options(element), arguments1 || {});
    var name = encodeURIComponent(
      (arguments1 && arguments1.name) ? arguments1.name : element.id);
    
    if (options.tree) {
      return Sortable.tree(element, arguments1).children.map( function (item) {
        return name + Sortable._constructIndex(item) + "[id]=" + 
          encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
      }).flatten().join('&');
    } else {
      return Sortable.sequence(element, arguments[1).map( function(item) {
        return name + "=" + encodeURIComponent(item);
      }).join('&');
    }
}

As my global.js file was referenced below the scriptaculous files in the page's header it was loaded afterwards and so the original Sortable.serialize method was over-written by my own. Any upgrades to dragdrop.js in the future won't break my code.

Moral of the story being — if you're going to hack a JavaScript library do it in your own space.

Comments

    • avatar
    • Jake Howlett
    • Wed 14 Feb 2007 12:32 PM

    Adendum (I can't now edit this post as all the <pre> formatting will be screwed up):

    Anybody who has tried to use the drag sorting demo database in their own applications, which already had a copy of the scriptaculous files in it, might have been a bit stumped as to why it didn't work -- it had to use the dragdrop.js file included in the demo!. Sorry if I caused any undue stress.

    • avatar
    • Jake Howlett
    • Wed 14 Feb 2007 01:22 PM

    Furthermore I've now updated the demo db for the original article and upload version 0.5, which shows the method I'm talking about here.

  1. Overriding methods is always a safer bet. Good job on translating that topic. I ran into that temptation when I found that the YUI calendar had hard coded image paths.

  2. You probably know this, but if you use list-style(-type):number (or an ordered list), the sort-order updates instantly. The power of CSS :)

    For the user, it looks like it's instantly re-sorted..

    • avatar
    • Jake Howlett
    • Wed 14 Feb 2007 04:59 PM

    Hi Tommy. No, I didn't know that. Nice tip. Thanks. I'll add in to the db now...

    • avatar
    • Jake Howlett
    • Wed 14 Feb 2007 05:02 PM

    Hmm. Ie6 no like it. They all end being 1. Works great in FF though!

  3. It looks like it has something to do with the style-attributes that are set by scriptaculous.

    I've found a quick-and-dirty fix for that, although I'm not sure that it would work every time.

    Try adding this when you submit the changes:

    if(typeof ActiveXObject =="function"){

    $('NewOrder').innerHTML = $('NewOrder').innerHTML.replace(/style="filter.*1"/i,"style=\"position:relative\"");

    }

    What it does, is resetting the style-tag on the item dropped, to style="position:relative" (using case-insensitive regular expression).

    I've only tried this using the Javascript-console bookmarklet in IE, where it worked.

    • avatar
    • YoGi
    • Thu 15 Feb 2007 02:09 AM

    Here is way I do not like prototype.

    With YUI you can just extend it to create your custom object (YAHOO.extend):

    {Link}

    Way much proper.

    • avatar
    • YoGi
    • Thu 15 Feb 2007 02:09 AM

    First way -> why

    sorry.

    • avatar
    • Jake Howlett
    • Thu 15 Feb 2007 05:03 AM

    YoGi. Not sure if it's the same thing or not but Prototype does have an extend method.

    • avatar
    • Brian Miller
    • Thu 15 Feb 2007 09:38 AM

    @Tommy:

    IE fails spectacularly to re-order list numbering when the list container or any of the elements has "hasLayout".

    Reference:

    {Link}

  4. My quick-and-dirty-fix removed the listeners on the node. :

    This bookmarklet does the same (and sets the position to static, like the article suggested):

    javascript:void $$('#NewOrder li').each(function(node){node.removeAttribute("style");node.style.position="static";})

    Some of the nodes still are out of order after drag/drop (I override the list-style using web accessibility toolbar).. Could maybe have something to do with styles from the stylesheet?

    The reason I'm taking an interest in this is that I had planned using CSS to display sort order for the user (making it seem like instant resorting), on a future project at work.

    The real sorting would happen backend.

  5. @Yogi: Extensibility is a part of Javascript, so any object you can prototype (not as in prototype.js but as in defining your own objects), you can later extend.

    @Jake: Another way to tackle the problem would be to write an wrapper function that would call the scriptaculous method and then process the result for you. That way you don't override all that functionality (hence making your code responsible for more or as much stability as the method you are overriding) and you have a layer that, being specifically for the purpose of tweaking the outbound data, provides a centralized point for doing so.

    So, you might have

    // Domino specific wrapper for Scriptaculous.serialize()

    function dominoSerialize(element) {

    var tmpstr = serialize(element); // call to scriptaculous

    regEx = /[\[\]]/g;

    tmpStr = tmpStr.Replace(regEx,"");

    return tmpStr;

    }

    I guess it boils down to an OO approach or an SOA approach. :-) Either is good - just depends on what your goals are. The later permits you to deflect concern about whether your code or theirs is the problem, if one arises.

  6. I found a fix for IE and opera.. It comes down to display:list-item, and zoom:normal!important (and position:static for opera)

    Instead of doing it from the browser, I actually downloaded ithe demo this time :)

    ul.sort li{

    cursor: move;

    background-color:#f1f1f1;

    margin:10px;

    border:1px solid #CCC;

    padding:4px;

    display:list-item;

    list-style-type:decimal;

    zoom:normal!important;

    list-style-position:inside;

    }

    /* opera hack */

    @media all and (min-width: 0px){

    ul.sort li{

    position:static!important;

    }

    }

    *erhm* The power of CSS! (finally)

    with position:static (to get opera to show the numbers), the items doesn't float in the air, but they still re-sort.

    list-style-position:inline puts the numbers inside the li-item, making it look just like the "hard-coded" numbers in your demo.

    Let me know if it works.. I've might have forgotten something..

    • avatar
    • Jake Howlett
    • Thu 15 Feb 2007 04:46 PM

    Thanks Tommy. It's obvious you know your stuff really well. I'll be keeping a keen eye on your blog hoping you make a go of it.

  7. Thank you! Google is my friend.. :)

    I also have to thank you for years of excellent code and examples to play with!!

    I'm kinda lazy with the blog, but if I learn something I get really excited about, I'll probably post it..

    The thing I'm most excited about right now is Mootools ({Link} I think it's 36kB packed with all included. Download a packed version for deployment in your applications, and an unpacked version with documentation for hacking/understanding how to extend the objects.

    They have a really nice php-application that lets you select only what you need (with dependency-checks). I recently downloaded only the Cookie-class for an "older" project that mainly uses Prot./Sciptac. Works great!

Your Comments

Name:
E-mail:
(optional)
Website:
(optional)
Comment:


About This Page

Written by Jake Howlett on Wed 14 Feb 2007

Share This Page

# ( ) '

Comments

The most recent comments added:

Skip to the comments or add your own.

You can subscribe to an individual RSS feed of comments on this entry.

Let's Get Social


About This Website

CodeStore is all about web development. Concentrating on Lotus Domino, ASP.NET, Flex, SharePoint and all things internet.

Your host is Jake Howlett who runs his own web development company called Rockall Design and is always on the lookout for new and interesting work to do.

You can find me on Twitter and on Linked In.

Read more about this site »

More Content

[