type: widget
release: 0.0
status: in planning
documentation: N/A
demo: See demos folder in GitHub source download
1 - Description:
This widget provides a well-constrained way to let users build a list of items. Common applications include the Facebook-style list builder for message recipients (F6) which provides a textarea with an auto-complete function and the ability to re-order and delete recipients. The Apple Mail style builder (F7) adds an action menu next for each item that can be used to offering multiple actions. This should support re-ordering via drag and drop, plus keyboard shortcuts to select and delete items.
The autocompletion is optional, when enabled, it needs the Autocomplete as a dependency.
The default is to be disabled: .listbilder({ autocomplete: false })
A fully working widget is now available on github as a standalone repository. The source download includes a demos folder showing 3 different use cases.
https://github.com/michael-lang/jquery-ui-listbuilder
This widget is available via NuGet. Instructions for installation:
https://www.nuget.org/packages/jquery-ui-listbuilder/
Alternate reference jQuery plug-ins:
http://plugins.jquery.com/project/facelist
http://www.emposha.com/javascript/jquerymultiselect.html
http://devthought.com/wp-content/projects/jquery/textboxlist/Demo/
http://tagedit.webwork-albrecht.de/index.html
Mark Gibson's plugin, built on jQuery UI. Can be used with Jorn's autocomplete (or others):
http://test3.internal.adaptavist.net/~mgibson/tokenlist/demo.html
Filament Group has a draft of the Facebook style interaction modeled here that uses progressive enhancement techniques on a textarea to ake it easy to get and set values:
<link...>
2 - Visual Design:
3 - Functional Specifications/Requirements:
Options:
- delimiter (string, default: ",")
- A text delimiter used to parse the original values at creation time and then to write list items back into the original text input each time an item is added, removed, or sorted to a new position.
- duplicates (boolean, default: false)
- Specifies if more than one item of the same text value can be added.
- sortable (boolean, default: false)
- Specifies if the list of items can be sorted. Details of the sort are not customizable.
- autocomplete (boolean, object, default: false)
- If false then the list builder does not auto-suggest value from an autocomplete
- To enable autocomplete pass in the options to be passed into the autocomplete exactly as normally supported by the autocomplete widget.
- If the select event is not specified on the autocomplete options then a default implementation is added to put the selected value in the list builder and then clears the token editor
- If the select event is specified on the autocomplete options, then that method must use the add method of listbuilder to put the selected value into the list.
Methods:
- add (string)
- Adds a new item to the list and to the underlying linked text input or textarea
- remove (list item element)
- Removes the specified list item from the list and then updates the underlying text input or text area from remaining items.
- editItem (list item element)
- Takes the text of the list item on puts it in the editable area, and then removes that list item from the list. When done editing the newly changed value becomes a new list item.
- selectedItem
- Getter for the currently selected list item
- listblur
- Removes focus from this input element. Blurs the text input or text area and removes any selected styles from list items.
Events:
- select
- Triggered when the user clicks on one of the items in the list (sets originalEvent to 'click')
- Triggered when the user hovers of one of the items in the list (sets orginalEvent to 'mouseenter')
- Triggered when the user uses arrow keys to navigate to an item in the list (sets originalEvent to ?) (not yet implemented)
- ui.item refers to the selected list item
Keyboard Navigation:
-
up / left
- navigates to previous list item
- if cursor is in token editor and there is a text value do not change selected item. leave focus in the textbox
- if cursor is in token editor and there is no text value, then select the last item.
- right / down
- navigates to next list item
- if cursor is in the token editor do not navigate
- delete
- deletes current list item and highlights next item (or last item if last was deleted)
- if cursor is in the token editor do not navigate. Instead edit text as normal.
- backspace
- deletes current list item and highlights previous item (or first item if first was deleted)
- if cursor is in the token editor do not navigate. Instead edit text as normal.
- any other character
- put the typed value in the token editor and deselect any selected item. Show autocomplete if appropriate.
- If autocomplete is visible disable keyboard navigation of list items since it should then navigate the autocomplete list
4 - Markup & Style:
4.1 Initial markup examples
Most common usage:
<input type="text" value="one, two, four"/>
Alternate 1 defines menu for each list builder item
<input id="test2" type="text" value="six, three, zero" class="test" style="width:200px"/>
<ul id="test2-menu">
<li><a href="/#">Edit item</a></li>
<li><a href="/#">Copy</a></li>
<li><a href="/#">Remove</a></li>
</ul>
Alternate 2 uses a text area instead of a text input
<textarea>one, two</textarea>
4.2 Recommended transformed HTML markup
First, hide the orignal input. Then immediately after add a div that will appear on top of the original input location. The script should size the div to match the original size of the input.
<input type="text" id="test1" style="width: 350px; display: none;" class="autocomplete test" value="one, two, four">
<div class="ui-state-default ui-listbuilder" style="width: 350px; min-height: 16px;">
<ul class="ui-listbuilder-items" style="width: 345px; min-height: 16px;">
<li class="ui-corner-all ui-widget-content ui-listbuilder-item">
<span>one</span>
<a class="ui-icon-circle-close ui-icon" style="float: right; position: absolute; right: 2px; top: 4px; width: 16px; height: 16px;"></a>
</li>
<li class="ui-corner-all ui-widget-content ui-listbuilder-item">
<span>two</span>
<a class="ui-icon-circle-close ui-icon" style="float: right; position: absolute; right: 2px; top: 4px; width: 16px; height: 16px;"></a>
</li>
<li class="ui-corner-all ui-widget-content ui-listbuilder-item">
<span>four</span>
<a class="ui-icon-circle-close ui-icon" style="float: right; position: absolute; right: 2px; top: 4px; width: 16px; height: 16px;"></a>
</li>
<li class="ui-listbuilder-item-input">
<input type="text" class="ui-autocomplete-input ui-listbuilder-input" style="width: 340px;" autocomplete="off" role="textbox" aria-autocomplete="list" aria-haspopup="true">
<div class="ui-state-active" style="display: none;">Type to receive suggestions</div>
</li>
</ul>
</div>
4.3 Accessibility recommendation
(Detailed recommendations for ARIA, HTML markup, CSS and javascript that will contribute to universal access and full section 508 accessibility for screen readers, mobile devices and any other devices)
4.4 CSS & Theme
The newly created wrapping div will be given class ui-listbuilder. Each item will be given class ui-listbuilder-item. The input to which the user can enter a new list item be given class ui-listbuilder-input, and any additional classes the optional autocomplete plugin applies.
Suggested new css styles and their values as follows:
.ui-listbuilder {
background-color:White;
border:1px solid #7F9DB9;
}
.ui-listbuilder-items {
margin:0;
overflow:hidden;
padding:3px 4px 0;
}
.ui-listbuilder-item {
border:1px solid #444444;
float:left;
height:20px;
list-style-type:none;
margin:2px;
padding:1px 18px 1px 3px;
position:relative;
}
.ui-listbuilder-item-input {
border:0 none;
}
.ui-listbuilder-input {
border:0 none;
}
5 - Latest version of plugin:
(Link to the latest version in the jQuery UI trunk here - not in Git yet)
http://www.nexul.com/prototypes/tokenlist/src/jquery-ui-listbuilder.js
6 - Open issues being discussed
1) close icon needs title attribute
2) should close icon class be configurable for normal and hover state?
icons:[normal:'ui-icon-circle-close', hover:'ui-close-icon']
3) options for localization of text
lang:{closeText:'close', suggestText:'Type to receive suggestions'}
4) how should additional plugins be applied such as watermarks, mask input, or validators?
a) should the input for entry of a new list item be exposed as a property so that other plugins can use that
b) should others be applied first and then copied/moved to the new token input
c) some plugins like validators may need to remain applied to original form input (like required input)
d) some plugins like masked input or watermark may need to be applied to token editor input.
5) sorting some items to some locations can be difficult. Maybe larger drop targets are needed?
6) Usability study shows user want to see a list without having to type, while advanced users still want to type in a new value occasionally.
a) should the list options (autocomplete) automatically show on focus if minLength of autocomplete is zero?
b) should there be a way to instead add a button to show some predefined values (when minLength is zero) Is that a typical use case of extending the autocomplete (combobox example) or something common enough to build into the list builder?
7) LI's should get a tab index of -1 for easy leverage by screen readers
8) bug: If you get the autocomplete to show and then arrow down a few then back up, the selection of previously entered items starts highlighting and moving. Then if you hit delete (to remove text in the input field if you typed some, it starts deleting previous items). Probably need to only have the up/down "navigate" when there is not text in the input, or something.
9) how do we activate an attached menu using keyboard only. The current demo only shows the attached menu on click. There is currently no event for item selected via keyboard navigation. maybe adding that would solve the problem.
Comments (22)
marcio said
at 9:03 am on Nov 12, 2010
Make sure it works with the "watermark" plugin.
Michael Lang said
at 3:46 pm on Nov 15, 2010
Can you attach a watermark to an autocomplete? If so, how is that done so that I can model after it.
Then what about other plugins? Should I just expose the textbox as a property of the listbuilder and let them do as they wish?
marcio said
at 5:51 am on Nov 16, 2010
i tried this and it works fine.
.autocomplete({source:data}).watermark();
About other plugins that could be used here too like masks,date/timepicker to get list of dates maybe ?
So i think that the textbox property would be ok.
Aaron Barker said
at 9:18 am on Nov 16, 2010
So the fun here is do you put the watermark, mask, etc on the original field and then need to find a way to copy them to the listbuilder created field, or do you pass in all those other plugins as options to the listbuilder for the listbuilder to instantiate. Both seem kind of weird in their own ways.
Michael Lang said
at 1:07 pm on Nov 17, 2010
I guess the problem with copying attached plugins from the original input is that ordering of application of plugins becomes vague to the developer using the widget. If there is a text input property that indicates to them that any widgets should be applied to that. Validators would still be applied to the original input, since the contained input clears the value after adding it to the list. The underlying linked input still gets updated as items are added to the list, so that input makes more sense for any validators. But the problem is what if the validator needs to show a visual indicator, then where does it appear in the DOM and visually?
Aaron Barker said
at 11:11 am on Nov 22, 2010
In the interest of keeping plugins simple (in the jQuery UI way), I'd suggest that listbuilder provides callbacks as hooks/opportunities to extend the input field with watermarks/masks/etc. This allows the developer to apply what is needed to the original element (required) prior to instantiation, and to the created element all on their own without the listbuilder plugin needing to know anything about them.
Power and simplicity at the same time.
Michael Lang said
at 8:58 am on Nov 16, 2010
I'm beginning to see some awkwardness when the autocomplete list appears automatically on listbuilder input focus. It shows just fine, but it won't disappear consistently on lost focus. The problem is that I can't tell it to hide on lost focus of the text input because that happens when the autocomplete list appears so that the user can navigate the list of items.
I'm thinking that a button to the side is a better indicator that a pre-defined item can be added to the list without requiring typing. Then show the autocomplete only on click of the indicator, or the usual typing of some characters.
Pierre.H A said
at 1:44 pm on Nov 17, 2010
IMO, Sortable is not realy easy to use, i think that this could necessite default value that would help for the manipulation.
I also noticed that text is selectable on non-sortable list. Their are some pros and cons here.
Michael Lang said
at 3:14 pm on Nov 17, 2010
Could you list the pros and cons to selectability of the text of items as you see it? When I select, copy, and then paste them I get this which seems good:
* six
* three
* zero
As for the ease of sort, I hope we can work that out by changing options or increasing margin around the items so the drop targets get bigger.
Aaron Barker said
at 3:50 pm on Nov 22, 2010
Got some user feedback on the version I have created that likely applies here. I am using a "no duplicates" type option, so if I already selected "Barney Rubble" then Barney won't even show up in the autocomplete (which itself is different from the current demo, which allows duplicates in the dropdown, but on select they aren't added again). The user is confused as to why their selection isn't added to the list, not realizing that it is already in the list (using mine or the current demo method).
Two thoughts I had to maybe handle this.
1 - Highlight text in previously selected entries like autocomplete sometimes does, making the matched text bold or similar.
2 - Provide an "onDuplicate" callback that can be used to fire an alert, or highlight the previous entry or something.
Michael Lang said
at 11:43 pm on Apr 4, 2011
Rather than on OnDuplicate, how about the add method just raise an event for every add. For that matter the remove method could also raise an event.
These events are less to implement in the plugin and also give more flexibility in their usefulness.
Aaron Barker said
at 3:54 pm on Nov 22, 2010
Expanding on the no duplicates handling I mentioned above. The current implementation shows the duplicate in the list but then when selected just doesn't add it. I think this sets the user up to fail and to be more confused then typing in a match to something they already selected and having it not show up in the list (that would at least be a form of indication that they already selected it). If they can't select it, then it shouldn't be shown in the autocomplete. It feels more like it is broken when I choose something but it doesn't get inserted.
Not sure of the best way to handle this yet, as in my implementation I send all current selections to the autocomplete ajax request so that the backend doesn't serve them up again. The solution should be built into the listbuilder script, and not require the developer to handle it in their autocomplete front end or back end code. IMO that is.
Michael Lang said
at 11:39 pm on Apr 4, 2011
Again, sorry for my delayed response. I'm not so sure that this belongs in listbuilder. The listbuilder keeps the original enhanced textbox in synch with the values within it at all times, not including a tag being typed and not yet added to the list.
You have two options in doing this with the autocomplete. First do as you are doing and pass the value of the current listbuilder enhanced textbox back to the server. Second, you can filter the results as they come back from your listbuilder source method ajax call (success event) where you map those returned values into labels and values. I prefer the first option just to make sure a full list of your configured max size to show at once appear in the autocomplete drop down. Also the filtering code is easier on the server, at least for me when using C#.
I'm interested in what others think about this also.
Adam Ritzel said
at 9:56 am on Apr 5, 2011
It would be ideal to have an option for ListBuilder to restrict allowable entries to my source data, be it an array or remote source. Additionally, an onAdd event could also be helpful.
Michael Lang said
at 10:00 pm on Apr 6, 2011
I see a few options. The more integrated option (more tightly coupled) with autocomplete would be for the listbuilder to check the source option of the autocomplete and then in the add method, only add an item that is in the source.
The option without any coupling to autocomplete would be to add an event called "added' or "onAdd" or "itemAdded" that is fired anytime an item is being added to the list. Then the custom page script can determine what to do on add (ex: validate value is valid to be added)
Another option is to add an option to the listbuilder that specified if items should be added on lost-focus of the text input. That is really when those items not in the listbuilder are being added. When set to false, the only way to add an item would be to select it from the autocomplete.
Maybe all of the above can be added for the most flexibility, but the more options added the more likely options are being added that many people will not use, hence wasted download bandwidth.
What do others think is a good balance of feature while limiting bulkiness? Right now there is no way to accomplish this goal with the current listbuiler, so something should be added to address this requirement. Well I take that back, I guess you could subscribe to the change event of the underlying textbox which is updated as items are added to the list. But that makes the page script do parsing into values and find the latest added one then do the extra validation.
LisetskyVal said
at 7:32 am on May 24, 2011
Hello,
I came here from http://www.codeproject.com/KB/custom-controls/aspnet_suggester.aspx project.
That project was made for ASP.NET but its nice to see that people are interested in similar implementation for jQueryUI.
1) Support for ID's - if user selects anything from autocomplete dropdown, usually this data comes from DB and it has ID. Its very comfortable to know what exactly was selected.
2) Keeping State Alive - if current page makes POST to server, all data will be lost except what stays in textbox. So ID's support will be impossible in this case. Hidden field will be suitable here.
Michael Lang said
at 8:57 am on Sep 5, 2011
I just noticed a breaking change in autocomplete that made my usage of it break. I used to set the label of each item of my ajax datasource in the source option using some html formatting. When I just upgraded to jqueryUI 1.8.15 (with jquery 1.6.2) the formatting stopped working. The workaround shown on the autocomplete documentation works ok if you are doing a straight up autocomplete. But if you are using listbuilder the autocomplete is hidden inside the listbuilder.
Here is the workaround code shown on the autocomplete page:
$( "#project" ).autocomplete({...})
.data( "autocomplete" )._renderItem = function( ul, item ) {
return $( "<li></li>" )
.data( "item.autocomplete", item )
.append( "<a>" + item.label + "<br>" + item.desc + "</a>" )
.appendTo( ul );
};
To do this in lisbuilder I had to drill down deeper into another ugly layer of internals.
$('.sharewith').listbuilder({ duplicates: true, delimeter: ';',
autocomplete: {...}
}).data("listbuilder")._tokenEditor.data("autocomplete")._renderItem = function (ul, item) {
return $( "<li></li>" )
.data("item.autocomplete", item)
.append('<a><strong>' + item.Name + '</strong>: ' + item.ContactCount + ':' + item.MemberCount + '</br><em>' + item.MemberSummary + '</em></a>')
.appendTo(ul);
};
I hope this helps someone else needing to format autocomplete items inside of a listbuilder.
Michael Lang said
at 9:10 am on Sep 5, 2011
I posted a comment to discuss making this easier on the autocomplete development wiki page.
http://wiki.jqueryui.com/w/page/12137709/Autocomplete
Jörn Zaefferer said
at 9:24 am on Jun 6, 2012
Once thing I noticed in the code: You should make use of widget methods like _bind. Can't blame you for not using them yet, as there's just existing code using them, but no docs. We've started speccing them here: http://wiki.jqueryui.com/Widget
David Whiteman said
at 10:53 am on Nov 15, 2013
FYI, the delimeter option is misspelled. It should be spelled "delimiter". Also suggest renaming "duplicates" to "allowDuplicates" for clarity.
Michael Lang said
at 12:26 pm on Nov 15, 2013
Thanks. I fixed the spelling of delimiter. The GitHub repository source is also updated, and a revised NuGet package released. Old code using delimeter will still work until the fallback is removed.
I am not convinced yet that allowDuplicates is a better option name. I tried to find a similar naming convention through the other jQuery UI widgets and did not find any.
Accordian and Tabs - collapsible vs allowCollapse
Dialog - resizable vs allowResize
Tabs - hide and show options specify the animation to use for each. They are not called hideAnimation and showAnimation
many widgets - disabled vs isDisabled, active vs isActive
What I am observing is that options names are as short as possible while still giving a general idea as to what they do. I am not completely against 'allowDuplicates', I just would like to hear more support for that name change before committing to it.
David Whiteman said
at 1:33 pm on Nov 15, 2013
The difference with those other terms is there is a "-ible" or "-able" version of them. "Duplicatable" is not a good word in this case however, hence my suggestion. :-)
thanks for the typo correction!
You don't have permission to comment on this page.