Vanilla JS
@selkit/dom renders the controller with plain DOM — no framework required.
Mounting
createSelkitDom(target, config) accepts an element or a selector string and returns an instance:
import { createSelkitDom } from '@selkit/dom'
const instance = createSelkitDom('#fruit', {
options: [
{ value: 'apple', label: 'Apple' },
{ value: 'banana', label: 'Banana' },
],
placeholder: 'Pick a fruit…',
clearable: true,
})The returned instance is:
interface SelkitDomInstance {
controller: SelkitController // the headless brain
element: HTMLElement // the rendered root
destroy(): void // unbind and remove
}Use instance.controller for everything behavioral — listening to events, selecting programmatically, and so on:
instance.controller.on('change', ({ value }) => console.log(value))
instance.controller.select('banana')Short aliases
For a select2 / Tom Select feel, two aliases are provided:
import { sk, Selkit } from '@selkit/dom'
sk('#fruit', { options }) // function alias for createSelkitDom
const s = new Selkit('#fruit', { options }) // class-style aliasEnhancing a native <select>
If the mount target is a <select>, Selkit reads its <option>s, value, multiple and name, hides the native element and keeps it in sync. This is progressive enhancement — the form still submits through the original control, and destroy() restores it.
<select id="fruit" name="fruit">
<option value="apple">Apple</option>
<option value="banana" selected>Banana</option>
</select>createSelkitDom('#fruit') // options come from the <select><optgroup> elements become Selkit groups automatically.
DOM-only config
SelkitDomConfig extends SelkitConfig with a couple of render-time options:
| Option | Type | Description |
|---|---|---|
classPrefix | string | BEM class prefix, defaults to "selkit". |
name | string | Maintain hidden inputs for plain form submission. See Forms. |
virtualScroll | boolean | Render only the visible slice. See Virtual Scroll. |
itemHeight | number | Fixed row height for virtual scroll, defaults to 36. |
dropdownParent | HTMLElement | string | Portal the dropdown into another element (e.g. document.body) to escape clipping ancestors like overflow:hidden containers or modals. |
templateOption | (option, meta) => string | Node | Customize a dropdown option's content. meta is { index, active, selected }. |
templateSelection | (option, meta) => string | Node | Customize the selected tag / single-value content. meta is { index, multiple }. |
For both, a returned string is set as textContent (safe — no HTML injection); return a Node to include markup such as an icon:
createSelkitDom(el, {
options,
multiple: true,
// dropdown option
templateOption: (option, { selected }) => {
const span = document.createElement('span')
span.textContent = `${selected ? '✓ ' : ''}${option.label}`
return span
},
// selected tag / single value
templateSelection: (option) => {
const span = document.createElement('span')
span.textContent = `🔖 ${option.label}`
return span
},
})Cleanup
Always call destroy() when removing the component to unbind listeners and, in enhancement mode, restore the native <select>:
instance.destroy()