NOTICE: This project is deprecated and no longer maintained. If you’d like
to continue supporting a forked version, please reach out on Twitter
(@kidjustino) to have it listed here.
Marco Polo
A jQuery autocomplete plugin for the discerning developer.
After spending years struggling with various autocomplete plugins, I became fed
up with their bugginess, poor documentation, lack of updates, inflexibility,
and antiquated coding patterns. Surely something as fundamental as autocomplete
could — really, should — be done better. And now it has. Meet Marco Polo. For
the discerning developer.
Developed by Justin Stayton while at
Monk Development for the
Ekklesia 360 CMS.
Features
- Cache and buffer. Marco Polo prevents unnecessary requests through its
build-in results cache (shared by all instances) and key press buffer (only
makes a request after the user has finished typing). - Remembers selection. Once a result is selected, if that same result
appears in the results again, it’s automatically highlighted. This is very
similar to how select inputs mark the currently selected item. - Require selection. Marco Polo can be configured to require a selection
be made from the results, ensuring that the text input is left empty when
no selection is made. - Overlabel support. Overlabel is the concept of placing a label
element over a text input for a more compact display. Marco Polo offers
built-in support for hiding and showing the label automatically, depending
on the state of interaction with the plugin. - Complete styling control. With straightforward markup that’s explained
in detail, you can easily style and modify all of the components to fit
your needs and aesthetic. - Callbacks for all major events. Add your own twist when a search is
made, result is selected, error occurs, and more. - Maintained. I developed this plugin for production use in the
Ekklesia 360 CMS at
Monk Development, so you can very much believe that
it will remain bug-free and up-to-date. Any feature requests, bug reports,
or feedback you submit will be responded to quickly as well. - Documented. I believe that code is only as useful as its documentation.
This manifests itself not only in clear and thorough developer
documentation (below), but also verbose documentation within the code
itself. - WAI-ARIA support. Assistive technology users can fully understand and
navigate Marco Polo.
Requirements
- jQuery >= 1.4.3
- jQuery UI Widget >= 1.8.21 (included in minified build)
- All modern browsers, including IE >= 6
Installation
Download
Bower
Bower is a package manager for the web. Once installed, Bower
can install Marco Polo with a single command:
bower install jquery-marcopolo
Manually
Include
Include both jQuery and Marco Polo in your HTML:
<script src="jquery.min.js"></script>
<script src="jquery.marcopolo.min.js"></script>
In most cases, jquery.marcopolo.min.js
is the best file to include, as it
contains the required libraries and source code in a single minified package.
The build
directory contains a number of other files as well:
jquery.marcopolo.js
contains the required libraries and source code in a
single unmifified package.build/parts
contains each individual library and source file in both
minified and unminified varieties.
Getting Started
Let’s say you want to create a user search field that redirects to the user’s
profile when a result is selected. To start, add a text input, if you haven’t
already:
<input type="text" name="userSearch" id="userSearch">
Then attach Marco Polo to the text input in your JavaScript:
$('#userSearch').marcoPolo({
url: '/users/search',
formatItem: function (data, $item) {
return data.first_name + ' ' + data.last_name;
},
onSelect: function (data, $item) {
window.location = data.profile_url;
}
});
When a search happens, a GET request is made to the url
with q
(the search value) added to the query string. (Additional data can be included
using the data
option.) Let’s say a search is made for Butler. A GET
request is made to /users/search?q=Butler
. Your backend code must then use
the q
parameter to find and return the matching users in JSON format:
[
{
"first_name": "James",
"last_name": "Butler",
"profile_url": "/users/78749",
…
},
{
"first_name": "Win",
"last_name": "Butler",
"profile_url": "/users/41480",
…
},
…
]
Each JSON user object is passed to the formatItem
callback option for display
in the results list. And when a user is selected from the results list, their
JSON object is then passed to the onSelect
callback option to complete the
browser redirect.
You should now have enough understanding of Marco Polo to start configuring it
for your specific needs. While this example demonstrates a number of
fundamental concepts, the possibilities extend far beyond the straightforward
search, click, redirect setup shown here. And when you’re ready, consider
reading through some of the more advanced guides:
Options
All options are optional, although url is usually specified unless the input
field is in a form by itself (in which case the form’s action attribute can
be used).
-
cache boolean
Whether to cache query results. The cache is shared by all instances, which
is a big advantage when many of the same field type appear on the same
page. For example, a tags field that’s repeated for every record on a page.Default: true
-
compare boolean, string
Whether to compare the selected item against items displayed in the results
list. The selected item is highlighted if a match is found, instead of the
first item in the list (highlight option must be enabled). Set this
option to true if the data is a string; otherwise, specify the data
object attribute name to compare on.Default: false
-
data object, string, function
Additional data to be sent in the request query string. (Note: The query
string parameter that is set with the input value (param option) will
overwrite the value in the data object if an attribute with the same name
exists.)Default: {}
When a function is used, it’s called for every request, allowing the data to
be dynamic. An object must be returned.Parameters:
- q string Requested input value.
this: jQuery object Text input (no need to wrap like $(this)).
Return: object of additional data.
-
delay integer
The number of milliseconds to delay before firing a request after a change
is made to the input value. This helps prevent an ajax request on every
keystroke from overwhelming the server and slowing down the page.Default: 250
-
hideOnSelect boolean
Whether to hide the results list when an item is selected. Interesting
things can be done when this is set to false, such as hiding and showing
certain items when other items are selected. The results list is still
hidden when the input is blurred for any other reason.Default: true
-
highlight boolean
Whether to automatically highlight an item when the results list is
displayed. Usually it’s the first item, but it could be the previously
selected item if compare is specified.Default: true
-
label selector, jQuery object, DOM element, null
Positioning a label over an input is a common design pattern (sometimes
referred to as overlabel) that unfortunately doesn’t work so well with
all of the input focus/blur events that occur with autocomplete. With this
option, however, the hiding/showing of the label is handled internally to
provide a built-in solution to the problem. The label receives the class
mp_label.Default: null
-
minChars integer
The minimum number of characters required before a request is fired. See
the formatMinChars callback to format the (optional) message displayed
when this value is not reached.Default: 1
-
param string
The name of the query string parameter that is set with the input value.
Default: q
-
required boolean
Whether to clear the input value when no selection is made from the results
list. This happens when the input is blurred, usually by clicking or
tabbing out of the field.Default: false
-
selectable selector
The list items to make selectable. For example, say you add the class
header to a number of list items (in the formatItem callback) that you
want to act as non-selectable headers. They can be excluded with the
selector :not(.header). Selectable items receive the class
mp_selectable.Default: *
-
selected object, null
Prime the input with a selected item. onSelect is called just as if the
item were selected from the results list.Default: null
-
submitOnEnter boolean
Whether to allow the browser’s default behavior of submitting the form on
ENTER.Default: false
-
url string, null
The URL to GET request for the results, which must be an array of strings
or JSON. If no URL is set, the parent form’s action attribute value is
used if one exists. q is added to the query string with the input value,
along with any additional data.Default: null
Callbacks
Formatting
-
formatData (data) function, null
Format the raw data that’s returned from the ajax request. Useful for
further filtering the data or returning the array of results that’s
embedded deeper in the object.Default: null
Parameters:
- data array, object Data returned from the request.
this: jQuery object Text input (no need to wrap like $(this)).
Return: array of objects to use as the data.
-
formatError ($item, jqXHR, textStatus, errorThrown)
function, nullFormat the text that’s displayed when the ajax request fails. The message
is displayed in a list item with the class mp_error:<li class="mp_error"> <em>Your search could not be completed at this time.</em> </li>
Setting this option to null or returning false suppresses the message
from being displayed.Default:
return '<em>Your search could not be completed at this time.</em>';
Parameters:
- $item jQuery object List item to display the message.
- jqXHR object or XMLHTTPRequest in jQuery 1.4.x.
- textStatus string Error status of the request.
- errorThrown string HTTP error status.
this: jQuery object Text input (no need to wrap like $(this)).
Return: string, DOM element, or jQuery object to use as the
message.
-
formatItem (data, $item) function
Format the display of each item in the results list. By default, the
title or name value of the data object is displayed. The returned value
is added to a list item with the class mp_item:<li class="mp_item">The Title of Something</li>
Default:
return data.title || data.name;
Parameters:
- data string, object Data returned from the request.
- $item jQuery object List item to display the result.
this: jQuery object Text input (no need to wrap like $(this)).
Return: string, DOM element, or jQuery object to use as the
display.
-
formatMinChars (minChars, $item) function, null
Format the text that’s displayed when the minimum number of characters
(specified with the minChars option) hasn’t been reached. The message is
displayed in a list item with the class mp_min_chars:<li class="mp_min_chars"> <em>Your search must be at least <strong>3</strong> characters.</em> </li>
Setting this option to null or returning false suppresses the message
from being displayed. It is also not displayed when there is no input
value.Default:
return '<em>Your search must be at least <strong>' + minChars + '</strong>characters.</em>';
Parameters:
- minChars integer Minimum number of characters required.
- $item jQuery object List item to display the message.
this: jQuery object Text input (no need to wrap like $(this)).
Return: string, DOM element, or jQuery object to use as the
message.
-
formatNoResults (q, $item) function, null
Format the text that’s displayed when there are no results returned for the
requested input value. The message is displayed in a list item with the
class mp_no_results:<li class="mp_no_results"> <em>No results for <strong>something</strong>.</em> </li>
Setting this option to null or returning false suppresses the message
from being displayed.Default:
return '<em>No results for <strong>' + q + '</strong>.</em>';
Parameters:
- q string Requested input value.
- $item jQuery object List item to display the message.
this: jQuery object Text input (no need to wrap like $(this)).
Return: string, DOM element, or jQuery object to use as the
message.
Events
-
onBlur () function, null
Called when the user is finished interacting with the autocomplete
interface, not just the text input, which loses and gains focus on a
results list mouse click.Default: null
Parameters: none
this: jQuery object Text input (no need to wrap like $(this)).
Event: You can also bind to the marcopoloblur event:
$(selector).on('marcopoloblur', function (event) { … });
-
onChange (q) function, null
Called when the input value changes.
Default: null
Parameters:
- q string Changed input value.
this: jQuery object Text input (no need to wrap like $(this)).
Event: You can also bind to the marcopolochange event:
$(selector).on('marcopolochange', function (event, q) { … });
-
onError ($item, jqXHR, textStatus, errorThrown)
function, nullCalled when the ajax request fails.
Default: null
Parameters:
- $item jQuery object List item to display the message.
- jqXHR object or XMLHTTPRequest in jQuery 1.4.x.
- textStatus string Error status of the request.
- errorThrown string HTTP error status.
this: jQuery object Text input (no need to wrap like $(this)).
Event: You can also bind to the marcopoloerror event:
$(selector).on('marcopoloerror', function (event, $item, jqXHR, textStatus, errorThrown) { … });
-
onFocus () function, null
Called when the text input receives focus. This is different than the
standard focus event on the text input, however, as this callback does
not fire when a results list item is selected via mouse click, which causes
the text input to blur and immediately gain focus again.Default: null
Parameters: none
this: jQuery object Text input (no need to wrap like $(this)).
Event: You can also bind to the marcopolofocus event:
$(selector).on('marcopolofocus', function (event) { … });
-
onMinChars (minChars, $item) function, null
Called when the minimum number of characters (specified with the minChars
option) hasn’t been reached by the end of the delay.Default: null
Parameters:
- minChars integer Minimum number of characters required.
- $item jQuery object List item to display the message.
this: jQuery object Text input (no need to wrap like $(this)).
Event: You can also bind to the marcopolominchars event:
$(selector).on('marcopolominchars', function (event, minChars, $item) { … });
-
onNoResults (q, $item) function, null
Called when there are no results returned for the request.
Default: null
Parameters:
- q string Requested input value.
- $item jQuery object List item to display the message.
this: jQuery object Text input (no need to wrap like $(this)).
Event: You can also bind to the marcopolonoresults event:
$(selector).on('marcopolonoresults', function (event, q, $item) { … });
-
onRequestBefore () function, null
Called before the ajax request is made. Useful for showing a loading
spinner if the request is going to take some time.Default: null
Parameters: none
this: jQuery object Text input (no need to wrap like $(this)).
Event: You can also bind to the marcopolorequestbefore event:
$(selector).on('marcopolorequestbefore', function (event) { … });
-
onRequestAfter (jqXHR, textStatus) function, null
Called after the ajax request completes (success or error). Useful for
hiding a loading spinner that’s shown in onRequestBefore.Default: null
Parameters:
- jqXHR object or XMLHTTPRequest in jQuery 1.4.x.
- textStatus string Status of the request.
this: jQuery object Text input (no need to wrap like $(this)).
Event: You can also bind to the marcopolorequestafter event:
$(selector).on('marcopolorequestafter', function (event, jqXHR, textStatus) { … });
-
onResults (data) function, null
Called when there are results to be displayed.
Default: null
Parameters:
- data array Data returned from the request.
this: jQuery object Text input (no need to wrap like $(this)).
Event: You can also bind to the marcopoloresults event:
$(selector).on('marcopoloresults', function (event, data) { … });
-
onSelect (data, $item, initial) function, null
Called when an item is selected from the results list or an initial value
(see Setting an Initial Value).
By default, the title or name value of the data object is used to
populate the input value.Default:
this.val(data.title || data.name);
Parameters:
- data string, object Data returned from the request.
- $item jQuery object, null Selected results list item. null if
selected option used. - initial boolean Whether this is an initial value.
this: jQuery object Text input (no need to wrap like $(this)).
Event: You can also bind to the marcopoloselect event:
$(selector).on('marcopoloselect', function (event, data, $item, initial) { … });
Methods
-
change
Programmatically change the input value without triggering a search request
(use the search method for that). If the value is different than the
current input value, the onChange callback is fired.Example:
$('#userSearch').marcoPolo('change', 'Wilson');
Parameters:
- q string New input value.
-
destroy
Remove the autocomplete functionality and return the selected input
fields to their original state.Example:
$('#userSearch').marcoPolo('destroy');
-
list
Get the results list element.
Example:
$('#userSearch').marcoPolo('list');
-
option
Get or set one or more options.
Example:
Get a specific option:
$('#userSearch').marcoPolo('option', 'url');
Get the entire options object:
$('#userSearch').marcoPolo('option');
Set a specific option:
$('#userSearch').marcoPolo('option', 'url', '/new/url');
Set multiple options:
$('#userSearch').marcoPolo('option', { url: '/new/url', onSelect: function (data, $item) { … } });
Parameters:
- nameOrValues string, object Optional options to get or set.
- value mixed Optional option value to set.
-
search
Programmatically trigger a search request using the existing input value
or a new one. The input receives focus to allow for keyboard navigation.Example:
Trigger a search on the existing input value:
$('#userSearch').marcoPolo('search');
Trigger a search on a new value:
$('#userSearch').marcoPolo('search', 'Wilson');
Parameters:
- q string Optional new input value to search on.
-
select
Set the currently selected data, just as if the user clicked or keyboard
selected an item from the results list. The onSelect callback is fired.Example:
$('#userSearch').marcoPolo('select', { first_name: 'Lindsay', … });
Parameters:
- data string, object Data of the selected item.
-
selected
Get the currently selected data (string, object, or null if not set).
Example:
$('#userSearch').marcoPolo('selected');
Feedback
Please open an issue to request a feature or submit a bug report. Or even if
you just want to provide some feedback, I’d love to hear. I’m also available on
Twitter as @kidjustino.
Contributing
- Fork it.
- Create your feature branch (
git checkout -b my-new-feature
). - Commit your changes (
git commit -am 'Added some feature'
). - Push to the branch (
git push origin my-new-feature
). - Create a new Pull Request.