Skip to content
This repository

A jQuery autocomplete plugin for the discerning developer.

README.md

Marco Polo

Selenium Test Status Build Status

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, null

    Format 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, null

    Called 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 @jstayton.

Contributing

  1. Fork it.
  2. Create your feature branch (git checkout -b my-new-feature).
  3. Commit your changes (git commit -am 'Added some feature').
  4. Push to the branch (git push origin my-new-feature).
  5. Create a new Pull Request.
Something went wrong with that request. Please try again.