State JS 0.1

Sets the data-state attr of elements on the page as they are clicked

by moob

HTML

<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 in each family)
        <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)
        <br/>
        <div data-state="inview">inview </div> (currently in viewport)
        <br/>
        <div data-state="seen">seen </div> (was previously or is currently in viewport)
    </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'
            <div>Only one child</div>
        </div>
        <div>to control
            <div>may be open</div>
        </div>
        <div>an accordion
            <div>at a time</div>
        </div>
    </section>

    <section id="eg3">
        <a id="slide0trigger" data-state="active">1</a><a id="slide1trigger">2</a><a id="slide2trigger">3</a>
        <ul>
            <li id="slide0"...

CSS

#key {
    pointer-events: none;
    border: 3px solid rgba(123, 23, 123, .1);
    font-family: monospace;
}

#key * {
    display: inline-block;
}

body * {
    background: white;
    border: 1px solid #eee;
    margin: 5px;
    position: relative;
}

body *[data-state~="clicked"] {
    border: 1px solid #666;
}

body *[data-state~="on"] {
    outline: 3px dotted rgba(255, 155, 155, .5);
}

body *[data-state~="active"] {
    background: rgba(0, 255, 50, 0.15);
}

body *[data-state~="previous"] {
    background: rgba(255, 255, 0, 0.1);
}

body *[data-state~="current"] {
    color: red;
    padding-right: 20px;
}

body *[data-state~="current"]:after {
    content: "*";
    position: absolute;
    top: 0%;
    right: 0%;
    color: red;
    font-size: 40px;
    height: 20px;
    width: 20px;
    line-height: 32px;
    background: rgba(255, 0, 0, 0.1);
    text-align: center;
}

*[data-state] {
    transition: all 0.3s;
}
*[data-state~="seen"] {
    background: rgba(100, 0, 100, 0.05);
}
*[data-state~="inview"] {
    background: rgba(0, 0, 0, 0.05);
}


/* eg1 */

section#eg1 > div > div {
    transition: all 0.5s;
    margin:0;
    height:0px; overflow:hidden;
}
section#eg1 > div[data-state~="on"] > div {
    height:100px;
}


/* eg2 */

section#eg2 > div > div {
    transition: all 0.5s;
    margin:0;
    height:0px; overflow:hidden;
}
section#eg2 > div[data-state~="active"] > div {
    height:100px;
}


/* eg3 */

section#eg3 > ul {
    box-sizing: border-box;
    display: black;
    position: relative;
    width: 100%;
    height: 200px;
    margin: 0 0 20px 0;
    padding: 0;
    list-style: none;
    overflow: hidden;
}

section#eg3 > ul > li {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
    list-style: none;
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    width: 100%;
    border: 1px solid red;
    z-index: 1;
    background-color: #eee;
    animation: slide-out 0.8s forwards;
    -webkit-animation: slide-out 0.8s forwards;
...

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) {
            if (!!el.dataset.state) {
                var states = el.dataset.state.split(' ');
                var index = states.indexOf(str); //IE >=9
                if (index < 0) {
                    states.push(str);
                }
                el.dataset.state = states.join(' ');
            } else {
                el.dataset.state = str;
            }
        },
        remove: function(el, str) {
            if (!!el.dataset && !!el.dataset.state) {
                var states = el.dataset.state.split(' ');
                var index = states.indexOf(str); //IE >=9
                if (index > -1) {
                    states.splice(index, 1);
                }
                el.dataset.state = states.join(' ');
            }
        },
        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 && !!el.dataset && !!el.dataset.state) {
                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.target,e.currentTarget);
            var start = performance.now();  
            //mark...