Everything you wish the HTML select element could do, wrapped up into a lightweight, extensible Vue component.
—
Complete keyboard navigation system with customizable key mappings, type-ahead functionality, and accessibility compliance.
Properties that control keyboard behavior and key mappings.
/**
* Array of key codes that will select the current option
* Default is Enter key (13)
*/
selectOnKeyCodes: Array // default: [13]
/**
* Set the tabindex for the input field
*/
tabindex: Number // default: null
/**
* Used to modify the default keydown events map for the search input
* Allows customization of keyboard shortcuts
* @param map - Default keydown map
* @param vm - Vue Select instance
* @returns Modified keydown map
*/
mapKeydown: Function // default: (map, vm) => map
/**
* DEPRECATED: When true, hitting tab key will select current value
* Use selectOnKeyCodes instead
*/
selectOnTab: Boolean // default: false
/**
* DEPRECATED: Select current value if selectOnTab is enabled
* Use selectOnKeyCodes instead
*/
onTab: Function
/**
* Enable automatic scrolling to keep the highlighted option in view
* When navigating with keyboard, the dropdown will scroll automatically
*/
autoscroll: Boolean // default: trueProperties and methods for keyboard navigation within the dropdown options.
// Data properties
data: {
/**
* Current position of keyboard navigation pointer in dropdown
* -1 means no option is highlighted
*/
typeAheadPointer: Number // default: -1
}
// Methods from typeAheadPointer mixin
methods: {
/**
* Move the typeAheadPointer visually up the list by
* setting it to the previous selectable option
*/
typeAheadUp(): void,
/**
* Move the typeAheadPointer visually down the list by
* setting it to the next selectable option
*/
typeAheadDown(): void,
/**
* Select the option at the current typeAheadPointer position
* Optionally clear the search input on selection
*/
typeAheadSelect(): void,
/**
* Move the pointer to the last selected option
* Useful for maintaining context in multiple selection
*/
typeAheadToLastSelected(): void
}Methods that handle various keyboard interactions.
/**
* Search input keyboard event handler
* Handles navigation keys, selection keys, and special keys
* @param e - Keyboard event object
* @returns Keydown handler function
*/
onSearchKeyDown(e: KeyboardEvent): Function
/**
* Search input keypress handler
* @param e - Keyboard event object
*/
onSearchKeyPress(e: KeyboardEvent): void
/**
* Handle escape key press
* Removes search text or closes dropdown
*/
onEscape(): void
/**
* Delete value on Delete keypress when no text in search input
* Useful for removing selected options with keyboard
* @returns Deleted value or undefined
*/
maybeDeleteValue(): anyAutomatic scrolling functionality to keep the keyboard-highlighted option visible.
/**
* Enable/disable auto-scrolling to keep pointer visible
*/
autoscroll: Boolean // default: true
/**
* Adjust the scroll position of the dropdown list
* if the current pointer is outside of the overflow bounds
* @returns Scroll adjustment result
*/
maybeAdjustScroll(): any
/**
* Get the currently viewable portion of the dropdown menu
* @returns Viewport bounds object
*/
getDropdownViewport(): Object<template>
<v-select
v-model="selected"
:options="options"
placeholder="Use arrow keys to navigate, Enter to select..."
/>
</template>
<script>
export default {
data() {
return {
selected: null,
options: ['Option 1', 'Option 2', 'Option 3', 'Option 4', 'Option 5']
};
}
};
</script><template>
<v-select
v-model="selected"
:options="options"
:selectOnKeyCodes="[13, 32]"
placeholder="Enter or Space to select..."
/>
</template>
<script>
export default {
data() {
return {
selected: null,
options: ['Option 1', 'Option 2', 'Option 3']
};
}
};
</script><template>
<v-select
v-model="selected"
:options="filteredOptions"
:mapKeydown="customKeydown"
placeholder="Custom keyboard shortcuts enabled..."
/>
</template>
<script>
export default {
data() {
return {
selected: null,
options: ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry']
};
},
computed: {
filteredOptions() {
return this.options;
}
},
methods: {
customKeydown(map, vm) {
return {
...map,
// Ctrl+A to select all (for multiple select)
'ctrl+65': (e) => {
e.preventDefault();
if (vm.multiple) {
vm.selectedValue = [...vm.options];
}
},
// Ctrl+D to clear selection
'ctrl+68': (e) => {
e.preventDefault();
vm.clearSelection();
},
// F1 for help
112: (e) => {
e.preventDefault();
alert('Keyboard shortcuts:\n' +
'Arrow keys: Navigate\n' +
'Enter/Space: Select\n' +
'Escape: Close\n' +
'Ctrl+A: Select all\n' +
'Ctrl+D: Clear selection');
}
};
}
}
};
</script><template>
<div>
<input tabindex="1" placeholder="First input" />
<v-select
v-model="selected"
:options="options"
:tabindex="2"
placeholder="Select with tabindex 2..."
/>
<input tabindex="3" placeholder="Third input" />
</div>
</template>
<script>
export default {
data() {
return {
selected: null,
options: ['Option 1', 'Option 2', 'Option 3']
};
}
};
</script><template>
<v-select
v-model="selectedItems"
:options="options"
multiple
placeholder="Use Backspace to remove last item..."
@keydown.delete="handleDelete"
/>
</template>
<script>
export default {
data() {
return {
selectedItems: [],
options: [
'JavaScript', 'Python', 'Java', 'C++', 'Go',
'React', 'Vue.js', 'Angular', 'Node.js'
]
};
},
methods: {
handleDelete(event) {
// Custom delete handling
if (this.selectedItems.length > 0 && !this.$refs.vSelect.search) {
console.log('Removing last selected item via keyboard');
}
}
}
};
</script><template>
<v-select
v-model="selected"
:options="manyOptions"
:autoscroll="false"
placeholder="Navigate without auto-scrolling..."
/>
</template>
<script>
export default {
data() {
return {
selected: null,
manyOptions: Array.from({ length: 50 }, (_, i) => `Option ${i + 1}`)
};
}
};
</script><template>
<v-select
v-model="selected"
:options="options"
@search:focus="onFocus"
@search:blur="onBlur"
placeholder="Keyboard event monitoring..."
ref="vSelect"
/>
</template>
<script>
export default {
data() {
return {
selected: null,
options: ['Option 1', 'Option 2', 'Option 3']
};
},
methods: {
onFocus() {
console.log('Search input focused - keyboard navigation active');
},
onBlur() {
console.log('Search input blurred - keyboard navigation inactive');
}
},
mounted() {
// Monitor typeAheadPointer changes
this.$watch('$refs.vSelect.typeAheadPointer', (newPointer) => {
if (newPointer >= 0) {
const option = this.$refs.vSelect.filteredOptions[newPointer];
console.log('Keyboard pointer moved to:', option);
}
});
}
};
</script><template>
<div>
<v-select
v-model="selected"
:options="options"
ref="vSelect"
placeholder="Use buttons to control keyboard navigation..."
/>
<div class="controls">
<button @click="moveUp">↑ Up</button>
<button @click="moveDown">↓ Down</button>
<button @click="selectCurrent">✓ Select</button>
<button @click="focusSearch">🔍 Focus</button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
selected: null,
options: ['Option 1', 'Option 2', 'Option 3', 'Option 4', 'Option 5']
};
},
methods: {
moveUp() {
if (this.$refs.vSelect.open) {
this.$refs.vSelect.typeAheadUp();
}
},
moveDown() {
if (this.$refs.vSelect.open) {
this.$refs.vSelect.typeAheadDown();
}
},
selectCurrent() {
if (this.$refs.vSelect.open) {
this.$refs.vSelect.typeAheadSelect();
}
},
focusSearch() {
this.$refs.vSelect.searchEl.focus();
}
}
};
</script>
<style scoped>
.controls {
margin-top: 10px;
}
.controls button {
margin-right: 10px;
padding: 5px 10px;
}
</style><template>
<v-select
v-model="selected"
:options="options"
:inputId="'accessible-select'"
aria-label="Choose your preferred programming language"
placeholder="Select programming language..."
/>
</template>
<script>
export default {
data() {
return {
selected: null,
options: [
'JavaScript', 'Python', 'Java', 'C#', 'Go',
'Rust', 'TypeScript', 'Kotlin', 'Swift'
]
};
}
};
</script>Vue Select includes these default keyboard shortcuts:
Install with Tessl CLI
npx tessl i tessl/npm-vue-select