JSFiddle - React, Tailwind, and code Playground

REGEX version sets the data-state attr of elements on the page as they are clicked

by moob

HTML

<span id="nav-trigger">&#9776;</span>

<div id="nav">
    <ul>
        <li><a href="#">Menu Item</a></li>
        <li><a href="#">This Thing</a></li>
        <li><a href="#">Another Thing</a></li>
        <li><a href="#">Something Else</a>
            <input type="checkbox" id="submenu1" />
            <label for="submenu1"></label>
            <ul>
                <li><a href="#">Sub Item</a></li>
                <li><a href="#">This Thing</a></li>
                <li><a href="#">Sub Item</a></li>
                <li><a href="#">This Thing</a></li>
            </ul>
        </li>
        <li><a href="#">Kittens</a></li>
        <li><a href="#nav-trigger">close</a></li>
    </ul>
</div>

<div id="main">

    <div id="key">
        <div data-state="clicked">clicked</div> (every element clicked is recorded as such)
        <br/>
        <div data-state="on">on</div> (on click toggles the 'on' state)
        <br/>
        <div data-state="active">active</div> (only 1 child can be active)
        <br/>
        <div data-state="previous">previous</div> (the last previously active child)
        <br/>
        <div data-state="current">current </div> (the very last element that been clicked)
    </div>

    <div>
        <p>paragraph</p>
        <p>paragraph <strong>strong</strong> <span>span</span></p>
        <a href='#'>link</a><a href='#'>link</a><a href='#'>link</a><a href='#'>link</a><a href='#'>link</a><a href='#'>link</a>
    </div>

    <ul>
        <li>one</li>
        <li>two</li>
        <li>three</li>
        <li>four</li>
    </ul>

    <section id="eg1">
        <div>using 'on'
            <div>All children can be opened independently</div>
        </div>
        <div>to control
            <div>All children can be opened independently</div>
        </div>
        <div>an accordion
            <div>All children can be opened independently</div>
        </div>
    </section>

    <section id="eg2">
        <div data-state="active">using 'active'
          ...

CSS

*,
*:before,
*:after {
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

html,
body {
    height: 100%;
    width: 100%;
    font-family: Helvetica, Arial, sans-serif;
}

body {
    overflow-x: hidden;
}

#nav-trigger {
    width: 30px;
    height: 30px;
    display: block;
    color: #000;
    font-size: 30px;
    line-height: 30px;
    -webkit-font-smoothing: none;
    font-smoothing: none;
    font-weight: bold;
    cursor: pointer;
    transition: color 0.2s, transform 0.4s;
}


/* The nav itself (off-screen by default) */

#nav {
    width: 300px;
    max-width: 80%;
    height: 100%;
    position: absolute;
    top: 0;
    right: -300px;
    bottom: 0;
    z-index: 10;
    list-style: none;
    background: #111;
}

#nav ul {
    margin: 0;
    padding: 0;
    display: block;
    position: relative;
    height: 100%;
    top: 0;
    right: 0;
    overflow: auto;
}

#nav li {
    display: block;
    position: relative;
    margin: 1px 0;
    padding: 0;
}

#nav li a,
#nav li label[for='nav-trigger'] {
    display: block;
    padding: 1em;
    background: #333;
    color: white;
    font-size: 1.2em;
    line-height: 1.2em;
    text-decoration: none;
    transition: color 0.2s, background 0.5s;
}

#nav li label {
    color: #999;
}

#nav li a:hover,
#nav li label:hover {
    color: rgb(160, 216, 239);
    background: #444;
}

#nav ul ul {
    opacity: 0.7;
    height: inherit;
    overflow: visible;
}

#nav ul ul li {
    display: block;
    max-height: 0;
    overflow: hidden;
    margin: 0;
    text-indent: 20px;
    -webkit-transition: max-height 0.2s;
    transition: max-height 0.2s;
}

#nav ul li input {
    position: absolute;
    clip: rect(0, 0, 0, 0);
    visibility: hidden;
}

#nav ul li input+label {
    -webkit-font-smoothing: none;
    font-smoothing: none;
    display: block;
    position: absolute;
    top: 0;
    right: 0;
    padding: 1em;
    font-size: 1.2em;
  ...

JavaScript

/*
clicked (every element clicked is recorded as such)
on (every element clicked is marked as on)
active (only 1 child can be active)
current (the very last element that been clicked)
inview (currently in viewport)
seen (was previously or is currently in viewport)
*/
;
(function() {
    "use strict";
    var state = {
        add: function(el, str) {
            //console.warn(el);
            if (!!el && !!el.dataset && !!el.dataset.state) {
                state.remove(el, str);
                el.dataset.state = el.dataset.state + " " + str;
            } else if (!!el && !!el.dataset) {
                el.dataset.state = str;
            }
            el.dataset.state = el.dataset.state.replace("  ", "");
        },
        remove: function(el, str) {
            if (!!el.dataset && !!el.dataset.state) {
                var re = new RegExp(str, "g");
                el.dataset.state = el.dataset.state.replace(re, "");
            }
        },
        toggle: function(el, str) {
            if (state.is(el, str)) {
                state.remove(el, str);
            } else {
                state.add(el, str);
            }
        },
        replace: function(el, oldstr, newstr) {
            if (state.is(el, oldstr)) {
                state.remove(el, oldstr);
                state.add(el, newstr);
            }
        },
        is: function(el, str) {
            if (!!el.dataset && !!el.dataset.state) {
                //TODO consider a regex here (it might be faster)
                var states = el.dataset.state.split(' ');
                var index = states.indexOf(str); //IE >=9
                if (index > -1) {
                    return true;
                }
            }
            return false;
        },
        onclick: function(e) {
            console.log(e);
            var el = e.target;
            //clicked (every element clicked is recorded as such)
            state.add(el, "clicked");
            //on (every element clicked is marked...