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">☰</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...