I am looking to create a custom HTML element that mimics the behavior of the native <select>
element but also triggers a specific update function whenever an attribute or child node is modified. This is essential for incorporating the bootstrap-select element within the Elm framework. For more context, you can refer to bootstrap-select and my previous inquiry on Stack Overflow.
Using the LitElement framework, I managed to construct a functional custom element called <lit-select>
with similar functionality as described above. However, I encountered a limitation where it does not support nested html <option>
or <optgroup>
elements as children. Instead, users need to provide the option list as a JSON encoded string within a specific attribute.
Essentially, rather than using
<lit-select>
<option>foo</option>
<option>bar</option>
</lit-select>
users have to utilize
<lit-select items='["foo", "bar"]'></lit-select>
How should I modify the definition of <lit-select>
to enable the former approach? While I am familiar with the <slot>
element, it cannot be used within <select>
tags as it gets stripped by the browser.
Thank you in advance!
Update 1
There are additional constraints complicating the issue:
- Avoiding shadow DOM is necessary as my custom element needs to be styled/enhanced by bootstrap (including bootstrap-select) CSS/JS, which only interacts with the regular DOM. This means
slot
elements are not viable due to their association with shadow DOM. - The custom element must be fully responsive to changes such as adding/removing child nodes and modifying attributes. This adaptability is crucial for integrating the element into virtual DOM environments like Elm and React.
Appendix
Here is the current definition of <lit-select>
:
import { LitElement, html, customElement, property } from 'lit-element';
import * as $ from 'jquery';
import 'bootstrap';
import 'bootstrap-select';
@customElement('lit-select')
export class LitSelect extends LitElement {
@property({ type : Array }) items = []
updated() {
$(this).find(".selectpicker").selectpicker('refresh');
}
createRenderRoot() {
return this;
}
private renderItem(item: string) {
return html`
<option>
${item}
</option>
`;
}
render() {
return html`
<select class="selectpicker" data-live-search = "true">
${this.items.map(item => this.renderItem(item))}
</select>
`;
}
}