Toggle

Simple toggle open close component

Toggle applies user interaction events on triggers that are linked to targets onto which class names can be changed individually. On user event on triggers (click, mouseenter, mousehover), Toggle swaps specified class names. Typical usage is modals, dropdowns, dismissable elements, etc.

Toggle is intended to be fully customized with CSS, Javascript involved is just the mechanical part.

Installation

Download and append next to the end body </body> tag as follows:

<script src="/path/to/my-toggle-native.js"></script>

Basic usage

Here is the minimal usage requirement for Toggle. By default, on open, the class name active replaces the current one.

<button my-toggle="foo">foo (trigger)</button>
<div my-toggle-name="foo">foo content (target)</div>
<!-- DEMO ONLY -->
<style>
    body {padding:20px}
    [my-toggle]:not(.active)::after {
        content: ' closed';
    }
    [my-toggle].active::after {
        content: ' opened';
    }
    [my-toggle-name]:not(.active) {
        display: none;
    }
</style>

Multiple triggers and targets

One target can have multiple triggers and one trigger can open multiple targets at once.

<button my-toggle="foo1">foo 1</button>
<button my-toggle="foo2">foo 2</button>
<button my-toggle="foo2">another foo 2</button>
<div my-toggle-name="foo1">
    foo 1 content<br>
    <button my-toggle="foo2">foo 2</button>
</div>
<div my-toggle-name="foo1">
    Another foo 1 content<br>
    <button my-toggle="foo2">foo 2</button>
</div>
<div my-toggle-name="foo2">
    foo 2 content<br>
    <button my-toggle="foo1">foo 1</button>
</div>
<!-- DEMO ONLY -->
<style>
    body {padding:20px}
    [my-toggle]:not(.active)::after {
        content: ' closed';
        background-color: orange;
    }
    [my-toggle].active::after {
        content: ' opened';
        background-color: lightgreen;
    }
    [my-toggle-name]:not(.active) {
        display: none;
    }
    [my-toggle-name].active {
        background-color: lightgreen;
        border-top: 1px solid green;
        padding: 1em;
    }
</style>

Custom trigger and target class names

The default state opened class name can be overridden on any trigger and target with the custom attribute data-opened-state-class="..." with space separated class names.

Original class name are set back when toggle is closed.

<button my-toggle="foo1" 
        data-opened-state-class="bg-green opened" 
        class="bg-orange">
    foo 1
</button>
<button my-toggle="foo2">foo 2</button>
<button my-toggle="foo3" 
        data-opened-state-class="bg-orange opened">
    foo 3
</button>
<div my-toggle-name="foo1">
    <h2>I am a foo1</h1>
    <code>bg-green opened</code> class names replace bg-orange on trigger
</div>
<div my-toggle-name="foo2" data-opened-state-class="bg-green opened">
    <h2>I am a foo2</h2>
    <code>bg-green opened</code> class names are applied on target for these two target foo2 
</div>
<div my-toggle-name="foo2" data-opened-state-class="bg-blue opened">
    <h2>I am a foo2</h2>
    <code>bg-blue opened</code> class names are applied on target for these two target foo2 
</div>
<div my-toggle-name="foo3" data-opened-state-class="bg-blue opened">
    <h2>I am a foo3</h1>
    Different class names applied on trigger and target<br>
    <code>bg-orange opened</code> applied on trigger<br>
    <code>bg-blue opened</code> applied on target<br>
</div>
<!-- DEMO ONLY -->
<style>
    body {padding:20px}
    [my-toggle]:not(.opened):not(.active)::after {
        content: ' closed';
    }
    [my-toggle].opened::after, [my-toggle].active::after {
        content: ' opened';
    }
    [my-toggle-name]:not(.opened):not(.active) {
        display: none;
    }
    [my-toggle-name] {
        padding: 1em;
    }
    .bg-orange { background-color: orange }
    .bg-green { background-color: yellowgreen }
    .bg-blue { background-color: deepskyblue }
</style>

Dismiss - Force close on external click

By default, the only way to toggle an element is to bind a user event (click, mousehover, mouseenter) on a trigger or invoke a method. It is possible to override this feature applying data-dismiss="true" on the trigger. Toggle targets can be closed clicking anywhere on the document excepting on toggles.

<button my-toggle="foo1">foo 1 (default trigger)</button>
<button my-toggle="foo2" data-dismiss="true">foo 1 (dismissable trigger)</button>
<div my-toggle-name="foo1">foo 1 content  - <strong>The only way to close this toggle is to click on the trigger</strong></div>
<div my-toggle-name="foo2">foo 2 content - <strong>This toggle target can be closed clicking anywhere on the document excepting on toggles</strong></div>
<!-- DEMO ONLY -->
<style>
    body {padding:20px}
    [my-toggle]:not(.active)::after {
        content: ' closed';
    }
    [my-toggle].active::after {
        content: ' opened';
    }
    [my-toggle-name]:not(.active) {
        display: none;
    }
</style>

Triggers events

By default, Toggle triggers use click as user interaction to open/close targets. Each trigger can have its own event type: mouseover and mouseenter

  • data-event="mousehover" Trigger is invoked on mouseenter and mouseleave
  • data-event="mouseover" same as mousehover
  • data-event="mouseenter" Trigger is invoked on mouseenter only
Event set Non touch device Touch device
default Opens specified targets when user clicks on the trigger, closes when clicks again Opens specified targets when user touches the trigger, closes when touches again
mousehover or mouseover Opens specified targets when mouse enters the trigger, closes when leaves Opens specified targets when touch on the trigger, closes when touch anywhere
mouseenter Opens specified targets when mouse enters the trigger Opens specified targets when touch on the trigger
<button my-toggle="foo1">foo 1 (default | click)</button>
<button my-toggle="foo2" data-event="mouseenter">foo 2 (mouseenter)</button>
<button my-toggle="foo3" data-event="mousehover">foo 3 (mousehover)</button>
<div my-toggle-name="foo1">
    foo 1 content - <strong>data-event attribute was unset, then this toggle was opened with default event "click".</strong>
    <ul>
        <li>On non touch devices, Toggle opens specified targets when user clicks on the trigger, closes when clicks again</li>
        <li>On touch devices, Toggle opens specified targets when user touches the trigger, closes when touches again</li>
    </ul>  
</div>
<div my-toggle-name="foo2">foo 2 content - <strong>This toggle was opened with mouseenter event, cannot close it until page/DOM refresh</strong></div>
<div my-toggle-name="foo3">
    foo 3 content - <strong>This toggle was opened with mousehover event.</strong>
    <ul>
        <li>On non touch devices, opens specified targets when mouse enters the trigger, closes when leaves</li>
        <li>On touch devices, opens specified targets when touch on the trigger, closes when touch anywhere</li>
    </ul>  
</div>
<!-- DEMO ONLY -->
<style>
    body {padding:20px}
    [my-toggle] {
        margin: 0em 1em 1em 0em;
        padding: 0.5em;
    }
    [my-toggle]:not(.active)::after {
        content: ' closed';
    }
    [my-toggle].active::after {
        content: ' opened';
    }
    [my-toggle-name]:not(.active) {
        display: none;
    }
</style>

Methods

Invoke this method at page start to enable Toggle instances and each time DOM has changed.

myToggle.update();

Opens all triggers and targets matching the specified toggle id

myToggle.open('TOGGLE_ID');

Closes all triggers and targets matching the specified toggle id

myToggle.close('TOGGLE_ID');

Closes all opened triggers and opened targets matching the specified toggle id. Opens all closed triggers and closed targets matching the specified toggle id.

myToggle.toggle('TOGGLE_ID');