Autosuggest
Autosuggest is used to suggest options to users as they enter something into an input field. A user can select one of the suggestions or enter their own answer.
<div class="ons-grid ons-grid--gutterless">
<div class="ons-grid__col ons-col-8@m">
<div id="country-of-birth-container" class="ons-js-autosuggest ons-autosuggest-input" data-instructions="Use up and down keys to navigate suggestions once you've typed more than two characters. Use the enter key to select a suggestion. Touch device users, explore by touch or with swipe gestures." data-aria-you-have-selected="You have selected" data-min-chars="" data-aria-min-chars="Enter 3 or more characters for suggestions." data-aria-one-result="There is one suggestion available." data-aria-n-results="There are {n} suggestions available." data-aria-limited-results="Results have been limited to 10 suggestions. Type more characters to improve your search" data-more-results="Continue entering to improve suggestions" data-results-title="Suggestions" data-no-results="No suggestions found. You can enter your own answer" data-type-more="Continue entering to get suggestions" data-autosuggest-data="https://gist.githubusercontent.com/rmccar/c123023fa6bd1b137d7f960c3ffa1fed/raw/4dede1d6e757cf0bb836228600676c62ceb4f86c/country-of-birth.json">
<div class="ons-field">
<label class="ons-label ons-label--with-description" for="country-of-birth" id="country-of-birth-label">Current name of country</label>
<span id="country-of-birth-label-description-hint" class="ons-label__description ons-input--with-description">Enter your own answer or select from suggestions</span>
<input type="text" id="country-of-birth" class="ons-input ons-input--text ons-input-type__input ons-js-autosuggest-input " autocomplete="off" aria-describedby="country-of-birth-label-description-hint" />
</div>
<div class="ons-autosuggest-input__results ons-js-autosuggest-results">
<header id="country-of-birth-suggestions" class="ons-autosuggest-input__results-title ons-u-fs-s">Suggestions</header>
<ul class="ons-autosuggest-input__listbox ons-js-autosuggest-listbox" role="listbox" id="country-of-birth-listbox" aria-labelledby="country-of-birth-suggestions" tabindex="-1"></ul>
</div>
<div class="ons-autosuggest-input__instructions ons-u-vh ons-js-autosuggest-instructions" id="country-of-birth-instructions" tabindex="-1">Use up and down keys to navigate suggestions once you've typed more than two characters. Use the enter key to select a suggestion. Touch device users, explore by touch or with swipe gestures.</div>
<div class="ons-autosuggest-input__status ons-u-vh ons-js-autosuggest-aria-status" aria-live="assertive" role="status" tabindex="-1"></div>
</div>
</div>
</div>
{% from "components/autosuggest/_macro.njk" import onsAutosuggest %}
<div class="ons-grid ons-grid--gutterless">
<div class="ons-grid__col ons-col-8@m">
{{ onsAutosuggest({
"id": "country-of-birth",
"label": {
"text": "Current name of country",
"description": "Enter your own answer or select from suggestions",
"id": "country-of-birth-label"
},
"autocomplete": "off",
"instructions": "Use up and down keys to navigate suggestions once you\'ve typed more than two characters. Use the enter key to select a suggestion. Touch device users, explore by touch or with swipe gestures.",
"ariaYouHaveSelected": "You have selected",
"ariaMinChars": "Enter 3 or more characters for suggestions.",
"ariaResultsLabel": "Country suggestions",
"ariaOneResult": "There is one suggestion available.",
"ariaNResults": "There are {n} suggestions available.",
"ariaLimitedResults": "Results have been limited to 10 suggestions. Type more characters to improve your search",
"moreResults": "Continue entering to improve suggestions",
"resultsTitle": "Suggestions",
"resultsTitleId": "country-of-birth-suggestions",
"autosuggestData": "https://gist.githubusercontent.com/rmccar/c123023fa6bd1b137d7f960c3ffa1fed/raw/4dede1d6e757cf0bb836228600676c62ceb4f86c/country-of-birth.json",
"noResults": "No suggestions found. You can enter your own answer",
"typeMore": "Continue entering to get suggestions"
}) }}
</div>
</div>
{% from "components/input/_macro.njk" import onsInput %}
{% macro onsAutosuggest(params) %}
<div
id="{{ params.id }}-container"
class="{% if not params.externalInitialiser %}ons-js-autosuggest {% endif %}{% if params.isEditable == false %}ons-js-address-not-editable{% endif %}{% if params.mandatory is defined and params.mandatory == true %} ons-js-address-mandatory{% endif %} {% if params.containerClasses is defined and params.containerClasses %} {{ params.containerClasses }}{% endif %} ons-autosuggest-input"
data-instructions="{{ params.instructions }}"
data-aria-you-have-selected="{{ params.ariaYouHaveSelected }}"
data-min-chars="{{ params.minChars }}"
data-aria-min-chars="{{ params.ariaMinChars }}"
data-aria-one-result="{{ params.ariaOneResult }}"
data-aria-n-results="{{ params.ariaNResults }}"
data-aria-limited-results="{{ params.ariaLimitedResults }}"
data-more-results="{{ params.moreResults }}"
data-results-title="{{ params.resultsTitle }}"
data-no-results="{{ params.noResults }}"
data-type-more="{{ params.typeMore }}"
{% if params.APIDomain is defined and params.APIDomain %}data-api-domain="{{ params.APIDomain }}"{% endif %}
{% if params.APIDomainBearerToken is defined and params.APIDomainBearerToken %}data-authorization-token="{{ params.APIDomainBearerToken }}"{% endif %}
{% if params.APIManualQueryParams is defined and params.APIManualQueryParams == true %}data-query-params=""{% endif %}
{% if params.allowMultiple is defined and params.allowMultiple == true %}data-allow-multiple="true"{% endif %}
{% if params.autosuggestData is defined and params.autosuggestData %}data-autosuggest-data="{{ params.autosuggestData }}"{% endif %}
{% if params.errorTitle is defined and params.errorTitle %}data-error-title="{{ params.errorTitle }}"{% endif %}
{% if params.errorMessageEnter is defined and params.errorMessageEnter %}data-error-enter="{{ params.errorMessageEnter }}"{% endif %}
{% if params.errorMessageSelect is defined and params.errorMessageSelect %}data-error-select="{{ params.errorMessageSelect }}"{% endif %}
{% if params.ariaGroupedResults is defined and params.ariaGroupedResults %}data-aria-grouped-results="{{ params.ariaGroupedResults }}"{% endif %}
{% if params.groupCount is defined and params.groupCount %}data-group-count="{{ params.groupCount }}"{% endif %}
{% if params.tooManyResults is defined and params.tooManyResults %}data-too-many-results="{{ params.tooManyResults }}"{% endif %}
{% if params.errorMessageAPI is defined and params.errorMessageAPI %}data-error-api="{{ params.errorMessageAPI }}"{% endif %}
{% if params.errorMessageAPILinkText is defined and params.errorMessageAPILinkText %}data-error-api-link-text="{{ params.errorMessageAPILinkText }}"{% endif %}
{% if params.options is defined and params.options %}
{% if params.options.oneYearAgo is defined and params.options.oneYearAgo %}data-options-one-year-ago="true"{% endif %}
{% if params.options.regionCode is defined and params.options.regionCode %}data-options-region-code="{{ params.options.regionCode }}"{% endif %}
{% if params.options.addressType is defined and params.options.addressType %}data-options-address-type="{{ params.options.addressType }}"{% endif %}
{% endif %}
>
{% set autosuggestResults %}
<div class="ons-autosuggest-input__results ons-js-autosuggest-results">
<header id="{{ params.resultsTitleId }}" class="ons-autosuggest-input__results-title ons-u-fs-s">{{ params.resultsTitle }}</header>
<ul class="ons-autosuggest-input__listbox ons-js-autosuggest-listbox" role="listbox" id="{{ params.id }}-listbox" aria-labelledby="{{ params.resultsTitleId }}" tabindex="-1"></ul>
</div>
<div class="ons-autosuggest-input__instructions ons-u-vh ons-js-autosuggest-instructions" id="{{ params.id }}-instructions" tabindex="-1">{{ params.instructions }}</div>
<div class="ons-autosuggest-input__status ons-u-vh ons-js-autosuggest-aria-status" aria-live="assertive" role="status" tabindex="-1"></div>
{% endset %}
{{ onsInput({
"id": params.id,
"classes": "ons-js-autosuggest-input " + (params.classes if params.classes else ''),
"width": params.width,
"label": {
"text": params.label.text,
"description": params.label.description,
"id": params.label.id,
"classes": params.label.classes
},
"autocomplete": params.autocomplete,
"legend": params.legend,
"legendClasses": params.legendClasses,
"value": params.value,
"attributes": params.attributes,
"error": params.error,
"mutuallyExclusive": params.mutuallyExclusive,
"accessiblePlaceholder": params.accessiblePlaceholder,
"name": params.name,
"autosuggestResults": autosuggestResults
}) }}
{% if params.mutuallyExclusive is not defined %}
{{ autosuggestResults | safe }}
{% endif %}
</div>
{% endmacro %}
.ons-autosuggest-input {
position: relative;
&__combobox {
border-radius: $input-radius;
display: inline-block;
@include mq(xxs, s) {
width: 100%;
}
}
&__results {
border: 1px solid $color-input;
border-radius: $input-radius;
display: none;
margin: 0.5rem 0 0;
overflow: hidden;
padding: 0;
width: 100%;
}
&__group {
color: $color-text-link;
text-decoration: underline;
}
&__results-title {
background: $color-grey-15;
border-bottom: 1px solid $color-input;
padding: 0.25rem 0.5rem;
}
&__listbox {
background: $color-white;
list-style: none;
margin: 0;
padding: 0;
&:focus {
outline: none;
}
}
&__option {
cursor: pointer;
margin: 0;
outline: none;
padding: $input-padding-horizontal;
&:not(:last-child) {
border-bottom: 1px solid $color-input;
}
&:not(&--no-results):not(&--more-results):hover,
&--focused:not(&--no-results) {
background: $color-text-link-hover;
border-color: $color-text-link-hover;
color: $color-white;
.ons-autosuggest-input__group,
.ons-autosuggest-input__category {
color: $color-white;
}
}
&:active:not(&--no-results):not(&--more-results) {
background: $color-focus;
color: $color-text-link-focus;
.ons-autosuggest-input__group,
.ons-autosuggest-input__category {
color: $color-text-link-focus;
}
}
&--no-results,
&--more-results {
background: $color-grey-15;
cursor: not-allowed;
padding: 0.25rem 0.5rem;
}
}
&__warning {
background: #f0f0f0;
margin: 0;
padding-left: 0.5rem;
&:not(:last-child) {
border-bottom: 1px solid $color-input;
}
}
&__panel.ons-panel--warn {
background: none;
border: 0;
margin: 0;
.ons-panel__icon {
font-size: 21px;
line-height: 25px;
margin-top: 0;
min-height: 24px;
min-width: 26px;
top: 17px;
}
.ons-panel__body {
font-weight: bold;
padding: 0.8rem 0.8rem 0.8rem 2rem;
}
}
// Modifiers
&:not(&--initialised) & {
&__instructions,
&__listbox,
&__status {
display: none;
}
}
&--has-results & {
&__results {
display: block;
}
}
&--header {
.ons-autosuggest-input__results {
border: none;
box-shadow: 0 0 5px 0 rgba($color-black, 0.6);
left: 0;
position: absolute;
z-index: 1;
}
}
}
When to use this component
Use the autosuggest component to help users select from a list of standard responses, for example, countries, languages or nationalities. You could also create your own list for the user to pick from.
How to use
The list needs to be made available as a JSON file (.json
). To query the list we use a JavaScript library called
Fuse.js
. You will need to provide the URL where the JSON file is stored using the autosuggestData
parameter.
List formatting
This code example shows how your JSON should be formatted. The key
should be the language code that matches the lang
attribute on the <html>
element of the page.
[
{
"en": "England"
},
{
"en": "Wales"
},
{
"en": "Scotland"
},
{
"en": "Northern Ireland"
},
]
Multiple suggestions
You can configure the component to allow multiple suggestions by passing in the parameter allowMultiple: "true"
.
<div class="ons-grid ons-grid--gutterless">
<div class="ons-grid__col ons-col-8@m">
<div id="country-of-birth-container" class="ons-js-autosuggest ons-autosuggest-input" data-instructions="Use up and down keys to navigate suggestions once you've typed more than two characters. Use the enter key to select a suggestion. Touch device users, explore by touch or with swipe gestures." data-aria-you-have-selected="You have selected" data-min-chars="" data-aria-min-chars="Enter 3 or more characters for suggestions." data-aria-one-result="There is one suggestion available." data-aria-n-results="There are {n} suggestions available." data-aria-limited-results="Results have been limited to 10 suggestions. Type more characters to improve your search" data-more-results="Continue entering to improve suggestions" data-results-title="Suggestions" data-no-results="No suggestions found. You can enter your own answer" data-type-more="Continue entering to get suggestions" data-allow-multiple="true" data-autosuggest-data="https://gist.githubusercontent.com/rmccar/c123023fa6bd1b137d7f960c3ffa1fed/raw/4dede1d6e757cf0bb836228600676c62ceb4f86c/country-of-birth.json">
<div class="ons-field">
<label class="ons-label ons-label--with-description" for="country-of-birth" id="country-of-birth-label">Passport</label>
<span id="country-of-birth-label-description-hint" class="ons-label__description ons-input--with-description">Enter your own answer or select from suggestions. You can enter multiple countries if you have a passport for more than one.</span>
<input type="text" id="country-of-birth" class="ons-input ons-input--text ons-input-type__input ons-js-autosuggest-input " autocomplete="off" aria-describedby="country-of-birth-label-description-hint" />
</div>
<div class="ons-autosuggest-input__results ons-js-autosuggest-results">
<header id="country-of-birth-suggestions" class="ons-autosuggest-input__results-title ons-u-fs-s">Suggestions</header>
<ul class="ons-autosuggest-input__listbox ons-js-autosuggest-listbox" role="listbox" id="country-of-birth-listbox" aria-labelledby="country-of-birth-suggestions" tabindex="-1"></ul>
</div>
<div class="ons-autosuggest-input__instructions ons-u-vh ons-js-autosuggest-instructions" id="country-of-birth-instructions" tabindex="-1">Use up and down keys to navigate suggestions once you've typed more than two characters. Use the enter key to select a suggestion. Touch device users, explore by touch or with swipe gestures.</div>
<div class="ons-autosuggest-input__status ons-u-vh ons-js-autosuggest-aria-status" aria-live="assertive" role="status" tabindex="-1"></div>
</div>
</div>
</div>
{% from "components/autosuggest/_macro.njk" import onsAutosuggest %}
<div class="ons-grid ons-grid--gutterless">
<div class="ons-grid__col ons-col-8@m">
{{ onsAutosuggest({
"id": "country-of-birth",
"label": {
"text": "Passport",
"id": "country-of-birth-label",
"description": "Enter your own answer or select from suggestions. You can enter multiple countries if you have a passport for more than one."
},
"autocomplete": "off",
"instructions": "Use up and down keys to navigate suggestions once you\'ve typed more than two characters. Use the enter key to select a suggestion. Touch device users, explore by touch or with swipe gestures.",
"ariaYouHaveSelected": "You have selected",
"ariaMinChars": "Enter 3 or more characters for suggestions.",
"ariaResultsLabel": "Passport suggestions",
"ariaOneResult": "There is one suggestion available.",
"ariaNResults": "There are {n} suggestions available.",
"ariaLimitedResults": "Results have been limited to 10 suggestions. Type more characters to improve your search",
"moreResults": "Continue entering to improve suggestions",
"resultsTitle": "Suggestions",
"resultsTitleId": "country-of-birth-suggestions",
"allowMultiple": true,
"autosuggestData": "https://gist.githubusercontent.com/rmccar/c123023fa6bd1b137d7f960c3ffa1fed/raw/4dede1d6e757cf0bb836228600676c62ceb4f86c/country-of-birth.json",
"noResults": "No suggestions found. You can enter your own answer",
"typeMore": "Continue entering to get suggestions"
}) }}
</div>
</div>
{% from "components/input/_macro.njk" import onsInput %}
{% macro onsAutosuggest(params) %}
<div
id="{{ params.id }}-container"
class="{% if not params.externalInitialiser %}ons-js-autosuggest {% endif %}{% if params.isEditable == false %}ons-js-address-not-editable{% endif %}{% if params.mandatory is defined and params.mandatory == true %} ons-js-address-mandatory{% endif %} {% if params.containerClasses is defined and params.containerClasses %} {{ params.containerClasses }}{% endif %} ons-autosuggest-input"
data-instructions="{{ params.instructions }}"
data-aria-you-have-selected="{{ params.ariaYouHaveSelected }}"
data-min-chars="{{ params.minChars }}"
data-aria-min-chars="{{ params.ariaMinChars }}"
data-aria-one-result="{{ params.ariaOneResult }}"
data-aria-n-results="{{ params.ariaNResults }}"
data-aria-limited-results="{{ params.ariaLimitedResults }}"
data-more-results="{{ params.moreResults }}"
data-results-title="{{ params.resultsTitle }}"
data-no-results="{{ params.noResults }}"
data-type-more="{{ params.typeMore }}"
{% if params.APIDomain is defined and params.APIDomain %}data-api-domain="{{ params.APIDomain }}"{% endif %}
{% if params.APIDomainBearerToken is defined and params.APIDomainBearerToken %}data-authorization-token="{{ params.APIDomainBearerToken }}"{% endif %}
{% if params.APIManualQueryParams is defined and params.APIManualQueryParams == true %}data-query-params=""{% endif %}
{% if params.allowMultiple is defined and params.allowMultiple == true %}data-allow-multiple="true"{% endif %}
{% if params.autosuggestData is defined and params.autosuggestData %}data-autosuggest-data="{{ params.autosuggestData }}"{% endif %}
{% if params.errorTitle is defined and params.errorTitle %}data-error-title="{{ params.errorTitle }}"{% endif %}
{% if params.errorMessageEnter is defined and params.errorMessageEnter %}data-error-enter="{{ params.errorMessageEnter }}"{% endif %}
{% if params.errorMessageSelect is defined and params.errorMessageSelect %}data-error-select="{{ params.errorMessageSelect }}"{% endif %}
{% if params.ariaGroupedResults is defined and params.ariaGroupedResults %}data-aria-grouped-results="{{ params.ariaGroupedResults }}"{% endif %}
{% if params.groupCount is defined and params.groupCount %}data-group-count="{{ params.groupCount }}"{% endif %}
{% if params.tooManyResults is defined and params.tooManyResults %}data-too-many-results="{{ params.tooManyResults }}"{% endif %}
{% if params.errorMessageAPI is defined and params.errorMessageAPI %}data-error-api="{{ params.errorMessageAPI }}"{% endif %}
{% if params.errorMessageAPILinkText is defined and params.errorMessageAPILinkText %}data-error-api-link-text="{{ params.errorMessageAPILinkText }}"{% endif %}
{% if params.options is defined and params.options %}
{% if params.options.oneYearAgo is defined and params.options.oneYearAgo %}data-options-one-year-ago="true"{% endif %}
{% if params.options.regionCode is defined and params.options.regionCode %}data-options-region-code="{{ params.options.regionCode }}"{% endif %}
{% if params.options.addressType is defined and params.options.addressType %}data-options-address-type="{{ params.options.addressType }}"{% endif %}
{% endif %}
>
{% set autosuggestResults %}
<div class="ons-autosuggest-input__results ons-js-autosuggest-results">
<header id="{{ params.resultsTitleId }}" class="ons-autosuggest-input__results-title ons-u-fs-s">{{ params.resultsTitle }}</header>
<ul class="ons-autosuggest-input__listbox ons-js-autosuggest-listbox" role="listbox" id="{{ params.id }}-listbox" aria-labelledby="{{ params.resultsTitleId }}" tabindex="-1"></ul>
</div>
<div class="ons-autosuggest-input__instructions ons-u-vh ons-js-autosuggest-instructions" id="{{ params.id }}-instructions" tabindex="-1">{{ params.instructions }}</div>
<div class="ons-autosuggest-input__status ons-u-vh ons-js-autosuggest-aria-status" aria-live="assertive" role="status" tabindex="-1"></div>
{% endset %}
{{ onsInput({
"id": params.id,
"classes": "ons-js-autosuggest-input " + (params.classes if params.classes else ''),
"width": params.width,
"label": {
"text": params.label.text,
"description": params.label.description,
"id": params.label.id,
"classes": params.label.classes
},
"autocomplete": params.autocomplete,
"legend": params.legend,
"legendClasses": params.legendClasses,
"value": params.value,
"attributes": params.attributes,
"error": params.error,
"mutuallyExclusive": params.mutuallyExclusive,
"accessiblePlaceholder": params.accessiblePlaceholder,
"name": params.name,
"autosuggestResults": autosuggestResults
}) }}
{% if params.mutuallyExclusive is not defined %}
{{ autosuggestResults | safe }}
{% endif %}
</div>
{% endmacro %}
.ons-autosuggest-input {
position: relative;
&__combobox {
border-radius: $input-radius;
display: inline-block;
@include mq(xxs, s) {
width: 100%;
}
}
&__results {
border: 1px solid $color-input;
border-radius: $input-radius;
display: none;
margin: 0.5rem 0 0;
overflow: hidden;
padding: 0;
width: 100%;
}
&__group {
color: $color-text-link;
text-decoration: underline;
}
&__results-title {
background: $color-grey-15;
border-bottom: 1px solid $color-input;
padding: 0.25rem 0.5rem;
}
&__listbox {
background: $color-white;
list-style: none;
margin: 0;
padding: 0;
&:focus {
outline: none;
}
}
&__option {
cursor: pointer;
margin: 0;
outline: none;
padding: $input-padding-horizontal;
&:not(:last-child) {
border-bottom: 1px solid $color-input;
}
&:not(&--no-results):not(&--more-results):hover,
&--focused:not(&--no-results) {
background: $color-text-link-hover;
border-color: $color-text-link-hover;
color: $color-white;
.ons-autosuggest-input__group,
.ons-autosuggest-input__category {
color: $color-white;
}
}
&:active:not(&--no-results):not(&--more-results) {
background: $color-focus;
color: $color-text-link-focus;
.ons-autosuggest-input__group,
.ons-autosuggest-input__category {
color: $color-text-link-focus;
}
}
&--no-results,
&--more-results {
background: $color-grey-15;
cursor: not-allowed;
padding: 0.25rem 0.5rem;
}
}
&__warning {
background: #f0f0f0;
margin: 0;
padding-left: 0.5rem;
&:not(:last-child) {
border-bottom: 1px solid $color-input;
}
}
&__panel.ons-panel--warn {
background: none;
border: 0;
margin: 0;
.ons-panel__icon {
font-size: 21px;
line-height: 25px;
margin-top: 0;
min-height: 24px;
min-width: 26px;
top: 17px;
}
.ons-panel__body {
font-weight: bold;
padding: 0.8rem 0.8rem 0.8rem 2rem;
}
}
// Modifiers
&:not(&--initialised) & {
&__instructions,
&__listbox,
&__status {
display: none;
}
}
&--has-results & {
&__results {
display: block;
}
}
&--header {
.ons-autosuggest-input__results {
border: none;
box-shadow: 0 0 5px 0 rgba($color-black, 0.6);
left: 0;
position: absolute;
z-index: 1;
}
}
}
Help improve this component
Let us know how we could improve this component or share your user research findings. Discuss the ‘Autosuggest’ component on GitHub