Skip to content

Instantly share code, notes, and snippets.

@chipbell4
Created February 27, 2021 17:47
Show Gist options
  • Save chipbell4/56fe1803c9208bbe68ecbcafec05c6c6 to your computer and use it in GitHub Desktop.
Save chipbell4/56fe1803c9208bbe68ecbcafec05c6c6 to your computer and use it in GitHub Desktop.
A custom element for rendering a checkbox list
/**
* A custom element for rendering a checkbox list of items. Example:
* <checkbox-list
* title="Hello World"
* itemLabels="Apple,Banana, Chocolate"
* items="a,b,c"
* value="all"
* />
*
* And change can be detected with:
* const element = document.querySelector('checkbox-list');
* element.addEventListener('change', (evt) => {
* console.log(evt.target.value);
* });
*/
class CheckboxList extends HTMLElement {
constructor() {
super();
this.container = document.createElement("div");
}
static get observedAttributes() {
return ["itemLabels", "items", "value"];
}
get title() {
return this.getAttribute("title");
}
set title(value) {
this.setAttribute("title", value);
}
get itemLabels() {
return this.getAttribute("itemLabels")
.split(",")
.map((s) => s.trim());
}
set itemLabels(value) {
this.setAttribute("itemLabels", value);
}
get items() {
return this.getAttribute("items")
.split(",")
.map((s) => s.trim());
}
set items(value) {
this.setAttribute("items", value);
}
get value() {
if (this.getAttribute("value") === "all") {
return this.items;
}
if (this.getAttribute("value").trim() === "") {
return [];
}
return this.getAttribute("value")
.split(",")
.map((s) => s.trim());
}
set value(value) {
this.setAttribute("value", value.join(","));
const event = new CustomEvent("change", this.value);
this.dispatchEvent(event);
}
get everythingSelected() {
return this.items.length === this.value.length;
}
onChangeAllCheckbox(evt) {
this.value = evt.target.checked ? this.items : [];
}
onChangeCheckbox(evt) {
const oldValues = this.value;
if (evt.target.checked) {
oldValues.push(evt.target.value);
// TODO: remove duplicates
} else {
const index = oldValues.indexOf(evt.target.value);
if (index > -1) {
oldValues.splice(index, 1);
}
}
this.value = oldValues;
}
renderListItem(labelText, value, checked) {
const li = document.createElement("li");
const checkedAttribute = checked ? "checked=checked" : "";
const checkbox = document.createElement("input");
checkbox.id = value;
checkbox.type = "checkbox";
checkbox.value = value;
if (checked) {
checkbox.checked = true;
}
if (value === "all") {
checkbox.addEventListener("change", (evt) =>
this.onChangeAllCheckbox(evt)
);
} else {
checkbox.addEventListener("change", (evt) => this.onChangeCheckbox(evt));
}
const label = document.createElement("label");
label.for = value;
label.appendChild(checkbox);
const textNode = document.createTextNode(labelText);
label.appendChild(textNode);
li.appendChild(label);
return li;
}
renderList() {
const list = document.createElement("ul");
const labels = ["Select All"].concat(this.itemLabels);
const values = ["all"].concat(this.items);
const checkedItems = this.value;
for (let i = 0; i < labels.length; i++) {
let isChecked = checkedItems.indexOf(values[i]) > -1;
if (values[i] === "all" && this.everythingSelected) {
isChecked = true;
}
list.appendChild(this.renderListItem(labels[i], values[i], isChecked));
}
return list;
}
rerender() {
this.container.innerHTML = "";
const title = document.createElement("span");
title.innerText = this.title;
this.container.appendChild(title);
const list = this.renderList();
this.container.appendChild(list);
}
connectedCallback() {
const shadow = this.attachShadow({ mode: "open" });
shadow.appendChild(this.container);
this.rerender();
}
attributeChangedCallback(name, oldValue, newValue) {
this.rerender();
}
}
customElements.define("checkbox-list", CheckboxList);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment