A modern, accessible select replacement — no dependencies required.
maxSelectsummarizeSelected'auto' (the default) to collapse only when the tags would wrap, 'off' to always show every tag, or a number to collapse once the count exceeds it. The label template is configurable via summarizeSelectedText. This demo uses a threshold of 1 with a custom label — pick one item to see the tag, two or more to see "{n} Channels Applied".<select> is updated programmatically. Dispatch kselect:sync to force the widget to reflect changes.change and custom kselect:change, kselect:open, kselect:close events.change event — alert testaddEventListener('change', …) on the underlying <select> — no kselect API involved. Select an item to see if the native event fires.nativeOnMobile: true to fall back to the OS picker on phones and tablets. Open this page on a phone to see the difference — on desktop you still get the full custom widget.const ks = Kselect.init('#native-mobile-demo', { nativeOnMobile: true, })[0]; // All API methods still work on either device ks.getValue(); ks.setValue('cherry');
nativeOnMobile: true works for multi-selects too. On phones, users get the OS-native multi-select UI (a stacked list on iOS); on desktop, the full custom widget with checkboxes and tag pills.const ks = Kselect.init('#native-mobile-multi-demo', { nativeOnMobile: true, })[0]; // getValue() returns an array for multi selects, in either mode ks.getValue(); // → ['cheese', 'olives'] ks.setValue(['cheese', 'mushrooms']);
allowHtml: trueallowHtml enabled (the default), HTML markup inside <option> tags is rendered live — bold text, colours, flags, icons, whatever you put in.allowHtml: falseallowHtml: false, HTML tags are shown as literal text. Useful when option content comes from user input or untrusted sources.