Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save dfkaye/612d34a66b9bb74efc29 to your computer and use it in GitHub Desktop.
Save dfkaye/612d34a66b9bb74efc29 to your computer and use it in GitHub Desktop.
traffic signal HTML test for 11 June, completed 12 June 2015, simplified 5 Sep 2015
<!DOCTYPE html>
<!--
click <next> to change signal lamp from red to green to yellow to red to...
simplified 5-6 Sep 2015
+ reset signal attribute on parent element (single value state)
+ let state rules define which lamp is colored.
31 Oct 2016
+ prefixed attributes with "data-" per the HTML5 Spec...
28 Aug 2017
+ changes attrs from [data-lamp][data-stop] to [data-lamp="stop"]
-->
<title>traffic signal test revised</title>
<style>
[data-signal] {
background: grey;
display: inline-block;
margin: 0;
outline: 1px solid;
padding: 0;
}
[data-lamp] {
background: #adadad;
border-radius: 2em 2em; /* make circular :) */
border: 1px solid;
height: 3em;
list-style-type: none;
margin: 1em;
padding: 0;
width: 3em;
}
[data-signal="stop"] [data-lamp="stop"] { background: red; }
[data-signal="slow"] [data-lamp="slow"] { background: yellow; }
[data-signal="go"] [data-lamp="go"] { background: green; }
[data-next] {
/* force newline after inline traffic list */
display:block;
}
</style>
<ul data-signal>
<li data-lamp="stop"></li>
<li data-lamp="slow"></li>
<li data-lamp="go"></li>
</ul>
<button type="button" data-next>next</button>
<script>
!(function() {
var signal = document.querySelector('[data-signal]')
var btn = document.querySelector('[data-next]')
var lamp; // store state here, no need to call signal.getAttribute
function next() {
lamp = (lamp == 'stop' ? 'go' : (lamp == 'go' ? 'slow' : 'stop'))
signal.setAttribute('data-signal', lamp)
}
next()
btn.addEventListener('click', next)
}());
</script>
<p>
Click `[next]` to change signal lamp from red to green to yellow to red to...
</p>
<p>
HTML uses no `class` attributes - instead uses custom attributes (prefixed
with "data-") - CSS uses attribute selectors - JS uses
querySelector('[data-attr-name]') and `.setAttribute(value)`
</p>
<!DOCTYPE html>
<!--
click <change> to change signal lamp from red to green to yellow to red to...
simplified 5-6 Sep 2015
+ reset signal attribute on parent element (single value state)
+ let state rules define which lamp is colored.
31 Oct 2016
+ prefixed attributes with "data-" per the HTML5 Spec...
-->
<title>traffic signal test revised</title>
<style>
[data-signal] {
background: grey;
display: inline-block;
margin: 0;
outline: 1px solid;
padding: 0;
}
[data-lamp] {
background: #adadad;
border-radius: 2em 2em; /* make circular :) */
border: 1px solid;
height: 3em;
list-style-type: none;
margin: 1em;
padding: 0;
width: 3em;
}
[data-signal='stop'] [data-stop] { background: red; }
[data-signal='slow'] [data-slow] { background: yellow; }
[data-signal='go'] [data-go] { background: green; }
[data-change] {
/* force newline after inline traffic list */
display:block;
}
</style>
<ul data-signal>
<li data-lamp data-stop></li>
<li data-lamp data-slow></li>
<li data-lamp data-go></li>
</ul>
<button data-change>change</button>
<script>
!(function() {
var signal = document.querySelector('[data-signal]')
var btn = document.querySelector('[data-change]')
var lamp; // store state here, no need to call signal.getAttribute
function change() {
lamp = (lamp == 'stop' ? 'go' : (lamp == 'go' ? 'slow' : 'stop'))
signal.setAttribute('data-signal', lamp)
}
change()
btn.addEventListener('click', change)
}());
</script>
<p>
Click `[change]` to change signal lamp from red to green to yellow to red to...
</p>
<p>
HTML uses no `class` attributes - instead uses custom attributes (prefixed
with "data-") - CSS uses attribute selectors - JS uses
querySelector('[data-attr-name]') and `.setAttribute(value)`
</p>
<!DOCTYPE html>
<!--
click <change> to change signal lamp from red to green to yellow to red to...
simplified 5 Sep 2015
+ reset signal attribute on parent element (single value state)
+ let state rules define which lamp is colored.
-->
<title>traffic signal test revised</title>
<style>
[signal] {
background: grey;
display: inline-block;
margin: 0;
outline: 1px solid;
padding: 0;
}
[lamp] {
background: #adadad;
border-radius: 2em 2em; /* make circular :) */
border: 1px solid;
height: 3em;
list-style-type: none;
margin: 1em;
padding: 0;
width: 3em;
}
[signal='stop'] [stop] { background: red; }
[signal='slow'] [slow] { background: yellow; }
[signal='go'] [go] { background: green; }
[change] {
/* force newline after inline traffic list */
display: block;
}
</style>
<ul signal>
<li lamp stop></li>
<li lamp slow></li>
<li lamp go></li>
</ul>
<button change>change</button>
<script>
!(function() {
var signal = document.querySelector('[signal]')
var btn = document.querySelector('[change]')
var lamp; // store state here, no need to call signal.getAttribute
function change() {
// decision algorithm for next state
lamp = (lamp == 'stop' ? 'go' : (lamp == 'go' ? 'slow' : 'stop'))
signal.setAttribute('signal', lamp)
}
change() // initialize...
btn.addEventListener('click', change)
}());
</script>
<!DOCTYPE html>
<!--
click <next> to change signal from red to green to yellow to red to...
completed version of this test by @dfkaye 12 June 2015 using
+ multiple attribute selectors
+ array.some() method for early iteration exit
+ omitted html, head and body tags ~ browsers insert these on parse!
-->
<title>traffic signal test</title>
<style>
[traffic] {
background: grey;
display: inline-block;
margin: 0;
outline: 1px solid;
padding: 0;
text-align: center;
}
[signal] {
background: #adadad;
border-radius: 2em 2em; /* make circular :) */
border: 1px solid;
height: 3em;
list-style-type: none;
margin: 1em;
padding: 0;
width: 3em;
}
/* select by presence of multiple attributes */
/* an active signal shows its color */
[active][stop] { background: red; }
[active][slow] { background: yellow; }
[active][go] { background: green; }
[next] {
/* force newline after inline traffic list */
display: block;
}
</style>
<ul traffic>
<li stop signal active></li><!-- default -->
<li slow signal></li>
<li go signal></li>
</ul>
<button next>next</button>
<script>
!(function() {
var signals = [].slice.call(document.querySelectorAll('[signal]'));
var btn = document.querySelector('[next]');
var active = document.querySelector('[signal][active]') || signals[0];
var handleClick = function handleClick(e) {
var next;
signals.some(function (signal, i) {
return signal === active && (next = signals[i === 0 ? signals.length - 1 : i - 1]);
});
active.removeAttribute('active');
next.setAttribute('active', '');
active = next;
};
btn.addEventListener('click', handleClick);
}());
</script>
@dfkaye
Copy link
Author

dfkaye commented Jun 13, 2015

Alternative JavaScript w/IIFE in array#some() w/IIFE:

!(function() {
  "use strict";

  var signals = [].slice.call(document.querySelectorAll('[signal]'));
  var active = document.querySelector('[active][signal]') || signals[0];
  var btn = document.querySelector('[next]');

  function handleClick(e) {
    signals.some(function (signal, i) {
      return signal === active && (function() {

        // one-stop re-assignment w/IIFE
        signal.removeAttribute('active');
        active = signals[i === 0 ? signals.length - 1 : i - 1];
        active.setAttribute('active', '');

        return true; // halt some() iteration
      })();
    });
  };

  btn.addEventListener('click', handleClick);
}());

@dfkaye
Copy link
Author

dfkaye commented Sep 7, 2015

The revised next algorithm in the revised file came to me unexpectedly, and may change the way I approach anything from now on. It feels more like a breakthrough than it really is due to its simplicity, which I prefer over the gratuitous chaining that functional programming seems to demand.

lamp = (lamp == 'stop' ? 'go' : 
       (lamp == 'go'   ? 'slow' : 
                         'stop'))

Real benefits:

  • easier to type (only possible choices are 'go', 'slow' or 'stop')
  • involves no functional iterator callback dependency because we don't iterate over lamp elements
  • sets the attribute only on the signal element
  • never reads from the signal element (no element.getAttribute() or removeAttribute() calls)
  • the decision sequence allows for an initial call to set a default state (i.e., to 'stop')
  • the attribute-based CSS uses a single-value attribute (signal) as a state on the ancestor to each lamp type attributes (go, slow, stop)

Possible TODO:

  • [role='tab'][aria-selected] for #a11y

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment