Event System

Vanilla Smart Select emits various events that allow you to react to user interactions and state changes.

πŸ’‘ Event Prefix: All events are prefixed with 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();
});