<form id="number-spinner-vertical" class="t-neutral">
<fieldset class="spinner spinner--vertical">
<label for="spinner-input" class="spinner__label">How many</label>
<button data-type="add" class="spinner__button spinner__button--top" title="Add 1" aria-controls="spinner-input">+</button>
<input type="number" class="spinner__input js-spinner-input-vertical" id="spinner-input" value="1" step="1" max="30" min="0" pattern="[0-9]*" role="alert" aria-live="assertlive" />
<button data-type="subtract" class="spinner__button spinner__button--bottom" title="Subtract 1" aria-controls="spinner-input">-</button>
</fieldset>
</form>
/* Theming */
.t-neutral .spinner__button {
background: #222;
color: $#fff;
}
.t-neutral .spinner__button:hover,
.t-neutral .spinner__button:focus,
.t-neutral .spinner__button:active {
background: #444;
}
.t-neutral .spinner__input {
background: #eee;
}
/* Aggressive reset to remove the initial spinner */
input[type="number"] {
-moz-appearance: textfield;
}
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
-moz-appearance: none;
-webkit-appearance: none;
appearance:none;
margin: 0;
}
input[type="number"]:hover::-webkit-inner-spin-button,
input[type="number"]:hover::-webkit-outer-spin-button {
-moz-appearance: none;
-webkit-appearance: none;
appearance:none;
margin:0;
}
/* The component (also includes vertical) */
.spinner {
margin: 0 0 5em 0;
}
.spinner:after {
clear: both;
content: "";
display: table;
}
.spinner__label {
display: block;
text-align: center;
width: 100%;
}
.spinner__button {
cursor: pointer;
display: block;
font-size: 1em;
font-weight: 700;
padding: 1.1em 1em 1.15em;
text-align: center;
text-decoration: none;
transition: background 0.4s ease;
}
.spinner__button--left {
border-bottom-left-radius: 5px;
border-top-left-radius: 5px;
}
.spinner__button--right {
border-bottom-right-radius: 5px;
border-top-right-radius: 5px;
}
.spinner__button--top {
border-top-left-radius: 5px;
border-top-right-radius: 5px;
}
.spinner__button--bottom {
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
}
.spinner--vertical .spinner__button {
padding:2em 1em;
width:100%;
}
.spinner__input {
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
background: none;
border: none;
-webkit-border-radius: 0;
border-radius: 0;
border-radius: none;
box-sizing: border-box;
display: block;
font-size: 1em;
line-height: 1.5;
text-align: center;
}
.spinner--horizontal .spinner__input {
padding: 1em 1em 1.05em 1em;
@media all and (min-width:40em) {
padding: 1.1em 1em 1.05em 1em;
}
}
.spinner--vertical .spinner__input {
padding:1.5em 1em 1.5em 1em;
width:100%;
}
.spinner--horizontal .spinner__button {
float: left;
width: 25%;
}
.spinner--horizontal .spinner__input {
float: left;
width: 50%;
}
//gets the input by element Id, gets min, max, and step from the markup. Gets the subtract and add buttons either by optional classnames, or by the next or last element sibling.
var NumberSpinner = function(elemId, subtractClassName, addClassName) {
'use strict';
var spinnerInput = document.getElementById(elemId);
var btnSubtract = document.querySelector(addClassName) || spinnerInput.previousElementSibling;
var btnAdd = document.querySelector(subtractClassName) || spinnerInput.nextElementSibling;
var minLimit, maxLimit, step;
function init(){
minLimit = makeNumber(getAttribute(spinnerInput, 'min')) || 0,
maxLimit = makeNumber(getAttribute(spinnerInput, 'max')) || false,
step = makeNumber(getAttribute(spinnerInput, 'step') || '1');
btnSubtract.addEventListener('click', changeSpinner, false);
btnAdd.addEventListener('click', changeSpinner, false);
btnSubtract.addEventListener('keyup', keySpinner, false);
btnAdd.addEventListener('keyup', keySpinner, false);
if(supportsTouch()) {
btnSubtract.addEventListener('touchend', removeClickDelay, false);
btnAdd.addEventListener('touchend', removeClickDelay, false);
}
if(supportsPointer()) {
btnSubtract.addEventListener('pointerup', removeClickDelay, false);
btnAdd.addEventListener('pointerup', removeClickDelay, false);
}
}
function removeClickDelay(e) {
e.preventDefault();
e.target.click();
}
function makeNumber(inputString){
return parseInt(inputString, 10);
}
function update(direction){
var num = makeNumber(spinnerInput.value);
if(direction === 'add'){
spinnerInput.value = ((num + step) <= maxLimit) ? (num + step) : spinnerInput.value;
} else if(direction === 'subtract') {
spinnerInput.value = ((num - step) >= minLimit) ? (num - step) : spinnerInput.value;
}
}
function getAttribute(el, attr){
var hasGetAttr = (el.getAttribute && el.getAttribute(attr)) || null;
if(!hasGetAttr) {
var attrs = el.attributes;
for(var i = 0, len = attrs.length; i < len; i++){
if(attrs[i].nodeName === attr) {
hasGetAttr = attrs[i].nodeValue;
}
}
}
return hasGetAttr;
}
/* Touch and Pointer support */
function supportsTouch(){
return ('ontouchstart' in window);
}
function supportsPointer(){
return ('pointerdown' in window);
}
/* Keyboard support */
function keySpinner(e){
switch(e.keyCode){
case 40:
case 37: // Down, Left
update('subtract');
btnSubtract.focus();
break;
case 38:
case 39: // Top, Right
update('add');
btnAdd.focus();
break;
}
}
function changeSpinner(e) {
e.preventDefault();
var increment = getAttribute(e.target, 'data-type');
update(increment);
}
init();
};
TBD.
TBD.
TBD.
TBD.
TBD.
TBD.
TBD.
TBD.
TBD.