Jonathan Tang's Corner of the Web

Projects

JQuery UI Combobox

This is a JQuery plugin for an editable combobox, built according to JQuery UI 1.5 conventions. There are a couple existing combobox plugins, but they either weren't true comboboxes (dropdown only) or they would fail if you tried typing something non-ASCII into the text field. This uses a different approach: the text field is real, but the drop-down is faked as a collection of spans.

Dependencies

Usage

$(selector).combobox() may be called on elements of any type. If the element is a <select> tag, the combobox will attempt to populate itself from the options of the select. Otherwise, the options arguments must have a 'data' field that contains a list of options. By default, those options are displayed verbatim in the drop-down list; you can customize this by providing a function for the listHTML option.

The selected element is completely replaced by a text field, drop-down list, and image to display the drop-down list. The name, class, and id attributes are copied over from the original element to the new input field, and after the call, the JQuery collection will point to the new input field(s). Calling combobox('destroy') will restore the original element.

The drop-down list is keyboard sensitive with the following keybindings:

Additionally, options are auto-selected by typing within the input box.

Options

All event handlers take 2 arguments: the original browser event, and an object with the following fields:

The event handlers are also available as custom JQuery events, following JQuery UI conventions (i.e. select = 'comboboxselect', key = 'comboboxkey', etc.)

(Comments disabled due to the onslaught of spam. Alas, django-threaded-comment moderation doesn't work with Django 1.0+. Will re-enable when I have time to find a replacement.)

Clif wrote:
Hi Jonathan - great plugin! I noticed that it removes any size I may have defined on my input tags, resulting in the default size being used. This was easy to change by adding in an extra line to copy the attribute, but it would make a good option (preserve size, or re-size to data list). Just a thought.
Jonathan wrote:
I fixed it so it now preserves the class, name, id, title, dir, lang, xml:lang, size, maxlength, and value attributes. The only one it doesn't save is inline styles, which might cause problems when positioning the drop-down. New version is up on GitHub as 1.0.4, but I haven't updated the homepage yet. (1.0.3 also had it pull the class attribute of the original element over to the list and arrow, for easier styling, and fixed a problem with relative positioning.)
amrelgarhy wrote:
How can i add item dynamically to the combobox after its a combobox? I want to click on a link and this link will add a new item to combo box, can this be done? Thanks, Amr
Jonathan Tang wrote:

Haven't tested this, but try $('#selector').combobox('setData', 'data', new_option_list). In general, the setData method on JQuery UI widgets lets you dynamically change options after the widget has been constructed. But it has to be specifically supported by the plugin (there's code in the combobox to do it, but it hasn't been well-exercised).

If it doesn't work, it's a bug. Let me know about it and I'll get around to fixing it sometime.

amrelgarhy wrote:
what is new_option_list? and what it will contain?
Jonathan wrote:
Same as the 'data' attribute when constructing the combobox. An array of (string) keys, for the combobox options.
amrelgarhy wrote:
I wrote it like this: var select = new Array('Opt1', 'Opt2', 'Opt3', 'Opt4', 'Opt5', 'Opt6', 'Opt7'); $('#demo1').combobox('setData', 'data', select); But i found in the combobox 7 items all "undefined" but when i select one of these undefined i got one of the options, "Opt2" for example. i wrote it wrong? Thanks for your support
Jonathan wrote:

I've updated the demo page with an example of how to do this.

There's also you'll a bugfix you'll need to get the getData call to work. It's up on both the website (1.0.5) and GitHub.

amrelgarhy wrote:
I tried your code and its working fine, but one issue i got that it still give "undefined" when trying to add a new item for a combobox which was originally a select element. Like this: <select id="demo1"> <option value="orange">Oranges</option> <option value="apple">Apples</option> <option value="pear">Pears</option> <option value="kiwi">Kiwis</option> <option value="banana">Bananas</option> <option value="grape">Grapes</option> </select>
Jonathan wrote:
Ah. The issue there is that filling a combobox from a select creates a default listHTML function that lets the list display something different from the actual value selected. I just pushed a fix that'll use the value itself if there's nothing specified in the select. If you need more control, provide a listHTML(val, index) option as described in the docs.
amrelgarhy wrote:
Things going fine now, just another question if you please, using your combobox, can i set text and value? "the same as i do with normal select dropbdown", i mean to be able to set array of data and also array of values or keys, so at the end i will have 1-Index 2-Text 3-Value, because that will make the control very useful when filling from databases to be able to fill it with the id and value of the rows. Got me?
Jonathan wrote:
Yeah - that's what the listHTML option is for. You specify the keys - this is what gets set as the value of the element when an option is selected - as normal, through the data array. Then you set the listHTML option to a function of the key (and optionally, index) that returns the actual HTML for each option in the dropdown. If you have a preset mapping, you'll probably just create a function that looks up the key in a dictionary. Check out the defaultListHTML() and fillDataFromSelect() functions in the source.
amrelgarhy wrote:
Good, could you please show me a small sample for filling with text and value using the listHTML?
Jonathan wrote:

The first example on the demos page, "Simple combobox from array", has an example of using listHTML. Just have it pick up the text from a JavaScript hash instead of computing it from the index.

amrelgarhy wrote:
I did it like this: $('#demo1').combobox({ data: ['1', '2', '3', '4', '5', '6'], autoShow: false, listHTML: function(val, index) { return $.ui.combobox.defaults.listHTML( "Option", index); } }); But when i select an item i got in the demo1 input the value as the selected not the text which is="Option" as i wrote in the listHTML. Is this a problem of i did something wrong?
amrelgarhy wrote:
and for example: <select id = "demo2"> <option value = "1">Oranges</option> <option value = "2">Apples</option> <option value = "3">Pears</option> <option value = "4">Kiwis</option> <option value = "5">Bananas</option> <option value = "6">Grapes</option> </select> <script> $('#demo2').combobox(); </script> when i select i got the value in the input not the text.
Jonathan Tang wrote:

Yes, that's intentional. The values in the data array control what will be placed in the input field. The result of the listHTML controls how these are displayed in the menu.

If you want to translate the displayed value into some other value for the server, just write an onSubmit handler that processes the value before submitting to the server. Or put in server-side code to translate the values. That's outside the scope of this plugin.

amrelgarhy wrote:
I will try to do what you said and tell you incase i found something new can help improve this plugin, by the way thank you very much for supporting and wanted to say that this is one of the best drop down lists on all over the internet, good work and keep improving :)
Andrew wrote:
First off, great plugin. I have tried to use it to display images for the drop down. How can i use a seperate data array for the listHTML? This way i could use it to pull a filename of a jpg to display instead of just using the data array.
Jonathan Tang wrote:
I just added a demo that pulls a list of images from an external array. Hopefully it's enough to get a sense of how to use external data, since this seems to be a common request.
Larry wrote:
This definitely looks nicer and better-featured than other comboboxes, but I discover that it doesn't want to work with jquery UI 1.6 (rc5) Version 1.53 runs fine, but it doesn't activate with 1.6 -- any thoughts?
Jonathan Tang wrote:
I just pushed a really quick fix of this to GitHub. They renamed the init method in 1.6, so all old (UI) plugins will break. I'll get it up on the homepage version eventually, but I want to add the demo for the previous commenter and have a couple other tweaks to do first. Sorry for the late response; I just started a new job and so don't have all that much time for open source.
Jon wrote:
I have a simple combobox drop down working with: $('select').combobox() The original select has drop-down display values that are different to the option text eg: <option value="value-12">Text 12</option> The drop-down works fine, displaying the correct option text when the arrow is pressed, but when I choose something the span shows the selected value, not option text (the opposite of what a normal select would do). How would i get it to show the option (but still submit the value to the server)? I think this has been alluded to in earlier comments, but I can't find a clear answer / demonstration. Thanks!
Jon wrote:

To rephrase my earlier, waffly, question ;)

How do I display the option text not the value?

Jon wrote:

Sorry, to clarify, it displays this 'value' to the user when the option is selected (rather than showing the option).

The dropdown looks ok otherwise ...

Jonathan Tang wrote:

Why not get rid of the values and just have the option texts? If you need a different value for the database, either put some postprocessing in the server-side code or write an onSubmit handler that checks the value of the form.

The plugin is setup so that the option text controls the menu display and the option value controls the textbox. This is the only way it can work: the combobox is actually a text input, and the value of a text input is submitted verbatim with a form. If the combobox widget itself were to intercept the displayed value before it goes to the server, it would need to know the structure of the form surrounding it, which is basically an encapsulation nightmare.

Jon wrote:

Jonathan, thanks for the help.

The system I use is localised. So the value is standard and the option text is translated depending on the users language settings.

I can understand why it's the only way combobox would work and post server processing would work in this case.

It's a shame though, as it would be great if it could behave just like the html select it's replacing.

Brian wrote:
Why not just insert a display: none field with the correct form value to be submitted, and onselect, copy the value into that field? That way, the text can be listed as in the select list, but the value of that select can still end up as what is actually submitted. No need then to know the form structure.
Jonathan Tang wrote:

You mean have an input type=hidden, and then update that with the new value whenever there's a change in the text field or a value is selected? Any change needs to take into account that values can both be selected or typed in manually into the field, and that typed values don't necessarily have to be on the list.

Anyway, I see two problems with that:

  1. What to name/id the hidden field? There may be multiple comboboxes on the page, and you have to make sure there're no accidental name collisions.
  2. It would add two fields to the resulting HTML POST - the visible text input and the invisible hidden input - instead of one.

(Continued in second comment...damn Django Threadedcomments has a length limit.)

Jonathan Tang wrote:
#1 is easily surmountable - I think that prefixing the original input's name with "jquery_ui_combobox" should be unique enough. But #2 makes me really, really nervous. Believe it or not, some apps iterate through all fields in the POST request and grab the values of the ones that match a common pattern. (This is commonly used to implement DHTML designs where you add new fields as users fill in old ones, for example.) You risk introducing some very hard-to-find bugs by adding new hidden fields to the form. Similarly, a lot of JQuery code may use $('form.myForm input:eq(3)') to select elements, even though its quite brittle. Adding new input tags will break this. In general, I want the combobox's footprint on the page to be as light as possible - don't go adding extra fields that don't absolutely have to be there. It seems like a lot of added complexity just to avoid some server post-processing.
Mat wrote:
Hey, nice work! One question, though - when "autoShow" is true, there is no way of leaving the combobox with a value that is not in the list. Is this correct? I'll probably be able to fix this for myself easily enough, but I'd think everyone would have the same problem... (as otherwise a regular HTML select would be enough). Regards, Mat
Jonathan Tang wrote:
You should be able to tab or return out of the field (or click elsewhere), but I just tried and those don't work. That's a bug. If you fix it, send me a patch and I'll incorporate it, or I might get around to fixing it myself one of these days.
Carlos Ferreira wrote:
Hey Jonathan, great work! Can the combo values be replaced with a javascript event?
Jonathan Tang wrote:

You mean dynamically changing the options based on some external event? Yeah; ComboBox responds to the same setData protocol that the other JQuery UI Widgets do. Either call setData('data', newOptions) on the combobox widget itself (which is not the same as the DOM element; you use $('#id').data('combobox') to retrieve it) or trigger the "setData.combobox" event on the JQuery element.

Carlos Ferreira wrote:
Thanks a lot Jonathan. It works nicely!
Carlos Ferreira wrote:
Hi Jonathan, I've noticed that it loses the style when I change the values. I had to create an extra function on the ui.combobox and force the class this.listElem.addClass('combo');
Vladimir Hidalgo wrote:
Hi!, there's a problem displaying the combobox over a Jquery UI inline DatePicker in FF 3.0 (don't know others). Thanks for your combobox plugin, it's really nice!.
Vladimir Hidalgo wrote:
Screenshot of the above described problem: http://img11.imageshack.us/my.php?image=test5c.jpg
Vladimir Hidalgo wrote:
Oh, I (kind of) solved it by editing ui.combobox.css I just added z-index: 100; to .ui-combobox-list Maybe it's not correct, but works fine.
Jonathan Tang wrote:
That's the right solution - I'm guessing that DatePicker has a z-index in its stylesheet so it pops up over everything else, but combobox does not. You may want to put it in your own stylesheet (loaded after ui.combobox.css or otherwise more specific according to the CSS selector rules) so that you don't have to manually port the fix over if a new version of ui.combobox.css comes out.
Marni wrote:
I'm loving this plugin. I just having one problem. If I have comboboxes inside a div and hide then display the div the comboboxes stop working as expected in FF. Any thoughts?
Adam wrote:
I am trying to use this widget with the latest jquery 1.3.2 and jquery UI 1.7.1 as I haven't found anything else that works similarly. The widget renders correctly, but I am unable to use setData, getData, or any of the listeners. Any ideas?
Joe Rozzi wrote:

In order to get the events to work properly in jQuery 1.3.1 and above, I had to modify the function called "fireEvent" in the comboxbox.js script (on or around line 317) to the following:

fireEvent: function(eventName, e, val) {
this.options[eventName](e,this.prepareCallbackObj(val));

System Message: WARNING/2 (<string>, line 5)

Definition list ends without a blank line; unexpected unindent.

},

making this modification might likely break some other functionality in this plugin but it did the job for me.

Joe Rozzi wrote:
Since my code didn't paste properly in the previous post, I will try again: fireEvent: function(eventName, e, val) { this.options[eventName](e,this.prepareCallbackObj(val)); },
Jonathan Tang wrote:

Don't worry about it...came through fine in the e-mail.

Thanks for the patch! I'll see about incorporating it when I get a chance.

Joe Rozzi wrote:
The patch in my previous post fixes the listeners in jQuery 1.3.1 and above, however there are still problems using jQuery UI 1.7.1. I can only get the plugin to work with jQuery UI 1.6rc5. It appears there might be newer methods to creating the combobox widget for UI 1.7.1 than the way you are doing it now: http://docs.jquery.com/UI/Developer_Guide#The_widget_factory Oh well... I'll post back if I ever get a chance to work on it more for UI 1.7 Thanks