HTML User Picker using jQuery + UI (part 1)

UPDATE: I created a project on GitHub. See the follow-up post.

I was recently tasked with solving a rather unusual problem at my company. It was unusual not in the uniqueness of the request but by how common it would seem to be yet we had no existing solution. While we have created standalone web applications to satisfy internal needs, it appears we never really had cause to build a proper user picker. Most of our solutions that needed one happened to be surfaced through SharePoint and made use of its user search and selection methods. That’s why I was surprised to have 3 applications which needed a picker for internal employees pop up at almost the exact same time.

Of course our first thought was to leverage an autocomplete control. Quite some time ago I’d made use of the jQuery plugin from bassistance.de before one was available as part of jQuery UI. Though the autocomplete was quite nice, the free-form nature of the text was not strict enough for our purposes in picking users.

A few suggestions were tossed around including turning an HTML div into a pseudo-input by listening to keypress events and updating the values. A coworker was tasked with turning out a prototype based on this design and he did a pretty good job. It was functional but it lacked some features people have come to expect from an autocomplete-style control like the ability to use arrow keys to navigate the selections. In addition, the input was a bit buggy and there were some race conditions with rendering suggestions from a remote service while a user was still typing. In order to solve the buggy input, the keypress listeners were replaced by a hidden input control nested inside the div and styled in such a way as to be invisible. We still had to address the usability and race condition.

At this point we had a good idea of what we wanted the control to look like – modeled after GMail’s address picking – and what it needed to do. I ended up gutting the prototype and rewriting it. While it is still an early version and could use some enhancement to make it more flexible, we achieved a solution that is working quite well for us.

The Design

In our current version we’re using a containing div to hold an HTML input control. This control is set up as an autocomplete connecting to a JSON service. When a user suggestion is selected, we create a user container styled to look like a GMail address which itself contains a hidden input with the value (in our case the employee ID and a friendly display name). Then we clear out the autocomplete input and reset the focus to be prepared for more input.

All the hidden inputs for a given container are given the same ‘name’ attribute which is specified using an HTML5 data attribute on the container. This makes rolling up the values after a form submit pretty simple. In fact we’re using an ASP.NET MVC application and the model binder will automatically turn them into a string array for us in our ViewModel.

Since the autocomplete input can’t take up the entire size of the containing div, it was necessary to add a listener to on the containing div to send focus to the input. The container was styled to look like a big input field. I also tweaked the CSS to change the cursor to text to further hint to the user how to use the control.

Once all this was worked out I went back and decorated the visual components with jQuery UI styles to allow the picker to be styled consistently within our applications.

So what does this look like?

Autocomplete Suggestion

A user has been chosen and added to the control

A duplicate user is being chosen.

After selecting a duplicate the UI highlights the previous copy and does not create the duplicate.

It’s a little hard to capture but in that last image we see the UI hint that lets the user know they’ve already selected a particular user. It’s just a highlight effect with an animation over 1 second.

You’ll notice the newly picked person is inserted into the DOM before the autocomplete input which is then slid to the right. This gives the appearance of a continuous left-to-right typing experience. If you continue to pick names, the container will grow vertically to give more space.

For removing elements we’ve attached a click listener to an HTML div that contains the dynamic HTML using jQuery.on(‘click’, …) and with a selector of the span holding the jQuery UI ‘x’. On clicking the ‘x’ the whole user container is removed which includes the dynamically inserted hidden input that contains the value of the user. In addition we added a backspace listener on the autocomplete input. When a user hits backspace without text in the input, the previous user is removed.

Overall the design seems to be very discoverable to a user. It’s pretty easy to see the difference between a valid user that has been selected and input that will be discarded on form submission. Standard behavior of tabbing between fields is maintained and the arrow key navigation of search suggestions is inherited from autocomplete.

Since the post is getting a bit long, I’ll include the HTML structure that is generated along with the simple styles I used to make the elements behave so fluidly. I’ll dive into the JavaScript and jQuery + UI in the next post. I am not particularly adept at CSS so please don’t hesitate to point out a better way. Above all else I share information in hopes of evolving designs and creating a better experience for our users.

The HTML

We start with a basic page.

<span>Universe A:</span>
<div class="employeepicker" data-input-name="alpha" ></div>
<span>Fighting Mongooses:</span>
<div class="employeepicker" data-input-name="beta"></div>

Within each div we want to be a picker, we add container HTML.

<div class="pickerParent">
  <div><!-- Reference point for user insert --></div>
  <input type="text" class="pickerInput ui-widget" />
  <div class="clear"></div>
</div>

Our JavaScript code will be dynamically inserting the following HTML every time a new user is picked.

<div class='employeeContainer'>
  <div class='employeeEntry ui-widget ui-widget-content ui-state-default'>
    <div class='innerWrapper'>
      <span data-employeeid='_ID_' class='employeeName'>_TEXT_</span>
      <span class='ui-icon ui-icon-close deleteEmployee'></span>
    </div>
  </div>
  <input name='_NAME_' type='hidden' value='_VALUE_' />
</div>

Now we see the complete HTML of a picker with one user selected.

<div class="employeepicker" data-input-name="alpha">
  <div class="pickerParent">
    <div>
      <div class="employeeContainer">
        <div class="employeeEntry ui-widget ui-widget-content ui-state-default">
          <div class="innerWrapper">
            <span data-employeeid="012345" class="employeeName">Erik Noren</span>
            <span class="ui-icon ui-icon-close deleteEmployee"></span>
          </div>
        </div>
        <input name="alpha" type="hidden" value="012345;Erik Noren" />
      </div>
    </div>
    <input type="text" class="pickerInput ui-widget">
    <div class="clear"></div>
  </div>
</div>

Here’s the very simple CSS I came up with to get this all lined up.

.pickerParent {
    height: auto;
    border: 1px solid black;
    overflow: auto;
    cursor: text;
}

input.pickerInput {
    border: 0px;
    float: left;
    width: 100px;
    padding: 0;
    margin: 3px 3px 3px 3px;
}

input.pickerInput:focus {
    /* Override default MVC styles to cloak input in div */
    border: none;
}

.clear {
    clear: both;
}

.pickerDisabled {
    background-color: #AAB;
}

.employeeContainer {
    cursor: default;
}

.employeeEntry {
    float:left;
    margin: 3px 3px 3px 3px;
}

.employeeEntry .innerWrapper {
    padding: 1px 0 1px 0;
}

.employeeEntry .innerWrapper .employeeName {
    float:left;
    margin-left: 2px;
}

One thing I’ve noticed while testing this out – Chrome seems to still style a border around the “cloaked” autocomplete input even with the styles I’ve chosen. I don’t know if it’s something I’m doing wrong or if it’s a feature. And don’t forget this relies on jQuery UI styles and at least the autocomplete control (plus highlight effect).

In the next post I’ll show the evolution of the JavaScript that ties this all together into my first jQuery plugin and look at ways this could be extended or made usable in a wider array of situations.

Happy Lunar New Year!
-Erik

Advertisements

7 responses to “HTML User Picker using jQuery + UI (part 1)

  1. Looks nice!
    I have the same problem here. Since I work for this quite big company, there are a few people with the same names. I wonder how does your gadget handle that?

    • That would be a matter for the service which provides the results. How do you handle it now with email address look up, for example? You could use some sort of display name that incorporates unique elements if that’s available. You might even be able to modify the autocomplete’s behavior to include mouse-over text on the result menu so you can see the difference between two people.

      Really this control just wraps the jQuery UI autocomplete and does a bit of styling. All the power is still there to customize. For our needs we’re probably going to be using a “preferred display name” which accounts for shortened or preferred versions of names. This could lead to some ambiguity but I haven’t seen a case yet – maybe we’re just not big enough for that! In our situation I’d probably include – as part of the display – the office for which they work. I can’t imagine there being duplicates per office. So, for example, “Erik Noren (Applications)” or whatever field is already available to us from the warehouse.

      I’m sure I can help with customizing the JavaScript for your particular need. I actually haven’t posted it yet (needed some edits) but I think it’ll be ready in the next couple days. I’d be happy to try to help if you have an immediate need before that time.

      -Erik

  2. Pingback: lhmlihaomin

  3. Pingback: “usersPicker” | lhmlihaomin

  4. Pingback: HTML User Picker now on GitHub! | Erik Noren's Tech Stuff

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s