Installation and usage
You can grab the module from NPM:
npm install enhanced-checkbox-list
And then import it to be used with your module system:
import EnhancedCheckboxList from 'enhanced-checkbox-list';
new EnhancedCheckboxList(document.getElementById('list'), options);
Or you can grab the code straight from unpkg:
<script src="https://unpkg.com/enhanced-checkbox-list"></script>
<script>
const list = document.getElementById('list');
new EnhancedCheckboxList(list, options);
</script>
The EnhancedCheckboxList function takes 2 arguments: the list element /
wrapper, and an optional options object.
Examples
1. Default options: filtering, toggling, select all, and shortcuts
<fieldset>
<legend>Fruits</legend>
<ul id="list">
<!-- checkbox list items here -->
</ul>
</fieldset>
const list = document.getElementById('list');
new EnhancedCheckboxList(list);
When keyboard focus is on a checkbox in the list, you can use the home, end, pageup, and pagedown keys to jump to the first, last, +10th, or -10th checkbox respectively.
The modifiers used for the pageup and pagedown keys are of course customisable using
the pageUpModifier and pageDownModifier options respectively.
2. Enhanced list with toggling disabled
<fieldset>
<legend>Fruits</legend>
<div id="list">
<!-- checkboxes and labels here -->
</div>
</fieldset>
const list = document.getElementById('list');
new EnhancedCheckboxList(list, {
togglable: false,
itemSelector: ''
});
The filter input's value can also be cleared using the Esc key.
This example has all of the checkboxes and labels directly inside of a
<div>, instead of in list items. So we're setting the
itemSelector option to an empty string to prevent any unnecessary DOM
interrogation. This means that, after filtering, only the checkboxes themselves will be
shown or hidden based on the filter value, so we need to use CSS to hide the sibling
label.
3. Togglable and open by default, but not filterable
<ul id="list" aria-label="Fruits">
<!-- checkbox list items here -->
</ul>
const list = document.getElementById('list');
new EnhancedCheckboxList(list, {
filterable: false,
visible: true
});
If togglable, you can also use the Esc key anywhere inside the wrapper
(other than the filter input) to close the list.
This example is also using an aria-label attribute on the list which is
being used for the toggle button text (instead of being inside a
<fieldset> with a <legend>).
4. Auto-closing panel, with custom button text
This example only shows the number of checked items in the button if there are some.
const list = document.getElementById('list');
new EnhancedCheckboxList(list, {
togglable: true,
autoClose: true,
toggleButtonText: (listLabel, checkedCount) => {
const labelLower = listLabel.toLowerCase();
if (checkedCount === 0) {
return `Select ${labelLower}`;
} else if (checkedCount > 0) {
return `Chosen ${labelLower} (${checkedCount})`;
}
}
});
5. All core functionality set to false
(because that's supported for some reason...)
This example basically just waps the checkbox list in a container... I'm not sure why you'd use it this way, but you can if you want to.
const list = document.getElementById('list');
new EnhancedCheckboxList(list, {
keyboardShortcuts: false,
selectAllControl: false,
filterable: false,
togglable: false
});
HTML requirements
For proper, accessible checkbox lists, they should be in a
<fieldset> with a <legend> to give the list
context.
If your list isn't in a <fieldset> though, the module will group and
label the list for screen reader users, as long as a label can be found. When looking
for a label for the list, the module attempts the following steps:
- Checking if the
listLabeloption has a value - Check for an
aria-labelattribute on the list element -
Checking for an
aria-labelledbyattribute on the list element, and looking for an element with thatid - Checking if another element labels the list using a
forattribute -
Travelling up the DOM tree looking for a
<fieldset>element; if one is found, its<legend>text will be used
For example:
<ul aria-label="Fruits">
<!-- list items in here -->
</ul>
<span id="fruits-label" hidden>Fruits</span>
<ul id="fruits" aria-labelledby="fruits-label">
<!-- list items in here -->
</ul>
<span for="fruits" hidden>Fruits</span>
<ul id="fruits">
<!-- list items in here -->
</ul>
<fieldset>
<legend>Fruits</legend>
<ul id="fruits">
<!-- checkbox list items here -->
</ul>
</fieldset>
As well as giving the list context, this label is used for the toggle button text, and as part of a hidden label for the filter input for screen reader users.
Options
The full list of available options is as follows:
Filtering
- filterable:
boolean -
Generate an input to allow filtering the list
Defaulttrue - filterDelay:
number -
Delay after typing in the filter input before filtering (to allow for fast typers)
Default:300 - itemSelector:
string -
Selector to use when doing a
.closest()DOM query to determine which element to show/hide after filtering
Default:`li`
Toggling
- togglable:
boolean -
Allow toggling visibility by generate a toggle button
Defaulttrue - toggleButtonText:
string | ((listLabel: string, checkedCount: number) => string) -
Text to use in the toggle button; can accept a string, or function.
Default(listLabel, checkedCount) => `${listLabel} (${checkedCount})` - autoClose:
boolean -
If togglable, close when clicking or blurring outside of the container / button
Default:false - visible:
boolean -
If togglable, determines initial visibility
Default:false
Select all
- selectAllControl:
boolean -
Include a custom select all control;
this has to be a custom element due to issues with updating the indeterminate state on native checkboxes in many browsers
Default:true - selectAllText:
string -
Text for the select all control label
Default:`Select all`
Keyboard shortcuts
- keyboardShortcuts:
boolean -
If keyboard focus is on a checkbox, allow: home, end, pageup, and pagedown
shortcuts to move to first, last, -10, or +10 checkbox respectively;
and escape key to close the wrapper if togglable
Default:true - pageUpModifier:
number -
The index modifier for the pageup shortcut;
e.g. if on the 15th checkbox, use pageup to go to the 5th
Default:-10 - pageDownModifier:
number -
The index modifier for the pagedown shortcut;
e.g. if on the 5th checkbox, use pagedown to go to the 15th
Default:10
Rendering options
- checkboxSelector:
string -
Selector to use when finding the checkboxes in the list
Default:`input[type="checkbox"], input[type="radio"]` - tabindex:
number | string -
Control the tabindex added to the toggle button and select all control in case your
checkbox list sits at a certain point in the page's tabbing order
Default:`0` - cssNameSpace:
string -
String to prepend to classes for BEM naming
e.g. "enhanced-checkbox-list__wrapper"
Default:`enhanced-checkbox-list` - listLabel:
string -
Label for the checkbox list - used for the toggle button text and the label for the
filter input;
if not provided, the module will search for one usingaria-labeloraria-labelledbyattributes, or looking for a parent<fieldset>'s<legend> - filterClassName:
string - Custom class name to add to the filter text input
- toggleClassName:
string - Custom class name to add to the toggle button
- wrapperClassName:
string - Custom class name to add to the component wrapper
Screen reader enhancements
- srEscapeToCloseText:
string -
Screen reader text added to list wrapper advising shortcut to close the wrapper (if
toggling enabled)
Default:`Esc to close` - srEscapeToClearText:
string -
Screen reader text added to filter field advising shortcut to clear filter value
(if filtering enabled)
Default:`Esc to clear` - srFilterText:
string -
Screen reader text used for label explaining the search/filter input;
combines withlistLabelif available
e.g. "filter departments"
Default:`filter` - srFoundText:
string -
Partial screen reader text used in message after filtering e.g. "12 found, 3
selected"
Default:`found` - srSelectedText:
string -
Partial screen reader text used in message after filtering e.g. "12 found, 3
selected"
Default:`selected`
Callbacks
- onFilter:
Function - Callback after a filter has run
- onReady:
Function - Callback once ready
- onShow:
Function - Callback when the list is toggled visible
- onHide:
Function - Callback when the list is toggled hidden
All component options that accept a function will also have their context
(this) set to include the full API (assuming you use an ordinary function
expression instead of arrow functions).
API
The returned
EnhancedCheckboxList instance exposes the following API ((which is also
available on the original element's enhancedCheckboxList property):
- list:
HTMLElement - The original list element that the function was called on
- options:
EnhancedCheckboxListOptions - The instance options that were / are being used
- checkboxes:
HTMLInputElement[] - The checkboxes that the instance currently recognises and will affect
- listWrapper:
HTMLDivElement - The wrapping
<div>generated by the module - filterInput:
HTMLInputElement - The filtering input generated by the module
- selectAllCheckbox:
HTMLSpanElement - The generated custom select all element
- show:
(): void - Toggle the checkbox list wrapper visible
- hide:
(): void - Toggle the checkbox list wrapper hidden
- update:
(updateCheckboxStorage: boolean = true): void - Update the toggle button text and select all state
-
filter:
(value: string, updateScreenReaderText: boolean = false): void - Filter the checkbox list by a chosen value
- destroy:
(clearInternalCache: boolean = false): void - Destroy the component