Select2 vs Chosen

Select2 and Chosen are both great javascript widgets for enhancing the HTML select tag. They have a lot of similarity and a few differences. How to pick? These just a few issues I have run into. I am using Select2 V4 and Chosen 1.1.

Form Tabbing

A lot of users prefer keyboard to a mouse. Both widgets allow that. However, Chosen handles tab better. With Chosen, tab completes the entry and moves to the next form field. With Select2, I could not get tab to move to the next field. Select2 messes with the tabindex property. I am guessing that is part of the problem.

Tagging

This is the reason I used Select2 instead of Chosen. Tagging allows the user to input something that is not already in the select widget.

 

 

Django, Selenium Headless Tests Fail

Running headless selenium tests is great. Except when the tests succeed when headed, then fail when headless. When that happens it’s hard not to let your imagination run wild with crazy javascript thoughts.

There are many ways to run headless. The method I use is described in this post. In a sense this is not headless because a normal browser is running; it’s just running on a hidden display. This makes the headless fails all the more perplexing.

In this case, the problem was the headless browser window was a different size compared to the headed version. I use the chosen widget a lot. It’s a fancy combo of an HTML select widget and auto-complete. Clicking on this widget creates a pop-up drop down. If the needed choice is outside of the browser, selenium auto scrolls the page so that the choice is visible. When it decides to scroll depends on the browser window dimensions.

In many cases, this auto scroll would no create problems. In my case, I also have a nav-bar locked to the top of the screen. Sometimes the desired choice would end up under the nav-bar after auto-scroll. When selenium moved to select the choice, it would get the nav bar instead. The form value would not be set and the form would fail. Although it did not happen to me, I supposed it’s possible selenium could have ended up on a different page. That could make some strange errors.

The first part of the fix is to explicitly set the browser window so the headed and headless versions are the same. Something like this:

if getattr(settings, 'HEADLESS_TESTS', False):
    self.vdisplay = Display(visible=False, size=(1600, 1000))
    self.vdisplay.start()
self.selenium = webdriver.Firefox()
self.selenium.set_window_size(1500, 900)

If all the problems go away, you are done. If not, then hopefully the headed tests and headless tests will now fail in the same way, making debugging easier.

In my case, I added code to scroll the page if the item was under the nav bar. Here is the code.

Django-dynamic-formset and Chosen

I really like the javascript widget Chosen. I also frequently need to make Django forms with a variable number of associated formsets. To do that, I use django-dynamic-formset. The user clicks “add another” and Django-dynamic-formset clones the last formset to make a new row. It also takes care of updating all the form inputs so that Django accepts these new formsets that magically appeared on the client-side. Conceptually it’s simple and easy. The problem comes when you use complicated widgets in your formset. They do not clone properly.

Chosen is one widget that does not work properly with django-dynamic-formset. Time to start hacking. There are a bunch of possible solutions. First I will present what worked. I layout my formset in a table. The solution that worked for me involved replacing the contents of the table cell with a new, dynamically created select and then associating it with chosen.

First I add the select options to the Django context:

# Django
kwargs['my_options'] = json.dumps([[x.id, unicode(x)] for x in my_queryset])

then in javascript, assign the options to a global var and attach a “added” function to the formset table

var my_options = {{ my_options|safe }};

$('#formset_table').find('.dynamic').formset({
 prefix: '{{ my_formset.prefix }}',
 formCssClass: 'dynamic-formset1',
 added: add_row
});

Next I created a helper function, that creates a function, to replace the contents of the table cell with a new select widget. You can see this code in my Github Gist. Create the actual function in the init part of my javascript:

$(document).ready(function(){
 chosen_fixer = make_fix_chosen_function('.container', '.my_select', my_options);
}

Finally I created the add_row function:

function add_possession_row(row){
   var new_select = chosen_fixer(row);
   new_select.chosen({width: '500px'}).change(changed);
 }

As I mentioned I tried other solutions before I got to this one. First, I noticed the name and id of the cloned select was wrong. So I wrote code to fix that. It was not enough. Next, tried several permutations of this from Stackoverflow. I could not get it to work.

Finally, the major reason to use chosen is when the selects are long. Thus my solution is inefficient because I pass the select data to the client at least two times. It might be possible to improve the efficiency by grabbing the select from an existing input. But I have a deadline and the speed is more than adequate.