A tiny JavaScript library to easily toggle the state of any HTML element and its targets.
Dropdown, navigation button, tooltip, collapsible panel, lightbox, tabs, switch like above…
UI components made in minutes without worried about JavaScript. Only set a few HTML attributes, and code the rest with your CSS skills.
Compressed, for production Download – 12,50ko
Uncompressed, for development View on GitHub
Why?
A front-end developer often has to code scripts for interface components. Components such as dropdowns, navigation buttons, tooltips, expandable panels, lightboxes, tabs, etc.
The thing is… Most of these components expose a recurrent behavior: a trigger element toggles the state of one or more target elements. So why not code this behavior once and for all?
So here is a solution: a simple script to toggle the state of a trigger element with a CSS class. You can then associate this element with one or more others: letās call them targets. By adding the right HTML attributes, it can adapt to any contexts and behave like a chosen component.
Only focus on adjusting the rest with your CSS creativity.
Quick start
Manually
- Direct download
- Choose another version: ES5 or ES6.
Then, add the script into your page, and itās done.
With npm
Install with npm:
npm install easy-toggle-state
Then load Easy Toggle State into your application, inside your main JS file:
import "easy-toggle-state";
// or
require("easy-toggle-state");
and the magic happens!
Compatibility
In addition of using ES5 version, compatibility for Internet Explorer is provided by polyfill.io which supports back to IE 7. It brings polyfills for Array.from
, Array.prototype.filter
, Element.prototype.closest
and more.
And before you ask, yes, you can run it locally.
How to use
Recommendation
Although you could use any element you want, I recommend using only buttons or links as trigger elements. For a link element with a fake href
value, donāt forget to add the role="button"
attribute.
Basics
-
<foo data-toggle-class="class-name">
-
A CSS class to toggle each time a click is triggered on this element. If empty, the
is-active
class is used. -
<foo data-toggle-class="class-name" data-toggle-target="selector">
-
Toggle the class on the trigger element and all the target elements, defined by the selector, in the page. This attribute has
data-toggle-target-all
as alias.
Targeting
You can go further in selecting targets by using one of the following options: it specify a scope where to select targets.
-
<foo data-toggle-class="class-name" data-toggle-target="selector">
-
Basic targeting: Toggle the class on the trigger element and all the target elements, defined by the selector, in the whole page. This attribute has
data-toggle-target-all
as alias. -
<foo data-toggle-class="class-name" data-toggle-target-parent="selector">
-
Toggle the class on the trigger element and all the target elements, defined by the selector, belonging to its parent container.
-
<foo data-toggle-class="class-name" data-toggle-target-self="selector">
-
Toggle the class on the trigger element and all the target elements, defined by the selector, within it.
-
<foo data-toggle-class="class-name" data-toggle-target-previous>
-
Toggle the class on the trigger element and its previous adjacent element.
-
<foo data-toggle-class="class-name" data-toggle-target-next>
-
Toggle the class on the trigger element and its next adjacent element.
Advanced
-
data-toggle-is-active
-
Specify a trigger element and its targets toggled as default, set during the
onload
event. In this case, you should add the specified class name on each element, even if the script will check that for you. -
data-toggle-group="groupName"
-
Specify if a trigger is a part of a group. Only one trigger of a group can be active at a time. It will actually behave like an accordion component.
-
data-toggle-radio-group="groupName"
-
Specify if a trigger is a part of a radio group. Only one trigger of a radio group can be active at a time.
Unlike a simple group as seen above, as soon as a trigger is activated, a radio group will always keep one trigger active. It will actually behave like radio buttons or tabs component.
Note that a radio grouped trigger canāt have
data-toggle-outside
ordata-toggle-escape
behavior. -
data-toggle-event="event"
-
Specify the event that will toggle the class,
mouseover
for example.click
by default if not specified. -
data-toggle-outside
-
Toggle off the class when the event is triggered again outside the trigger element.
-
data-toggle-target-only
-
Toggle the class only on the target elements.
-
data-toggle-escape
-
Toggle the class when the trigger element is active and when the escape key is released.
-
data-toggle-trigger-off
-
Add this attribute to an element inside a target to enable this element to toggle the behavior. For example, a close button in a modal.
Accessibility
If a trigger or a target element has the aria-expanded
, aria-selected
, aria-checked
or aria-hidden
attribute, its value will also change.
Expert
Custom prefix
You can change the prefix toggle
in data-toggle-class
and every attributes to prevent conflict with another JS library. To do so, add this attribute to your <html>
element with your new prefix:
<html data-easy-toggle-state-custom-prefix="my-prefix">
It will so be set to all attributes like data-my-prefix-class
.
Asynchronous needs
For asynchronous needs, you can initiate the magic with:
window.initEasyToggleState();
Hooks
You can also add some hooks: each trigger and target element fires a toggleBefore
event before and a toggleAfter
event after toggling its state, so you can add event listeners on these events, and test the active state with isToggleActive
property. For example:
document.querySelector("#myTrigger").addEventListener("toggleAfter", (event) => {
console.log(`Active: ${event.target.isToggleActive}`);
}, false);
Examples
Here are some demos to show you how this is powerful. Of course, these examples can be made in other ways.
Checkbox
You want to display a checkbox, i.e. a box with a checkmark that appears on click on this box.
Even if you should use the <input type="checkbox">
native component, in some case, you canāt.
Simple checkbox
<button type="button"
class="example-checkbox"
data-toggle-class="is-checked">
</button>
Checkbox with ARIA attributes for accessibility
<button type="button"
class="example-checkbox"
data-toggle-class="is-checked"
role="checkbox"
aria-checked="false"
title="Toggle this box">
<span class="sr-only">Toggle</span>
</button>
Checkbox with external label
<button type="button"
class="example-checkbox"
data-toggle-class="is-checked"
id="checkbox"
role="checkbox"
aria-checked="false"
title="Toggle this box">
<span class="sr-only">Toggle</span>
</button>
<label for="checkbox">
Awesome checkbox
</label>
Checkbox with internal label
<button type="button"
class="example-checkbox-container"
data-toggle-class="is-checked"
data-toggle-target-self=".example-checkbox"
role="checkbox"
aria-checked="false">
<span class="example-checkbox" aria-hidden="true"></span>
Awesome checkbox
</button>
Explanation
Toggle .is-checked
class on click on the box. As aria-checked
attribute is set, its value change too.
The CSS class .sr-only
is for hiding content for accessibility.
Radio buttons
You want to display some radio buttons, i.e. somes boxes with a checkmark that appears on click on these boxes, only one at a time.
Even if you should use the <input type="radio">
native component, in some case, you canāt.
Simple radio buttons
<button type="button"
class="example-radio"
id="radio_1"
data-toggle-class="is-checked"
data-toggle-radio-group="radioGroup">
</button>
<label for="radio_1">Option 1</label><br>
<button type="button"
class="example-radio"
id="radio_2"
data-toggle-class="is-checked"
data-toggle-radio-group="radioGroup">
</button>
<label for="radio_2">Option 2</label><br>
<button type="button"
class="example-radio"
id="radio_3"
data-toggle-class="is-checked"
data-toggle-radio-group="radioGroup">
</button>
<label for="radio_3">Option 3</label>
Radio buttons with ARIA attributes for accessibility
<div role="radiogroup">
<button type="button"
class="example-radio"
id="radio_1"
data-toggle-class="is-checked"
data-toggle-radio-group="radioGroup"
role="radio"
aria-checked="false"
title="Toggle this box">
<span class="sr-only">Toggle</span>
</button>
<label for="radio_1">Option 1</label><br>
<button type="button"
class="example-radio"
id="radio_2"
data-toggle-class="is-checked"
data-toggle-radio-group="radioGroup"
role="radio"
aria-checked="false"
title="Toggle this box">
<span class="sr-only">Toggle</span>
</button>
<label for="radio_2">Option 2</label><br>
<button type="button"
class="example-radio"
id="radio_3"
data-toggle-class="is-checked"
data-toggle-radio-group="radioGroup"
role="radio"
aria-checked="false"
title="Toggle this box">
<span class="sr-only">Toggle</span>
</button>
<label for="radio_3">Option 3</label>
</div>
Explanation
Toggle .is-checked
class on click on this element, but only one of the radioGroup
group at a time, and always keep one active. As aria-checked
attribute is set, its value change too.
The label is associated to the trigger by the for
attribute.
The CSS class .sr-only
is for hiding content for accessibility.
Tooltip
You want to put a tooltip on a text, i.e. show an element when hovering this text, and hide it when the cursor leaves this text.
Simple tooltip
<span class="example-tooltip"
data-toggle-class="is-visible"
data-toggle-target-self=".tooltip"
data-toggle-target-only
data-toggle-event="mouseover"
data-toggle-outside>
Hover me
<span class="tooltip" aria-hidden="true">Awesome tooltip</span>
</span>
Explanation
Toggle .is-visible
class only on the target .tooltip
located inside this trigger, on mouseover
event, and toggle again when mouseover
is triggered outside this trigger.
As aria-hidden
attribute is set, its value change too.
Collapsible panel / accordion
You want to create a collapsible panel, i.e. a frame, with some content, that can be collapsed on click on a button.
Even if you should use <details>
and <summary>
HTML elements, in some case, you canāt.
Simple collapsible panel
<button type="button"
class="example-collapsible-button"
data-toggle-class="is-open"
data-toggle-target-next>
Click here
</button>
<div class="example-collapsible-panel">
<div class="example-collapsible-panel-content">
Lorem ipsum dolor sit amet…
</div>
</div>
Collapsible panel with escape key support
<button type="button"
class="example-collapsible-button"
data-toggle-class="is-open"
data-toggle-target-next
data-toggle-escape>
Click here
</button>
<div class="example-collapsible-panel">
<div class="example-collapsible-panel-content">
Lorem ipsum dolor sit amet…
</div>
</div>
Collapsible panel with ARIA attributes for accessibility
<button type="button"
class="example-collapsible-button"
id="buttonForPanel"
data-toggle-class="is-open"
data-toggle-target-parent="#panel"
data-toggle-escape
aria-controls="panel"
aria-expanded="false">
Click here
</button>
<div class="example-collapsible-panel"
id="panel"
role="region"
aria-labelledby="buttonForPanel"
aria-hidden="true">
<div class="example-collapsible-panel-content">
Lorem ipsum dolor sit amet…
</div>
</div>
Accordion
<!-- First panel, opened by default -->
<button type="button"
class="example-collapsible-button"
id="buttonForPanel_1"
data-toggle-class="is-open"
data-toggle-target-parent="#panel_1"
data-toggle-group="accordion"
data-toggle-escape
data-toggle-is-active
aria-controls="panel_1"
aria-expanded="true">
Click here
</button>
<div class="example-collapsible-panel"
id="panel_1"
role="region"
aria-labelledby="buttonForPanel_1"
aria-hidden="false">
<div class="example-collapsible-panel-content">
Lorem ipsum dolor sit amet…
</div>
</div>
<!-- Second panel -->
<button type="button"
class="example-collapsible-button"
id="buttonForPanel_2"
data-toggle-class="is-open"
data-toggle-target-parent="#panel_2"
data-toggle-group="accordion"
data-toggle-escape
aria-controls="panel_2"
aria-expanded="false">
Click here
</button>
<div class="example-collapsible-panel"
id="panel_2"
role="region"
aria-labelledby="buttonForPanel_2"
aria-hidden="true">
<div class="example-collapsible-panel-content">
In in erat blandit ante mollis tincidunt…
</div>
</div>
<!-- Third panel -->
<button type="button"
class="example-collapsible-button"
id="buttonForPanel_3"
data-toggle-class="is-open"
data-toggle-target-parent="#panel_3"
data-toggle-group="accordion"
data-toggle-escape
aria-controls="panel_3"
aria-expanded="false">
Click here
</button>
<div class="example-collapsible-panel"
id="panel_3"
role="region"
aria-labelledby="buttonForPanel_3"
aria-hidden="true">
<div class="example-collapsible-panel-content">
Nam posuere tortor a augue vulputate…
</div>
</div>
Explanation
The button toggles .is-open
class on itself and its target located inside its parent container. If thereās more than one button / panel, be careful to target a unique ID.
For an accordion, each trigger must be linked to the same group, here accordion
. This way, only one of this group can be opened at a time. The first panel is opened by default with data-toggle-is-active
.
As aria-expanded
and aria-hidden
attributes are set, their value change too.
Tabs
You want to create some tabs, i.e. some panels visualisable by clicking on their associated button.
Simple tabs
<div class="example-tabs">
<button type="button"
data-toggle-class
data-toggle-target="#tabPanel_1"
data-toggle-radio-group="tabsGroup"
data-toggle-is-active>
tab 1
</button>
<button type="button"
data-toggle-class
data-toggle-target="#tabPanel_2"
data-toggle-radio-group="tabsGroup">
tab 2
</button>
<button type="button"
data-toggle-class
data-toggle-target="#tabPanel_3"
data-toggle-radio-group="tabsGroup">
tab 3
</button>
</div>
<div class="example-tab-panel" id="tabPanel_1">
Panel 1<br>
Lorem ipsum dolor sit amet…
</div>
<div class="example-tab-panel" id="tabPanel_2">
Panel 2<br>
In in erat blandit ante mollis tincidunt…
</div>
<div class="example-tab-panel" id="tabPanel_3">
Panel 3<br>
Nam posuere tortor a augue vulputate…
</div>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam semper faucibus odio, vitae faucibus dui tempor in. Duis vitae luctus mi. Phasellus ultrices rhoncus lectus, non vehicula tortor sodales id.
In in erat blandit ante mollis tincidunt. Quisque ac tempus nisi, a ultricies metus. Suspendisse et quam nec mauris feugiat luctus. Curabitur porta sem vitae nulla congue malesuada.
Nam posuere tortor a augue vulputate, at blandit ipsum tincidunt. Donec dictum eros ligula, id congue justo porta eget. Aliquam vel erat venenatis, ornare nisi vel, convallis libero.
Tabs with ARIA attributes for accessibility
<div class="example-tabs" role="tablist">
<button type="button"
id="tab_1"
data-toggle-class
data-toggle-target="#tabPanel_1"
data-toggle-radio-group="tabsGroup"
data-toggle-is-active
role="tab"
aria-selected="true"
aria-controls="tabPanel_1">
tab 1
</button>
<button type="button"
id="tab_2"
data-toggle-class
data-toggle-target="#tabPanel_2"
data-toggle-radio-group="tabsGroup"
role="tab"
aria-selected="false"
aria-controls="tabPanel_2">
tab 2
</button>
<button type="button"
id="tab_3"
data-toggle-class
data-toggle-target="#tabPanel_3"
data-toggle-radio-group="tabsGroup"
role="tab"
aria-selected="false"
aria-controls="tabPanel_3">
tab 3
</button>
</div>
<div class="example-tab-panel"
id="tabPanel_1"
role="tabpanel"
aria-labelledby="tab_1"
aria-hidden="false">
Panel 1<br>
Lorem ipsum dolor sit amet…
</div>
<div class="example-tab-panel"
id="tabPanel_2"
role="tabpanel"
aria-labelledby="tab_2"
aria-hidden="true">
Panel 2<br>
In in erat blandit ante mollis tincidunt…
</div>
<div class="example-tab-panel"
id="tabPanel_3"
role="tabpanel"
aria-labelledby="tab_3"
aria-hidden="true">
Panel 3<br>
Nam posuere tortor a augue vulputate…
</div>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam semper faucibus odio, vitae faucibus dui tempor in. Duis vitae luctus mi. Phasellus ultrices rhoncus lectus, non vehicula tortor sodales id.
Explanation
Toggle .is-active
class ā by default ā on click on this element, but only one of the tabsGroup
group at a time, and always keep one active. Toggle the same class on the target element. As thereās more than one button / panel, be careful to target a unique ID.
Each trigger must be linked to the same group, here tabsGroup
. This way, only one of this group can be opened at a time. Since data-toggle-radio-group
is used instead of data-toggle-group
, this group will always keep one of its triggers active. The first panel is opened by default with data-toggle-is-active
.
As aria-selected
and aria-hidden
attributes are set, their value change too.
Dropdown
You want to create a dropdown, i.e. a list of choices that appears when you click on a button. Of course you should use <select>
and <option>
HTML elements, but in some case, you canāt.
Simple dropdown
<div class="example-dropdown">
<button type="button"
class="example-dropdown-button"
data-toggle-class="is-open"
data-toggle-target-next
data-toggle-outside
data-toggle-escape>
Select something here
</button>
<ul class="example-dropdown-list">
<li>
<button type="button">Lorem ipsum dolor</button>
</li>
<!-- Several options -->
</ul>
</div>
Dropdown with ARIA attributes for accessibility
<div class="example-dropdown">
<button type="button"
class="example-dropdown-button"
data-toggle-class="is-open"
data-toggle-target-next
data-toggle-outside
data-toggle-escape
id="dropdownButton"
aria-haspopup="listbox"
aria-controls="dropdownList"
aria-expanded="false">
Select something here
</button>
<ul class="example-dropdown-list"
id="dropdownList"
role="listbox"
aria-labelledby="dropdownButton"
aria-hidden="true">
<li>
<button type="button">Lorem ipsum dolor</button>
</li>
<!-- Several options -->
</ul>
</div>
Explanation
Toggle .is-open
class on click on this element. Toggle the class on its target, the next adjacent element, at the same time. Click outside of the trigger or its target, or press escape key to toggle off.
You could also add data-toggle-trigger-off
attribute on each option button to toggle off dropdownās state by clicking on them.
As aria-expanded
and aria-hidden
attributes are set, their value change too.
Note: You can not change the value of the trigger by clicking on an option with this script. Easy Toggle State only deal with changing state. You will have to do this by yourself.
Dialog
You want to create a dialog, i.e. an information or form box displayed in front of all page content on click on a trigger.
Even if you should use <dialog>
HTML element, in some case, you canāt.
Simple dialog
<button type="button"
class="example-dialog-trigger"
data-toggle-class
data-toggle-target="#dialog"
data-toggle-target-only
data-toggle-escape>
Click to see the awesomeness
</button>
<!-- This dialog anywhere you want in the page -->
<div class="example-dialog" id="dialog">
<section class="example-dialog-container">
<!-- Your content here, like text or form. -->
<button type="button" data-toggle-trigger-off>close</button>
</section>
</div>
Dialog title
Any content you want, like text or form.
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Dialog with ARIA attributes for accessibility
<button type="button"
class="example-dialog-trigger"
data-toggle-class
data-toggle-target="#dialog"
data-toggle-target-only
data-toggle-escape>
Click to see the awesomeness
</button>
<!-- This dialog anywhere you want in the page -->
<div class="example-dialog"
id="dialog"
role="dialog"
aria-labelledby="dialogTitle"
aria-describedby="dialogContent"
aria-hidden="true">
<section class="example-dialog-container">
<header id="dialogTitle" class="example-dialog-header">
Dialog title
</header>
<div id="dialogContent" class="example-dialog-content">
<!-- Your content here, like text or form. -->
</div>
<footer class="example-dialog-footer">
<button type="button" data-toggle-trigger-off>close</button>
</footer>
</section>
</div>
Explanation
Toggle .is-active
class ā as default ā on click on this element. Toggle the class on its target #dialog
, placed anywhere in the page ā at the bottom for example ā, at the same time. Press escape key to toggle off.
The attribute data-toggle-trigger-off
enable the ācloseā button to toggle off dialogās state by clicking on it.
As aria-hidden
attribute is set, its value change too.
Lightbox
You want to create a lightbox, i.e. a box displayed in front of all page content to see a larger version of an image.
Simple lightbox
<a class="example-lightbox-trigger"
href="image-lg.jpg"
title="Zoom"
data-toggle-class
data-toggle-target-parent=".example-lightbox"
data-toggle-target-only
data-toggle-escape>
<img src="image.jpg" alt="desc">
</a>
<div class="example-lightbox" role="dialog" aria-hidden="true">
<div class="example-lightbox-bg"
data-toggle-trigger-off>
</div>
<button type="button"
class="example-lightbox-close"
data-toggle-trigger-off
title="close">
ā
</button>
<img class="example-lightbox-img" src="image-lg.jpg" alt="desc">
</div>
Explanation
Toggle .is-active
class ā as default ā on click on this element. Toggle the class on its target .example-lightbox
, belonging to its parent container, at the same time. Press escape key to toggle off.
The attribute data-toggle-trigger-off
enable the ācloseā button and the opacified black background to toggle off lightbox state by clicking on them.
As aria-hidden
attribute is set, its value change too.
Navigation icon
You want to create a navigation icon, i.e. an icon that represent a button you can click on to make a navigation menu visible.
Simple navigation icon
<button type="button"
class="example-nav-trigger"
title="Navigation"
data-toggle-class
data-toggle-escape>
<svg width="1em" class="example-nav-trigger-icon" role="img" viewBox="0 0 100 100">
<title>Navigation icon</title>
<rect class="top-bar" x="0" y="15" width="100" height="12"/>
<rect class="mid-bar" x="0" y="44" width="100" height="12"/>
<rect class="bot-bar" x="0" y="73" width="100" height="12"/>
</svg>
</button>
Explanation
Toggle .is-active
class ā by default ā on click on this element. data-toggle-escape
attribute means you can press escape key to toggle off.
You will probably add data-toggle-target
attribute with the ID of your navigation bar to toggle it at the same time. Here is an example of navigation management.
Consider adding some ARIA attributes for accessibility. Refer to other examples above.
Contribution
- Clone the repo:
git clone https://github.com/twikito/easy-toggle-state.git
- Development:
npm run build
- Fork repository on GitHub
- Report a bug
- Suggest a feature