Mootools Sortable List Example
Recently, I’ve been playing around with mootools - the new Javascript Framework from the Mad4Milk guys. As you’ve probably already noticed, at the time of launch, there wasn’t much available in terms of demos and documentation. As time passes, the documentation is improving and people are adding resources to the mootools wiki that is available now.
One of the first examples available on the web was a Drag and Drop example provided by Jonathan Snook on his blog (See example here). After I played around with his example, I was interested in testing out the Sortable List functionality available in the effects portion of mootools. I put together a quick example of a Sortable List implementation, so feel free to check it out and view the source.
I tried to go a bit farther than simply implementing the Sortable List functionality by adding an Opacity effect and making additions to the onDrag and onComplete functions of the dragger. For a more in-depth explanation of the code, read the full post.
Creating a Sortable List
Creating a Sortable List with the mootools framework is fairly straight-forward. However, some of the code in the Sortables class has been stripped out to allow the list to sort properly. When I first started playing with the Sortables, I was using Revision 57 of mootools. I noticed that the sorting of the list was very buggy and not working properly at all, so I updated to Revision 64 and everything worked fine. Well… sorta. In Revision 58, the droppables logic was stripped out of the Sortables class because it was apparrently causing problems. With this change, the basic functionality of Sortable Lists is available, but it will be nice when the droppables are added back in so we can make use of the onOver, onLeave and onDrop functions.
To create a Sortable List with mootools, all you need to do is create a new Sortables object and have each sortable element extend that object. So first, I defined my list of sortable elements and gave each of them a className of “sort” so I can easily fetch them from the DOM:
<ul>
<li class="sort">Item 1</li>
<li class="sort">Item 2</li>
<li class="sort">Item 3</li>
<li class="sort">Item 4</li>
</ul>
Now, we just need to create our Sortables object and loop through our array of sort items to have each of them extend that Sortables object. I like the concept of having a single function to call to make a Sortable List, similar to the way you can call makeDraggable() on an element. To give myself this ability for Sortable Lists as well, I extended Array and added a makeSortable() function that takes in a sortableOptions object to use when instantiating our Sortables object:
Array.extend({
makeSortable: function(options){
var Sortable = new Sortables(this, options);
this.each(function(el){
el.style.cursor = 'move';
el.extend(Sortable);
})
}
});
The makeSortable() function just creates our Sortables object, then loops through the Array of sort items it has been called on to perform some additional logic. In this case, it sets the cursor style of each sort item to ‘move’ to indicate that a user can drag it, and then has each sort item extend the Sortables object we just created.
Now that we have the new makeSortable() function, all we have to do is use the helpful $S() function to grab all of our sort items and call makeSortable on them with any additional options we might want. This is done in the window.onload() function so our Sortable List is ready to go as soon as the window is done loading.
window.onload = function() {
$S('#container li.sort').makeSortable(sortableOptions);
}
Using onStart, onComplete and onDrag
At this point, we have a perfectly functioning Sortable List. But that’s a little too easy, so I decided to add a status indicator below the list that informs the user of the current operation being performed on the list. This is just an empy span element in the source with the id ’status’ so I can get a handle on it later.
I will update the contents of that span element with information while the user is sorting the list.
To do this, I need to add logic to the onDrag and onComplete functions of the dragger object that is created in the Sortables class. Unfortunately, Sortables is not setup to accept function definitions for onStart, onComplete and onDrag. It would be nice if you could specify custom functionality for these events in the sortableOptions that are used to initialize the Sortables object and then have the base logic of those functions extend whatever was passed in with the sortableOptions.
Since that doesn’t seem to be possible right now, I decided to use the implement() feature of mootools to create a new implementation of the initialize function in the Sortables class. This isn’t very efficient because I want to keep most of the functionality of the Sortables class and only need to add a few lines of code for my custom behavior, but I can’t think of a better way to do it right now. Below is my custom implementation of the initialize function in the Sortables class:
Sortables.implement({
initialize: function(elements, options){
this.setOptions(options);
this.options.handles = this.options.handles || elements;
var trash = new Element('div').injectInside($(document.body));
$A(elements).each(function(el, i){
var copy = $(el).clone().setStyles({
'position': 'absolute',
'opacity': '0',
'display': 'none'
}).injectInside(trash);
var elEffect = el.effect('opacity', {
duration: this.options.fxDuration,
wait: false,
transition: this.options.fxTransition
}).set(1);
var copyEffects = copy.effects({
duration: this.options.fxDuration,
wait: false,
transition: this.options.fxTransition,
onComplete: function(){
copy.setStyle('display', 'none');
}
});
var dragger = new Drag.Move(copy, {
xModifier: false,
onStart: function(){
copy.setHTML(el.innerHTML).setStyles({
'display': 'block',
'opacity': this.options.maxOpacity,
'top': el.getTop()+'px',
'left': el.getLeft()+'px'
});
elEffect.custom(elEffect.now, this.options.maxOpacity);
}.bind(this),
onComplete: function(){
copyEffects.custom({
'opacity': [this.options.maxOpacity, 0],
'top': [copy.getTop(), el.getTop()]
});
elEffect.custom(elEffect.now, 1);
$('status').setHTML('Sorting complete!');
statusFade.custom(1,0);
}.bind(this),
onDrag: function(){
if (el.getPrevious() && copy.getTop() < (el.getPrevious().getTop()))
el.injectBefore(el.getPrevious());
else if (el.getNext() && copy.getTop() > (el.getNext().getTop()))
el.injectAfter(el.getNext());
statusFade.clearTimer();
$('status').setOpacity(1).setHTML('Sorting in progress...');
}
});
this.options.handles[i].onmousedown = dragger.start.bind(dragger);
}, this);
}
});
The lines of code highlighted in red are the only additions I made to the initialize function in my implementation. Now, the code that initializes my Sortables object will automatically use my implementation and it will get the benefits of the custom logic I have added.
You’ll notice that in the onComplete and onDrag functions I am updating the contents of the status element I created earlier through the use of the setHTML() function which is really just a wrapper around innerHTML.
$('status').setHTML('Sorting complete!');
You’ll also notice that I am doing some other stuff with an Opacity effect that I created. When a user is sorting the list, the status element will displaying the text Sorting in progress.... When they are finished, it will display the text Sorting complete!, but I don’t want that status to just sit there forever. Therefore, I created an Opacity effect that will cause the status text to slowly fade out after a set delay.
Creating an effect
To do this, I created the effect in the window.onload event so it is available to use as soon as the page is done loading:
statusFade = new Fx.Opacity('status', {duration: 2000, transition: Fx.cubic});
This basically says that I want to create an Opacity effect on the element with id of ’status’ and then specifies some options for that effect. Since I want my status text to fade slowly, I gave the effect a duration of 2000 milliseconds. I also specified a cubic transition which will cause the fade to perform slowly at first and speed up toward the end.
When I want to use this effect, I can call the custom() function on it and specify beginning and ending parameters. In this case, I want to go from zero Opacity (fully visible) to 100% Opacity, I call my effect like so:
onComplete: function(){
copyEffects.custom({
'opacity': [this.options.maxOpacity, 0],
'top': [copy.getTop(), el.getTop()]
});
elEffect.custom(elEffect.now, 1);
$('status').setHTML('Sorting complete!');
statusFade.custom(1,0);
}
In the above code, when a user drops the item they are sorting, the onComplete function is executed. At this time, the contents of the span element with an id of ’status’ are updated and then the effect is called to cause the status to fade out slowly. Calling custom(1,0) will execute my Opacity effect and cause the element to go from fully visible (Opacity of 1) to invisible (Opacity of 0).
I also want to update the contents of the status element while a user is sorting the list. The problem is, after I fade out the status element, any subsinquent sorting status will not be visible to the user since that element is still invisible. Also, another problem is that once the status text starts fading, it won’t stop. So if a user drags an item to a new location and drops it, the text “Sorting complete!” will be displayed and begin to fade. But if the user then starts dragging an item again, the “Sorting in progress…” text will be displayed but continue to fade until it is gone. The following code illustrates how I dealt with both of those issues:
onDrag: function(){
if (el.getPrevious() && copy.getTop() < (el.getPrevious().getTop()))
el.injectBefore(el.getPrevious());
else if (el.getNext() && copy.getTop() > (el.getNext().getTop()))
el.injectAfter(el.getNext());
statusFade.clearTimer();
$('status').setOpacity(1).setHTML('Sorting in progress...');
}
In the onDrag function, I want to interrupt the fade effect if a user begins to sort the list again. To do this, we can use the clearTimer() method on the effect. By calling this method on the fade effect, it causes the effect to finish immediately. Once that is done, I simply set the Opacity of the element back to 1 (fully visible) before updating the contents of its innerHTML. Now the user will always be able to see the proper status of their operations.
Conclusion
That pretty much explains the logic behind all the pieces of this Sortable List example. I know it’s not very flashy, but the purpose of this example was to illustrate some of the cool things that can be done with a Sortable List and when they will be executed. I also figured it would be nice to explain a bit about using an effect as well. In reality, instead of updating a status element with different text when a user is sorting or finishes sorting, a back-end process would probably be triggered via an Ajax request to update a datasource or something similiar in order to store the state of the list.
I hope this lengthy explanation is helpful to some people and feel free to leave comments or suggestions for improvements to the code.
October 14th, 2006 at 5:38 am
Hello,
thanks for the tutorial Bill. We need more and more examples like this one on how we can use and twick mootools as it’s really a powerfull js tool.
Been working with drag and drop and sortable list with mootools too.
Here you can find an example of a 2-dimensional sortable list (multiple columns and rows) based upon the rewrite of the Sortables class to take care of the X axis too :http://chatalors.chez-alice.fr/sortables.html
And some important comments about it here :
http://freewebsfarms.com/forums/viewtopic.php?id=1013
October 14th, 2006 at 10:04 am
Hey Absynthe –
Thanks alot for your work on the 2-dimensional sortable list! I had noticed the problem with the x-axis when putting together my example, but hadn’t figured out how to fix it. Looking forward to taking a closer look at the source soon.
October 21st, 2006 at 2:36 pm
Hey, really thanks for the examples!
I’m trying to replace all the things i’ve done with script.aculo.us to mootools for its lightweight, and the sortable lists was still a problem.
This example help me a lot.
But something that i’m still missing is the serialize function… how can i get a serialized string telling me the order of the sortable list?
Thanks again for your kindness!
November 4th, 2006 at 9:14 pm
[…] Bill Krueger’s TechBlog » Blog Archive » Mootools Sortable List Example (tags: javascript mootools tutorial) […]
November 9th, 2006 at 9:09 am
@Esteban:
I had the same problem - I was switching from script.aculo.us to mootools, and I needed the serialize function. I’m not proficient enough in javascript to add it to the class, but I wrote a function to serialize. I know it’s a few weeks late, but I hope it can help you too.
I posted it on my blog, here is the link: http://www.jesirose.com/entry.php?id=10
Hope that helps!
November 9th, 2006 at 9:30 pm
@Jessica and Esteban:
I just made a new blog post that details a serialize function I wrote to be included in the Sortables class in order to gain the necessary serialization functionality. Check it out here:
http://techblog.billkrueger.com/2006/11/09/serialize-function-for-mootools-sortables/
November 9th, 2006 at 9:33 pm
Also, there is an update to this example that shows how to pass functions into the onStart and onComplete of the dragger for your Sortables object in my blog here:
http://techblog.billkrueger.com/2006/10/22/sortable-list-example-updated/
This cuts down on alot of duplicate code and is much more efficient so check it out.
January 18th, 2007 at 1:48 am
[…] למשל: Sortable Lis. […]
February 24th, 2008 at 10:53 pm
[…] http://techblog.billkrueger.com/2006/10/11/mootools-sortable-list-example/ — 可排序列表实例 […]
February 25th, 2008 at 6:55 pm
[…] — AjaxPlus:为AJAX增加超时事件 参与评论 评论 RSS 下一篇: mootools教程 […]
April 9th, 2008 at 2:26 am
[…] 28. Sortable List Example […]
May 3rd, 2008 at 7:08 am
[…] 28. Sortable List Example […]
December 15th, 2008 at 1:01 am
[…] Sortable List Example […]
January 27th, 2009 at 12:13 pm
[…] 28. Sortable List Example […]
April 6th, 2009 at 12:51 am
Fantastic thank you. Find the files you are looking for at http://all-shares.com the most comprehensive source for free-to-try files downloads on the Web
May 5th, 2009 at 9:59 am
[…] 28. Sortable List Example […]