Event System
Vanilla Smart Select emits various events that allow you to react to user interactions and state changes.
vs: to avoid conflicts with other plugins.
Basic Events
vs:select
Fired when an item is selected.
select.on('vs:select', function(e) {
console.log('Item selected:', e.detail);
// e.detail contains: { id, text, element, ... }
});
vs:unselect
Fired when an item is deselected (multi-select only).
select.on('vs:unselect', function(e) {
console.log('Item removed:', e.detail);
});
vs:change
Fired when the select value changes.
select.on('vs:change', function(e) {
console.log('Value changed to:', this.val());
console.log('Complete data:', this.getSelected());
});
vs:clear
Fired when selection is cleared (via X button or clear()).
select.on('vs:clear', function(e) {
console.log('Selection cleared');
});
Dropdown Events
vs:open
Fired when the dropdown is opened.
select.on('vs:open', function(e) {
console.log('Dropdown opened');
});
vs:close
Fired when the dropdown is closed.
select.on('vs:close', function(e) {
console.log('Dropdown closed');
});
Search Events
vs:query
Fired when the user types in the search field.
select.on('vs:query', function(e) {
console.log('Search term:', e.detail.query);
});
vs:results
Fired when search results are displayed.
select.on('vs:results', function(e) {
console.log('Results found:', e.detail.results.length);
});
Lifecycle Events
vs:init
Fired when the plugin is initialized.
select.on('vs:init', function(e) {
console.log('Plugin initialized');
});
vs:destroy
Fired when the plugin is destroyed.
select.on('vs:destroy', function(e) {
console.log('Plugin destroyed');
});
AJAX Events
vs:ajaxLoading
Fired when an AJAX request is started.
select.on('vs:ajaxLoading', function(e) {
console.log('Loading data...');
});
vs:ajaxSuccess
Fired when an AJAX request is successful.
select.on('vs:ajaxSuccess', function(e) {
console.log('Data loaded:', e.detail.data);
});
vs:ajaxError
Fired when an AJAX request fails.
select.on('vs:ajaxError', function(e) {
console.error('Error loading data:', e.detail.error);
});
vs:dataLoaded
Fired when data is loaded (AJAX or local).
select.on('vs:dataLoaded', function(e) {
console.log('Data available:', e.detail.data.length);
});
Special Events
vs:selectionLimitReached
Fired when the maximum selection limit is reached.
select.on('vs:selectionLimitReached', function(e) {
console.log('Limit reached:', e.detail.maximum);
alert('You can only select ' + e.detail.maximum + ' items');
});
Preventive Events
Some events can be canceled by calling e.preventDefault():
vs:selecting
Fired before an item is selected. Can be canceled.
select.on('vs:selecting', function(e) {
// Prevent selection of items with odd ID
if (parseInt(e.detail.id) % 2 !== 0) {
e.preventDefault();
alert('Only even IDs can be selected');
}
});
vs:unselecting
Fired before an item is deselected. Can be canceled.
select.on('vs:unselecting', function(e) {
if (!confirm('Are you sure you want to remove this item?')) {
e.preventDefault();
}
});
vs:clearing
Fired before selection is cleared. Can be canceled.
select.on('vs:clearing', function(e) {
if (!confirm('Clear entire selection?')) {
e.preventDefault();
}
});
vs:opening
Fired before the dropdown opens. Can be canceled.
select.on('vs:opening', function(e) {
// Load data before opening
if (!dataLoaded) {
e.preventDefault();
loadData().then(() => select.open());
}
});
vs:closing
Fired before the dropdown closes. Can be canceled.
select.on('vs:closing', function(e) {
// Keep open if search is still active
if (isSearching) {
e.preventDefault();
}
});
Listening to Events
Add Listener
function myHandler(e) {
console.log('Event fired:', e.type);
console.log('Data:', e.detail);
}
select.on('vs:select', myHandler);
Remove Listener
select.off('vs:select', myHandler);
Multiple Events
// Listen to multiple events
select
.on('vs:select', handleSelect)
.on('vs:unselect', handleUnselect)
.on('vs:change', handleChange);
Event Delegation (Native DOM)
You can also use native DOM events:
document.getElementById('my-select').addEventListener('vs:select', function(e) {
console.log('Item selected via DOM:', e.detail);
});
Event Data Structure
The e.detail property contains specific information for each event:
vs:select / vs:unselect
{
id: "1",
text: "Option 1",
element: HTMLOptionElement,
disabled: false,
selected: true,
// ... other custom fields
}
vs:query
{
query: "search term",
term: "search term" // alias
}
vs:results
{
results: [
{ id: "1", text: "Option 1" },
{ id: "2", text: "Option 2" }
],
query: "term"
}
vs:ajaxSuccess
{
data: { ... }, // API response
status: 200,
xhr: XMLHttpRequest
}
vs:selectionLimitReached
{
maximum: 5,
current: 5
}
Complete Example
const select = new VanillaSmartSelect('#my-select', {
multiple: true,
maximumSelectionLength: 3
});
// Basic events
select.on('vs:select', function(e) {
console.log('β
Selected:', e.detail.text);
updateUI();
});
select.on('vs:unselect', function(e) {
console.log('β Removed:', e.detail.text);
updateUI();
});
select.on('vs:change', function() {
const values = this.val();
console.log('π Current values:', values);
saveToLocalStorage(values);
});
// Dropdown events
select.on('vs:open', function() {
console.log('π Dropdown opened');
trackEvent('dropdown_opened');
});
select.on('vs:close', function() {
console.log('π Dropdown closed');
});
// Search
select.on('vs:query', function(e) {
console.log('π Searching:', e.detail.query);
trackSearchQuery(e.detail.query);
});
// Selection limit
select.on('vs:selectionLimitReached', function(e) {
alert(`You can only select ${e.detail.maximum} items!`);
});
// Preventive events
select.on('vs:selecting', function(e) {
// Validate before selecting
if (!isValidChoice(e.detail.id)) {
e.preventDefault();
showError('This option is not available for you');
}
});
// Cleanup
select.on('vs:destroy', function() {
console.log('ποΈ Plugin destroyed');
cleanup();
});