UNPKG

awesomplete

Version:

http://leaverou.github.io/awesomplete/

540 lines (468 loc) 23.7 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title>Awesomplete: Ultra lightweight, highly customizable, simple autocomplete, by Lea Verou</title> <link rel="stylesheet" href="prism/prism.css" /> <link rel="stylesheet" href="awesomplete.css" /> <link rel="stylesheet" href="style.css" /> <script src="awesomplete.js"></script> <script src="index.js"></script> </head> <body class="language-markup"> <header> <h1>Awesomplete</h1> <a href="#download" class="size"><strong>2KB</strong> minified <span class="amp">&amp;</span> gzipped!</a> <p>Ultra lightweight, customizable, simple autocomplete widget with zero dependencies, built with modern standards for <abbr title="Verified to work in: IE9 (sorta), IE10+, Chrome, Firefox, Safari 5+, Mobile Safari">modern browsers</abbr>. <a href="http://lea.verou.me/2015/02/awesomplete-2kb-autocomplete-with-zero-dependencies">Because <code>&lt;datalist></code> still doesn’t cut it.</a></p> <nav></nav> </header> <section> <h1>Demo (no JS, minimal options)</h1> <label> Pick a programming language:<br /> <input autofocus class="awesomplete" data-list="Ada, Java, JavaScript, Brainfuck, LOLCODE, Node.js, Ruby on Rails" /> </label> <p>Note that by default you need to type <strong>at least 2 characters</strong> for the popup to show up, though that’s <a href="#customization">super easy to customize</a>. With Awesomplete, making something like this can be as simple as:</p> <pre class="language-markup"><code>&lt;input class="awesomplete" data-list="Ada, Java, JavaScript, Brainfuck, LOLCODE, Node.js, Ruby on Rails" /></code></pre> <pre class="language-javascript"><code>// No extra JS needed for basic usage!</code></pre> </section> <section id="basic-usage"> <h1>Basic usage</h1> <p>Before you try anything, you need to include <code>awesomplete.css</code> and <code>awesomplete.js</code> in your page, via the usual <code>&lt;link rel="stylesheet" href="awesomplete.css" /></code> and <code>&lt;script src="awesomplete.js" async>&lt;/script></code> tags.</p> <p>For the autocomplete, you just need an <code>&lt;input></code> text field (might work on <code>&lt;textarea></code> and elements with <code>contentEditable</code>, but that hasn’t been tested). Add <code>class="awesomplete"</code> for it to be <strong>automatically processed</strong> (you can still specify many options via HTML attributes), otherwise you can instantiate with a few lines of JS code, which allow for more customization.</p> <p>There are many ways <strong>to link an input to a list of suggestions</strong>. The simple example above could have also been made with the following markup, which provides a nice native fallback in case the script doesn’t load:</p> <pre class="language-markup"><code>&lt;input class="awesomplete" list="mylist" /> &lt;datalist id="mylist"> &lt;option>Ada&lt;/option> &lt;option>Java&lt;/option> &lt;option>JavaScript&lt;/option> &lt;option>Brainfuck&lt;/option> &lt;option>LOLCODE&lt;/option> &lt;option>Node.js&lt;/option> &lt;option>Ruby on Rails&lt;/option> &lt;/datalist></code></pre> <pre class="language-javascript"><code>// None!</code></pre> <p>Or the following, if you don’t want to use a <code>&lt;datalist></code>, or if you don’t want to use IDs (since any selector will work in <code>data-list</code>):</p> <pre class="language-markup"><code>&lt;input class="awesomplete" data-list="#mylist" /> &lt;ul id="mylist"> &lt;li>Ada&lt;/li> &lt;li>Java&lt;/li> &lt;li>JavaScript&lt;/li> &lt;li>Brainfuck&lt;/li> &lt;li>LOLCODE&lt;/li> &lt;li>Node.js&lt;/li> &lt;li>Ruby on Rails&lt;/li> &lt;/ul></code></pre> <pre class="language-javascript"><code>// None!</code></pre> <p>Or the following, if we want to instantiate in JS:</p> <pre class="language-markup"><code>&lt;input id="myinput" /> &lt;ul id="mylist"> &lt;li>Ada&lt;/li> &lt;li>Java&lt;/li> &lt;li>JavaScript&lt;/li> &lt;li>Brainfuck&lt;/li> &lt;li>LOLCODE&lt;/li> &lt;li>Node.js&lt;/li> &lt;li>Ruby on Rails&lt;/li> &lt;/ul></code></pre> <pre class="language-javascript"><code>var input = document.getElementById("myinput"); new Awesomplete(input, {list: "#mylist"});</code></pre> <p>We can use an <strong>element reference</strong> for the list instead of a selector:</p> <pre class="language-markup"><code>&lt;input id="myinput" /> &lt;ul id="mylist"> &lt;li>Ada&lt;/li> &lt;li>Java&lt;/li> &lt;li>JavaScript&lt;/li> &lt;li>Brainfuck&lt;/li> &lt;li>LOLCODE&lt;/li> &lt;li>Node.js&lt;/li> &lt;li>Ruby on Rails&lt;/li> &lt;/ul></code></pre> <pre class="language-javascript"><code>var input = document.getElementById("myinput"); new Awesomplete(input, {list: document.querySelector("#mylist")});</code></pre> <p>We can also directly use an <strong>array of strings</strong>:</p> <pre class="language-markup"><code>&lt;input id="myinput" /></code></pre> <pre class="language-javascript"><code>var input = document.getElementById("myinput"); new Awesomplete(input, { list: ["Ada", "Java", "JavaScript", "Brainfuck", "LOLCODE", "Node.js", "Ruby on Rails"] });</code></pre> <p>We can even set it (or override it) later and it will just work:</p> <pre class="language-markup"><code>&lt;input id="myinput" /></code></pre> <pre class="language-javascript"><code>var input = document.getElementById("myinput"); var awesomplete = new Awesomplete(input); /* ...more code... */ awesomplete.list = ["Ada", "Java", "JavaScript", "Brainfuck", "LOLCODE", "Node.js", "Ruby on Rails"];</code></pre> <p>Suggestions with different <strong>label</strong> and <strong>value</strong> are supported too. The label will be shown in autocompleter and the <strong>value</strong> will be inserted into the input. If you want to insert the label into the input you can provide your own <strong>replace</strong> function</p> <pre class="language-markup"><code>&lt;input id="myinput" /></code></pre> <pre class="language-javascript"><code>var input = document.getElementById("myinput"); // Show label but insert value into the input: new Awesomplete(input, { list: [ { label: "Belarus", value: "BY" }, { label: "China", value: "CN" }, { label: "United States", value: "US" } ] }); // Same with arrays: new Awesomplete(input, { list: [ [ "Belarus", "BY" ], [ "China", "CN" ], [ "United States", "US" ] ] }); // Show label and insert label into the input: new Awesomplete(input, { list: [ { label: "Belarus", value: "BY" }, { label: "China", value: "CN" }, { label: "United States", value: "US" } ], // insert label instead of value into the input. replace: function(suggestion) { this.input.value = suggestion.label; } }); </code></pre> </section> <section id="customization"> <h1>Customize</h1> <p>All settings discussed in this section are settable via either a <code>data-</code> attribute on the <code>&lt;input></code> element or a JS property on the second argument of the <code>Awesomplete</code> constructor, like so:</p> <pre class="language-javascript"><code>new Awesomplete(inputReference, { minChars: 3, maxItems: 15, ... });</code></pre> <p>You can of course combine both HTML attributes and JS properties. <strong>In case of conflict</strong> (e.g. you’ve specified both a <code>data-minchars</code> on the text field and a <code>minChars</code> JS property, <strong>the HTML attribute wins</strong>. You can also use the JS properties to change a parameter <strong>after</strong> the object has been created, in which case the change will apply even if there is a conflicting HTML attribute.</p> <table> <thead> <tr> <th>JS property</th> <th>HTML attribute</th> <th>Description</th> <th>Value</th> <th>Default</th> </tr> </thead> <tbody> <tr> <td><code>list</code></td> <td><code>data-list</code></td> <td>Where to find the list of suggestions. Described in more detail in the “<a href="#basic-usage">Basic usage</a>” section above.</td> <td><ul> <li>Array of strings</li> <li>HTML element</li> <li>CSS selector (no groups, i.e. no commas)</li> <li>String containing a comma-separated list of items</li> </ul></td> <td>N/A</td> </tr> <tr> <td><code>minChars</code></td> <td><code>data-minchars</code></td> <td>Minimum characters the user has to type before the autocomplete popup shows up.</td> <td>Number</td> <td>2</td> </tr> <tr> <td><code>maxItems</code></td> <td><code>data-maxitems</code></td> <td>Maximum number of suggestions to display.</td> <td>Number</td> <td>10</td> </tr> <tr> <td><code>autoFirst</code></td> <td><code>data-autofirst</code></td> <td>Should the first element be automatically selected? Demo: <input class="awesomplete" data-autofirst data-list="Ada, Java, JavaScript, Brainfuck, LOLCODE, Node.js, Ruby on Rails" xautofocus /></td> <td>Boolean</td> <td>false</td> </tr> <tr> <td><code>tabSelect</code></td> <td><code>data-tabSelect</code></td> <td>Should the first element be selected when the user hits the TAB key when the field has focus?></td> <td>Boolean</td> <td>false</td> </tr> <tr> <td><code>listLabel</code></td> <td><code>data-listlabel</code></td> <td>Denotes a label to be used as aria-label on the generated autocomplete list.</td> <td>String</td> <td>Results List</td> </tr> </tbody> </table> </section> <section id="extensibility"> <h1>Extend</h1> <p>The following JS properties do not have equivalent HTML attributes, because their values are functions. They allow you to completely change the way Awesomplete works:</p> <table> <thead> <tr> <th>Property</th> <th>Description</th> <th>Value</th> <th>Default</th> </tr> </thead> <tbody> <tr> <td><code>filter</code></td> <td>Controls how entries get matched. By default, the input can match anywhere in the string and it’s a case insensitive match.</td> <td>Function that takes two parameters, the first one being the current suggestion text that’s being tested and the second a string with the user’s input it’s matched against. Returns <code>true</code> if the match is successful and <code>false</code> if it is not. For example, to only match strings that <strong>start with the user’s input</strong>, <strong>case sensitive</strong>, we can do this: <pre class="language-javascript"><code>filter: function (text, input) { return text.indexOf(input) === 0; }</code></pre> For case-<strong>in</strong>sensitive matching from the start of the word, there is a predefined filter that you can use, <code class="language-javascript">Awesomplete.FILTER_STARTSWITH</code> </td> <td><code class="language-javascript">Awesomplete.FILTER_CONTAINS</code>: Text can match anywhere, case insensitive.</td> </tr> <tr> <td><code>sort</code></td> <td>Controls how list items are ordered.</td> <td>Sort function (will be passed directly to <code>Array.prototype.sort()</code>) to sort the items after they have been filtered and before they are truncated and converted to HTML elements. If value is <code>false</code>, sorting will be disabled.</td> <td>Sorted by length first, order second.</td> </tr> <tr> <td><code>container</code></td> <td>Controls how list container element is generated.</td> <td>Function that takes one parameter, the user’s input and returns an element.</td> <td>Generates <code>&lt;div></code> with class <code>awesomplete</code></td> </tr> <tr> <td><code>item</code></td> <td>Controls how list items are generated.</td> <td>Function that takes two parameters, the first one being the suggestion text and the second one the user’s input and returns a list item.</td> <td>Generates list items with the user’s input highlighted via <code>&lt;mark></code>.</td> </tr> <tr> <td><code>replace</code></td> <td>Controls how the user’s selection replaces the user’s input. For example, this is useful if you want the selection to only partially replace the user’s input.</td> <td>Function that takes one parameter, the text of the selected option, and is responsible for replacing the current input value with it. </td> <td><pre class="language-javascript"><code>function (text) { this.input.value = text; }</code></pre></td> </tr> <tr> <td><code>data</code></td> <td>Controls suggestions' <code>label</code> and <code>value</code>. This is useful if you have list items in custom format, or want to change list items based on user's input.</td> <td>Function that takes two parameters, the first one being the original list item and the second a string with the user’s input and returns a list item in one of supported by default formats: <ul> <li><code>"JavaScript"</code></li> <li><code>{ label: "JavaScript", value: "JS" }</code></li> <li><code>[ "JavaScript", "JS" ]</code></li> </ul> To <strong>use objects without <code>label</code> or <code>value</code> properties</strong>, e.g. <code>name</code> and <code>id</code> instead, you can do this: <pre class="language-javascript"><code>data: function (item, input) { return { label: item.name, value: item.id }; }</code></pre> You can <strong>use any object for <code>label</code> and <code>value</code></strong> and it will be converted to String where necessary: <pre class="language-javascript"><code>list: [ new Date("2015-01-01"), ... ] </code></pre> Original list items as Date objects will be accessible in <code>filter</code>, <code>sort</code>, <code>item</code> and <code>replace</code> functions, but by default we'll just see Date objects converted to strings in autocompleter and the same value will be inserted to the input. <br /> We can also <strong>generate list items based on user's input</strong>. See E-mail autocomplete example in <a href="#advanced-examples">Advanced Examples</a> section. </td> <td><code class="language-javascript">Awesomplete.DATA</code>: Identity function which just returns the original list item.</td> </tr> </tbody> </table> </section> <section id="events"> <h1>Events</h1> <p>Custom events are thrown in several places and are often cancellable. To avoid conflicts, all custom events are prefixed with <code>awesomplete-</code>.</p> <table> <thead> <tr> <th>Name</th> <th>Description</th> <th>event.preventDefault()?</th> </tr> </thead> <tbody> <tr> <td><code>awesomplete-select</code></td> <td>The user has made a selection (either via pressing enter or clicking on an item), but it has not been applied yet. Callback will be passed an object with <code>text</code> (selected suggestion), <code>origin</code> (DOM element) properties and <code>originalEvent</code> the original triggering DOM event.</td> <td>Yes. The selection will not be applied and the popup will not close.</td> </tr> <tr> <td><code>awesomplete-selectcomplete</code></td> <td>The user has made a selection (either via pressing enter or clicking on an item), and it has been applied. Callback will be passed an object with a <code>text</code> property containing the selected suggestion and <code>originalEvent</code> the original triggering DOM event.</td> <td>No</td> </tr> <tr> <td><code>awesomplete-open</code></td> <td>The popup just appeared.</td> <td>No</td> </tr> <tr> <td><code>awesomplete-close</code></td> <td>The popup just closed. Callback will be passed an object with a <code>reason</code> property that indicates why the event was fired. Reasons include <code>"blur"</code>, <code>"esc"</code>, <code>"submit"</code>, <code>"select"</code>, and <code>"nomatches"</code>.</td> <td>No</td> </tr> <tr> <td><code>awesomplete-highlight</code></td> <td>The highlighted item just changed (in response to pressing an arrow key or via an API call). Callback will be passed an object with a <code>text</code> property containing the highlighted suggestion.</td> <td>No</td> </tr> </tbody> </table> </section> <section id="api"> <h1>API</h1> <p>There are several methods on every Awesomplete instance that you can call to customize behavior:</p> <table> <thead> <tr> <th>Method</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td><code>open()</code></td> <td>Opens the popup.</td> </tr> <tr> <td><code>close()</code></td> <td>Closes the popup.</td> </tr> <tr> <td><code>next()</code></td> <td>Highlights the next item in the popup.</td> </tr> <tr> <td><code>previous()</code></td> <td>Highlights the previous item in the popup.</td> </tr> <tr> <td><code>goto(i)</code></td> <td>Highlights the item with index <code>i</code> in the popup (<code>-1</code> to deselect all). Avoid using this directly and try to use <code>next()</code> or <code>previous()</code> instead when possible.</td> </tr> <tr> <td><code>select()</code></td> <td>Selects the currently highlighted item, replaces the text field’s value with it and closes the popup.</td> </tr> <tr> <td><code>evaluate()</code></td> <td>Evaluates the current state of the widget and regenerates the list of suggestions or closes the popup if none are available. You need to call it if you dynamically set <code>list</code> while the popup is open.</td> </tr> <tr> <td><code>destroy()</code></td> <td>Clean up and remove the instance from the input. The container is only removed if it wasn't manually set but created by Awesomplete.</td> </tr> </tbody> </table> </section> <section id="advanced-examples"> <h1>Advanced Examples</h1> <p>These examples show how powerful Awesomplete’s minimal API can be.</p> <section id="email"> <h2>E-mail autocomplete</h2> <label>Type an email: <input type="email"></label> <pre class="language-markup"><code>&lt;input type="email" /></code></pre> <pre class="language-javascript"><code><script>new Awesomplete('input[type="email"]', { list: ["aol.com", "att.net", "comcast.net", "facebook.com", "gmail.com", "gmx.com", "googlemail.com", "google.com", "hotmail.com", "hotmail.co.uk", "mac.com", "me.com", "mail.com", "msn.com", "live.com", "sbcglobal.net", "verizon.net", "yahoo.com", "yahoo.co.uk"], data: function (text, input) { return input.slice(0, input.indexOf("@")) + "@" + text; }, filter: Awesomplete.FILTER_STARTSWITH });</script></code></pre> </section> <section id="multiple-values"> <h2>Multiple values</h2> <label>Tags (comma separated): <input data-list="CSS, JavaScript, HTML, SVG, ARIA, MathML" data-multiple data-minchars="1" /></label> <pre class="language-markup"><code>&lt;input data-list="CSS, JavaScript, HTML, SVG, ARIA, MathML" data-multiple /></code></pre> <pre class="language-javascript"><code><script>new Awesomplete('input[data-multiple]', { filter: function(text, input) { return Awesomplete.FILTER_CONTAINS(text, input.match(/[^,]*$/)[0]); }, item: function(text, input) { return Awesomplete.ITEM(text, input.match(/[^,]*$/)[0]); }, replace: function(text) { var before = this.input.value.match(/^.+,\s*|/)[0]; this.input.value = before + text + ", "; } });</script></code></pre> </section> <section id="ajax-example"> <h2>Ajax example <small>(restcountries.eu api)</small></h2> <label class="">Select French speaking country <input type="text"></label> <pre class="language-javascript"><code><script>var ajax = new XMLHttpRequest(); ajax.open("GET", "https://restcountries.eu/rest/v1/lang/fr", true); ajax.onload = function() { var list = JSON.parse(ajax.responseText).map(function(i) { return i.name; }); new Awesomplete(document.querySelector("#ajax-example input"),{ list: list }); }; ajax.send();</script></code></pre> </section> <section id="ajax-example"> <h2>Custom list example <small>(based on user input)</small></h2> <pre class="language-markup"><code>&lt;input id="query" class="awesomplete" /&gt;</code></pre> <pre class="language-javascript"><code><script> const queryInput = document.querySelector("#query"); const awesomplete = new Awesomplete(queryInput, { filter: () => { // We will provide a list that is already filtered ... return true; }, sort: false, // ... and sorted. list: [] }); queryInput.addEventListener("input", (event) => { const inputText = event.target.value; // Process inputText as you want, e.g. make an API request. awesomplete.list = ["my"+inputText, "custom"+inputText, "list"+inputText]; awesomplete.evaluate(); }); </script></code></pre> </section> <section id="combobox"> <h2>Combobox dropdown</h2> <label>Type or click dropdown: <input data-list="CSS, JavaScript, HTML, SVG, ARIA, MathML" class="dropdown-input" /><button class="dropdown-btn" type="button"><span class="caret"></span></button></label> <pre class="language-markup"><code>&lt;input data-list="CSS, JavaScript, HTML, SVG, ARIA, MathML" class="dropdown-input" /></code></pre> <pre class="language-javascript"><code><script>var comboplete = new Awesomplete('input.dropdown-input', { minChars: 0, }); Awesomplete.$('.dropdown-btn').addEventListener("click", function() { if (comboplete.ul.childNodes.length === 0) { comboplete.minChars = 0; comboplete.evaluate(); } else if (comboplete.ul.hasAttribute('hidden')) { comboplete.open(); } else { comboplete.close(); } });</script></code></pre> </section> <section id="download"> <h1>Download!</h1> <ul> <li><strong><a href="https://github.com/LeaVerou/awesomplete/archive/gh-pages.zip">Download everything as a zip file</a></strong></li> <li><a href="https://github.com/LeaVerou/awesomplete">Fork me on Github</a></li> <li><a href="https://github.com/LeaVerou/awesomplete/issues/new">File a bug</a> </li> <li><a href="https://github.com/LeaVerou/awesomplete/issues/new">Suggest a feature</a> </li> <li><a href="https://github.com/LeaVerou/awesomplete/blob/gh-pages/LICENSE">License: MIT license</a></li> </ul> <p><strong>Pull requests are very welcome</strong>, as long as you maintain the code style and ask before adding tons of LOCs to the codebase!</p> </section> <footer>Made with &hearts; by <a href="http://lea.verou.me">Lea Verou</a> &bull; <a href="http://lea.verou.me/2015/02/awesomplete-2kb-autocomplete-with-zero-dependencies">Read the blog post</a> &bull; <a href="http://shop.oreilly.com/product/0636920031123.do">Buy my book!</a> </footer> <script src="prism/prism.js" defer></script> <iframe src="https://ghbtns.com/github-btn.html?user=leaverou&repo=awesomplete&type=watch&count=true&size=large" height="30" width="170" frameborder="0" scrolling="0" style="width:170px; height: 30px;" allowTransparency="true" class="github-star"></iframe> <a href="https://twitter.com/share" class="twitter-share-button" data-via="LeaVerou" data-size="large">Tweet</a> <script async src="//cdn.carbonads.com/carbon.js?zoneid=1673&serve=C6AILKT&placement=leaveroume" id="_carbonads_js"></script> <script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');</script> <script> (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-25106441-4', 'auto'); ga('send', 'pageview'); </script> </body> </html>