Odo Dropdown
Custom dropdown component that defaults to native select elements on touch devices.
Support
IE9+ with polyfills.
To support IE<=11, you will need to add classList
and Element#closest
polyfills.
Dependencies
Odo Base Component, Odo Helpers.
Basic Odo Dropdown
On non-touch devices, this component acts similarly to native select elements. All keyboard events that interact with native select elements may be used with the custom markup.
- When component is in closed state:
- Can tab over component to focus
- Can use
SPACE
,ENTER
,UP
, orDOWN
keys to open the custom markup options
- When component is in open state:
- Can use
UP
orDOWN
keys to highlight options - Can use
SPACE
orENTER
keys to select a highlighted option - Can use
ESC
orTAB
keys to close the options, and return to closed state
- Can use
Markup
<div id="demo-1" class="odo-dropdown">
<select class="odo-dropdown__select">
<option value="0">Sunday</option>
<option value="1">Monday</option>
<option value="2">Tuesday</option>
<option value="3">Wednesday</option>
<option value="4">Thursday</option>
<option value="5">Friday</option>
<option value="6">Saturday</option>
</select>
<button type="button" class="odo-dropdown__button">
<div class="odo-dropdown__button-inner">
<span class="odo-dropdown__default">Sunday</span>
<span class="odo-dropdown__value"></span>
<svg class="odo-dropdown__button-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<path d="M8,12c-0.3,0-0.5-0.1-0.7-0.3l-5-5c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0L8,9.6l4.3-4.3c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-5,5 C8.5,11.9,8.3,12,8,12z"/>
</svg>
</div>
</button>
</div>
Setup
var dropdown = new OdoDropdown(document.getElementById('demo-1'));
Options Height
The options list has a static max-height
applied to it. Feel free to change this, or subclass the dropdown and calculate the height based on the available viewport.
Public Event
The selected day value above is:
The CHANGE
event is emitted when a user has selected a value in the dropdown. Use on
to listen for the event. OdoDropdown is an event emitter, so you can also use once
and off
.
dropdown.on(OdoDropdown.EventType.CHANGE, (data) => {
console.log(data.value);
});
Public Properties
dropdown.value // get or set the value of the select.
dropdown.selectedText // get the selected option's text content.
dropdown.select // get the <select> element.
dropdown.button // get the <button> element.
dropdown.selectedIndex // get or set the value of the <select> by index.
dropdown.disabled // get or set the disabled state of the component.
Public Methods
dropdown.toggleOptionByValue(value, isDisabled)
dropdown.disableOptionByValue('bar')
dropdown.enableOptionByValue('today')
dropdown.getCustomOptions()
dropdown.getNativeOptions()
dropdown.dispose()
Options Placement
Additionally, you can modify the position of the drop down to display the custom options where you would like. In this example, the options are placed below the button.
This example also shows how you can use a <label>
with the dropdown. You need the for
attribute! You could also put the <label>
inside the <button>
if you wanted.
CSS
Add a style which moves the options container, then add the class to the main element.
.odo-dropdown--options-below .odo-dropdown__options {
transform: translate(0, 45px);
}
JavaScript
var dropdown = new OdoDropdown(document.getElementById('demo-2'));
Selected Option
It can be initialized with an option already selected via the selected
attribute on an <option>
.
Disabled Option(s)
It can be initialized with an option disabled via the disabled
attribute on an <option>
.
Custom Styling
Instead of importing the css file for the dropdown and overriding what you need, it is recommended to copy the styles to your own stylesheet and modify them there.
Accessibility with WAI-ARIA
This component manages aria-*
attributes to provide screen readers with the appropriate information to control and choose options.
If your odo-dropdown__button
is not a <button>
element, you need to add the role="button"
attribute to it.
Usage with React
Use the insertMarkup
option and set it to false
.
You will still need to use the lifecycle methods like componentDidMount
to initialize the dropdown instance and componentWillUnmount
to dispose of it.
Here's an example render
method to give you an idea from a component called LabelledDropdown
.
render() {
const selectedOption = this.props.options.find(option => option.selected) || {};
return (
<div className="labelled-dropdown">
<label htmlFor={this.props.id}>{this.props.label}</label>
<div className="odo-dropdown" ref={element => this.element = element}>
<div className={OdoDropdown.Classes.OPTIONS_CONTAINER} role="menu" aria-hidden="true">
{this.props.options.map((option) => {
const selected = option.selected ? ' ' + OdoDropdown.Classes.OPTION_SELECTED : '';
const disabled = option.disabled ? ' ' + OdoDropdown.Classes.OPTION_DISABLED : '';
const className = OdoDropdown.Classes.OPTION + selected + disabled;
return <div key={option.value} className={className} data-value={option.value} tabIndex="-1" role="menuitem">{option.label}</div>
})}
</div>
<select className="odo-dropdown__select" name={this.props.name} id={this.props.id} defaultValue={selectedOption.value}>
{this.props.options.map((option) => (
<option key={option.value} value={option.value}>{option.label}</option>
))}
</select>
<button type="button" className="odo-dropdown__button">
<div className="odo-dropdown__button-inner">
<span className="odo-dropdown__default">{selectedOption.label}</span>
<span className="odo-dropdown__value"></span>
<svg className="odo-dropdown__button-icon" viewBox="0 0 12.021 6.717">
<polygon points="6.01 6.717 0 0.707 0.707 0 6.01 5.303 11.313 0 12.021 0.707 6.01 6.717"/>
</svg>
</div>
</button>
</div>
</div>
);
}
The LabelledDropdown
component could then be used like this:
<LabelledDropdown options={[{ selected: false, value: 'first_name', label: 'First Name' }, { selected: true, value: 'last_name', label: 'Last Name' }]}
name="sorter" id="sorter-dropdown" label="Sort" />