npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@h7o/searchable-select

v1.0.0

Published

A framework-agnostic searchable select dropdown component — zero dependencies. Self-contained JS + CSS that works anywhere.

Downloads

54

Readme

searchable-select

A framework-agnostic searchable select dropdown — zero dependencies. No Bootstrap, no React, no jQuery. Self-contained JS + CSS that works anywhere.

Vanilla JS Zero Dependencies License

Part of the SearchableSelect family:

Features

  • Search & Filter — Case-insensitive substring matching across all option labels
  • Custom Values — Optionally allow users to type values not in the list
  • Clearable — One-click clear button (can be disabled)
  • Keyboard Navigation — Arrow keys, Enter to select, Escape to close, Tab to close
  • Programmatic APIgetValue(), setValue(), addOption(), removeOption(), clearSelection(), and more
  • Data-Attribute Init — Configure entirely in HTML with data-bss-* attributes
  • Custom Eventsbss.change, bss.show, bss.hide, bss.clear on the container element
  • Styling — Inline colour props, CSS class overrides, or CSS custom properties (variables)
  • Validation — Built-in .bss-invalid support for form validation
  • Dark Modedata-bss-theme="dark", bss-dark class, or Bootstrap's data-bs-theme="dark"
  • Theming — Full control via CSS custom properties — colours, borders, fonts, radii, shadows
  • Accessible — ARIA combobox / listbox roles, keyboard support
  • Zero Dependencies — Ships its own complete CSS, works in any environment
  • UMD Module — Works with <script> tags, CommonJS (require), and AMD (define)
  • ES5 Compatible — No transpilation needed, works in older browsers

Installation

CDN (quickest)

<!-- CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/searchable-select@1/dist/searchable-select.min.css">

<!-- JS -->
<script src="https://cdn.jsdelivr.net/npm/searchable-select@1/dist/searchable-select.min.js"></script>

npm

npm install searchable-select
// ES module
import SearchableSelect from 'searchable-select';
import 'searchable-select/dist/searchable-select.css';

// CommonJS
const SearchableSelect = require('searchable-select');

Manual

Copy dist/searchable-select.min.js and dist/searchable-select.min.css into your project and include them in your HTML.

Quick Start

JavaScript

<link rel="stylesheet" href="searchable-select.min.css">
<script src="searchable-select.min.js"></script>

<div id="my-select"></div>

<script>
var select = new SearchableSelect('#my-select', {
    options: [
        { value: 'apple',  label: 'Apple' },
        { value: 'banana', label: 'Banana' },
        { value: 'cherry', label: 'Cherry' }
    ],
    placeholder: 'Select a fruit...',
    onChange: function (value, option) {
        console.log('Selected:', value);
    }
});
</script>

Data Attributes (no JS required)

<div data-bss-toggle="searchable-select"
     data-bss-placeholder="Pick a fruit..."
     data-bss-options='[
         {"value":"apple","label":"Apple"},
         {"value":"banana","label":"Banana"},
         {"value":"cherry","label":"Cherry"}
     ]'>
</div>

Elements with data-bss-toggle="searchable-select" are automatically initialised on DOMContentLoaded. Listen for events via the DOM:

document.getElementById('my-select')
    .addEventListener('bss.change', function (e) {
        console.log('Selected:', e.detail.value);
    });

Configuration

Pass these as the second argument to new SearchableSelect(el, config), or as data-bss-* attributes in HTML.

| Option | Type | Default | Description | |--------|------|---------|-------------| | options | Array<{ value, label }> | [] | Array of selectable options | | value | any | null | Initially selected value | | placeholder | string | 'Select...' | Placeholder text | | maxDropdownHeight | number | 200 | Max dropdown height in pixels | | allowCustomValue | boolean | false | Allow values not in the options list | | disabled | boolean | false | Disable the component | | clearable | boolean | true | Show/hide the clear (×) button | | invalid | boolean | false | Apply .bss-invalid styling | | activeBackgroundColor | string | — | Background colour of the selected item | | activeTextColor | string | — | Text colour of the selected item | | activeClassName | string | — | CSS class for the selected item (overrides inline styles) | | inactiveBackgroundColor | string | — | Background colour for unselected items | | inactiveTextColor | string | — | Text colour for unselected items | | inactiveClassName | string | — | CSS class for unselected items (overrides inline styles) | | onChange | function(value, option) | — | Callback on selection change; receives (null, null) on clear |

Data-attribute mapping

Prefix every option with data-bss- and use kebab-case:

<div data-bss-toggle="searchable-select"
     data-bss-placeholder="Pick…"
     data-bss-allow-custom-value="true"
     data-bss-clearable="false"
     data-bss-max-dropdown-height="300"
     data-bss-options='[{"value":"a","label":"A"}]'>
</div>

Methods

All methods are available on the instance returned by new SearchableSelect(...).

| Method | Returns | Description | |--------|---------|-------------| | getValue() | any | Current value (selected option value or custom text) | | setValue(value) | void | Programmatically select a value | | isCustomValue() | boolean | true if the current value is not from the options list | | getInputText() | string | Raw text currently shown in the input | | clearSelection() | void | Clear the selection | | addOption({ value, label }) | void | Add an option (duplicates ignored) | | removeOption(value) | void | Remove an option; clears selection if it was active | | setOptions(options) | void | Replace the entire options list | | getOptions() | Array | Get a copy of the current options list | | enable() | void | Enable the component | | disable() | void | Disable the component | | setInvalid(bool) | void | Toggle .bss-invalid styling | | dispose() | void | Tear down: unbind events, remove DOM, clean up |

Static Methods

| Method | Description | |--------|-------------| | SearchableSelect.initAll() | Initialise all [data-bss-toggle="searchable-select"] elements | | SearchableSelect.getInstance(el) | Retrieve existing instance from a container element | | SearchableSelect.fromDataAttributes(el) | Create instance from an element's data-bss-* attributes |

Events

Custom DOM events are dispatched on the container element:

| Event | e.detail | Description | |-------|------------|-------------| | bss.change | { value, option } | Selection changed | | bss.show | {} | Dropdown opened | | bss.hide | {} | Dropdown closed | | bss.clear | {} | Selection cleared |

document.querySelector('#my-select')
    .addEventListener('bss.change', function (e) {
        console.log('New value:', e.detail.value);
        console.log('Option object:', e.detail.option); // null if custom
    });

Theming with CSS Custom Properties

Every visual aspect can be customised via CSS variables on .bss-container:

.bss-container {
    /* Input */
    --bss-input-bg: #fff;
    --bss-input-color: #212529;
    --bss-input-border: #ced4da;
    --bss-input-border-radius: 0.375rem;
    --bss-input-focus-border: #86b7fe;
    --bss-input-focus-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
    --bss-input-placeholder-color: #6c757d;

    /* Dropdown */
    --bss-dropdown-bg: #fff;
    --bss-dropdown-border: #dee2e6;
    --bss-dropdown-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
    --bss-dropdown-radius: 0.375rem;

    /* Options */
    --bss-option-active-bg: #0d6efd;
    --bss-option-active-color: #fff;
    --bss-option-hover-bg: #f8f9fa;
    --bss-option-focused-bg: #e9ecef;

    /* Typography */
    --bss-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
    --bss-font-size: 0.9375rem;
}

Dark Mode

Enable dark mode in any of these ways:

<!-- On any ancestor element -->
<html data-bss-theme="dark">

<!-- On the container itself -->
<div id="select" class="bss-dark"></div>

<!-- Also works with Bootstrap's data-bs-theme -->
<html data-bs-theme="dark">

Migrating from Chosen

If you're replacing the jQuery Chosen plugin:

  1. Remove Chosen JS and CSS files
  2. Include searchable-select.min.js and searchable-select.min.css
  3. Replace <select> elements with <div> containers
  4. Initialise with new SearchableSelect(el, { options: [...] })
  5. Replace Chosen events with bss.change, bss.show, bss.hide
// Before (Chosen)
$('#my-select').chosen({ allow_single_deselect: true });
$('#my-select').on('change', function () { ... });

// After (SearchableSelect)
var select = new SearchableSelect('#my-select', {
    options: [...],
    clearable: true,
    onChange: function (value) { ... }
});

Migrating from bs-searchable-select

The API is identical — just change the constructor name:

// Before
var select = new BsSearchableSelect('#el', config);

// After
var select = new SearchableSelect('#el', config);

Both use the same bss- CSS prefix, same data attributes, same events, and same methods.

Examples

Custom Values

var select = new SearchableSelect('#el', {
    options: myOptions,
    allowCustomValue: true,
    placeholder: 'Select or type…'
});

select.getValue();       // the selected/typed value
select.isCustomValue();  // true if typed something not in the list

Inline Colour Styling

new SearchableSelect('#el', {
    options: myOptions,
    activeBackgroundColor: '#198754',
    activeTextColor: '#fff',
    inactiveBackgroundColor: '#f8f9fa',
    inactiveTextColor: '#333'
});

CSS Class Styling

new SearchableSelect('#el', {
    options: myOptions,
    activeClassName: 'my-active',
    inactiveClassName: 'my-inactive'
});
.my-active {
    background-color: #6f42c1 !important;
    color: #fff !important;
    border-left: 3px solid #4a2a8a;
}
.my-inactive:hover {
    background-color: #e9ecef;
}

Form Validation

var select = new SearchableSelect('#el', {
    options: myOptions,
    invalid: true
});

document.querySelector('#el').addEventListener('bss.change', function (e) {
    if (e.detail.value) select.setInvalid(false);
});

Dynamic Options

var select = new SearchableSelect('#el', { options: [] });

fetch('/api/items')
    .then(function (r) { return r.json(); })
    .then(function (data) {
        select.setOptions(data.map(function (d) {
            return { value: d.id, label: d.name };
        }));
    });

Demo

Open index.html in a browser to see all features in action — no build step required, no CDN dependencies.

npx serve .

Building

To generate minified dist files:

npm install
npm run build

This produces:

  • dist/searchable-select.js — unminified copy
  • dist/searchable-select.min.js — minified
  • dist/searchable-select.css — unminified copy
  • dist/searchable-select.min.css — minified

License

MIT © Hussein Al Bayati