MTMultiSelect

A paginated, filterable multi-select widget for MooTools

The standard HTML list box is a much maligned user interface element. It requires use of both mouse and keyboard to operate, and with any more than a few elements it requires the user to scroll. It's also fairly ugly and difficult to style. I've seen a few nice javascript widgets that improve them, but often they still forced the user to scroll, or simply created lists that pushed page contents down.

I decided to write my own, mostly as an exercise, but also to fit a few requirements I thought were essential to an easy-to-use multi-select element: it should occupy a fixed vertical space, the user should never have to scroll, the user should be able to quickly see the elements she has selected and the elements she has yet to select, and finally, it should allow the user to filter down the elements that are important. Also, I wanted something that worked with MooTools, a great javascript framework.

MTMultiselect is my answer. It's a easy-to-use, paginated, filterable multi-select widget built on MooTools. Take a look at the demo page or download the code from bitbucket.org.

17 comments add comment

  • That is genius, you should make more of the fact that it degrades back to a standard multiselect if javascript is disabled. Great job

    - Dave Hunter on 09/16/2009

  • Justin, Hi. Thanks for making this available. Would like to see an a multicolumn example using MooTools sortable columns. Would also like to see the multicolumn example ported to jQuery. Thanks. Rob

    - Robert Visser on 09/17/2009

  • Hi Justin the script is really great ! I'd consider two things though: 1. this.setpagenum(evt.target.text); doesn't work in IE 6 (nor does the png transparency) i'd suggest to use this.setpagenum(evt.target.innerHTML); instead. 2.perhaps it'd be resonable to filter case insensitively - like this: filter_by_text = function(item){ //return item.text.contains(evt.target.value); return item.text.toLowerCase().contains(evt.target.value.toLowerCase()); }; Thanks a lot for your work - it has helped me a lot ;)

    - Jakub Miara on 09/18/2009

  • one more thing: I'd use some non existent target for paginator href (#asdklhsldkh instead of just #) otherwise every click on paginator scrolls the page to the top. It behaves weirdly in IE anyway, but scrolls only to the top of the control. Regards, Jakub

    - Jakub Miara on 09/18/2009

  • Jakub - Thanks for the IE 6 catch. Also, I think I'll make the search case-insensitivity an option.

    - Justin Donato on 09/20/2009

  • Good job, how to select item with keyboard?

    - Jean-Phi on 10/06/2009

  • Nice job. I haven't taken a look at your code too much in depth yet. Not sure if it exists yet (and if not I might try to add this in myself), but what I think might be a useful option is a "max select amount" to limit the number of selected options that can be made.

    - Adam on 10/07/2009

  • Okay, well that was a SIMPLE fix. If you want to add a maximum to the allowable select, try amending the code as follows: 1) In the DisplayList class, under options, add this: max_select: 2 2) In the DisplayList class, amend the build function: build: function(opts){ // If there's already an ol, remove it. var old = this.options.view.getElement('ol'); if(old !== null) old.destroy(); // create the list to hold the visible elements list = new Element('ol'); place = this.options.paginator_on_bottom ? 'before' : 'after'; list.inject(this.options.view.getLast(), place); //this.options.view.grab(list); opts.each(function(item){ var li = new Element('li',{ 'class': item.selected ? this.options.selectedcls : null, 'text': item.get('text') }); li.store('select', item); list.grab(li); li.addEvent('click', function(evt){ if(this.numselected() < this.options.max_select){ //this is the change here evt.target.toggleClass('selected'); evt.target.retrieve('select').selected = evt.target.hasClass('selected'); this.fireEvent('rebuild', this.numselected()); } else{ /*something to do if there are too many selected items*/ } }.bind(this)); }.bind(this)); } Simple edits that require adding a conditional during the click event.

    - Adam on 10/07/2009

  • Sorry for the repeat posting. As a quick change to the above item, I need to change my conditional to the code below. If you don't change this, then you won't be able to un-select an option! if(this.numselected() < this.options.max_select || evt.target.hasClass('selected') == true){

    - Adam on 10/07/2009

  • Very usefull your comment Adam. I set only one option, and I want that when a user press another box even other box is selected to unselect the selected box and select the box he clicks. I belive that this should be done on else, but I don't know how to deselect all selected items. Thanks, Marius.

    - marius on 01/14/2010

  • Just wondering, how can i start this js Object with several options already selected? I want it to remember the options after a form is submitted.

    - Luke on 02/10/2010

  • Luke, Just set the options to selected in the HTML and they'll be selected in the widget. It should work seamlessly with any form you have.

    - Justin Donato on 02/19/2010

  • Hi there Thanks for this great plugin. I am having trouble getting the values of selected options from the list on an DOM event? Is there a function that return the current values of selected options?

    - Ads on 05/14/2010

  • Ads, Inside the DisplayList you can access the options like this: this.options.datasrc.getChildren().each(function(item){ if(item.selected) // do something; });

    - Justin on 05/14/2010

  • hi, fantastic plug-in! just a question, anyone know how can i add "select all/deselect all" options for all elements on all pages ? bye!

    - Mark on 05/25/2010

  • Hey Justin, Loving your plugin. I made some minor adjustments where it looks like you started coding to add classes for the total, selected and unselected buttons but then stopped! So i added the code to enable this. Lines 299 - 303 classes: { 'total': 'mttotal', 'selected': 'mtselected', 'unselected': 'mtunselected' } Lines 316 - 328 this.totalbtn = this.makebtn(this.options.labels.total, this.options.classes.total, this.showtotal, this.options.datasrc.getChildren().length); ul.grab(this.totalbtn); this.selectedbtn = this.makebtn(this.options.labels.selected, this.options.classes.selected, this.showselected, this.numselected); ul.grab(this.selectedbtn); this.unselectedbtn = this.makebtn(this.options.labels.unselected, this.options.classes.unselected, this.showunselected, this.options.datasrc.getChildren().length - this.numselected); Lines 359 - 363 makebtn: function(label, clas, func, prefix){ var li = new Element('li'); var btn = new Element('a', {'html': label, 'href': '#cpcspswdnbd', 'class': clas, Enjoy!

    - John on 06/22/2010

  • Mark, did you ever find a way of using a select all function? This would make this very useful for me

    - James Roberts on 09/20/2010

Recent Projects all →

See Also