const inputs = document.querySelectorAll('input[data-type="number"]');
for(let i = 0; i < inputs.length; i++)
{
inputs[i].addEventListener("input", onInput);
inputs[i].addEventListener("keydown", onKeydown);
inputs[i].nextSibling.addEventListener("mousedown", onMousedown);
inputs[i].nextSibling.addEventListener("mouseup", onMouseup);
}
function onInput(e)
{
clearTimeout(this._timer);
const input = e.target,
min = input.min === "" ? null : Number(input.min),
max = input.max === "" ? null : Number(input.max),
canMinus = min === null || min < 0,
canPoint = input.hasAttribute("nopoint") ? false : true;
let curStart = input.selectionStart,
dot = 0,
minus = 0,
i = 0,
timeout = null,
val = input.value;
val = val.replace(/[^0-9]/g, (char, pos) =>
{
if ( (char == "-" && canMinus && !(pos-i) && --minus)
|| ((char == "." || char == ",") && canPoint && pos && (pos-i > 1 || !minus) && !dot++))
{
return char;
}
if (i < curStart)
curStart--;
i++;
return "";
});
if (curStart < 0)
curStart = 0;
const isNumber = val.match(/[0-9]/);
if (isNumber && min !== null && val < min)
val = timeout = min;
if (isNumber && max !== null && val > max)
val = timeout = max;
const exec = () =>
{
input.value = val
input.selectionStart = curStart;
input.selectionEnd = curStart;
}
if (timeout === null)
return exec();
this._timer = setTimeout(exec, 1000);
}
function onKeydown(e)
{
const input = e.target,
min = input.min === "" ? null : Number(input.min),
max = input.max === "" ? null : Number(input.max),
step = input.step === "" ? 1 : Number(input.step),
keys = {ArrowUp: step, ArrowDown: -step};
if (keys[e.key])
{
let val = (keys[e.key] == ~~keys[e.key] ? ~~input.value : Number(input.value)) + keys[e.key];
if (min !== null && val < min)
val = min;
if (max !== null && val > max)
val = max;
input.value = val;
return e.preventDefault();
}
}
function onMousedown(e)
{
if (e.target.parentNode.previousSibling.tagName != "INPUT")
return;
const that = this,
obj = {
target: e.target.parentNode.previousSibling,
key: (e.target.previousSibling === null) ? "ArrowUp" : "ArrowDown",
preventDefault: ()=>{}
};
let time = 300;
!function loop()
{
onKeydown(obj);
that._timer = setTimeout(loop, time);
time = 30;
}();
}
function onMouseup(e)
{
clearTimeout(this._timer);
}
input[data-type="number"] {
appearance: textfield;
padding-right: 1.2em;
}
input[data-type="number"]:not(:focus):not(:hover) ~ span:not(:focus):not(:hover)
{
visibility: hidden;
}
input[data-type="number"] ~ span
{
float:right;
position: relative;
right: 1.8em;
top: 0.2em;
height: 1.8em;
width: 1.5em;
line-height: 1em;
font-size: 0.6em;
cursor: default;
display: grid;
background-color: #F1F1F1;
user-select: none;
}
input[data-type="number"] ~ span > span:nth-child(1):before,
input[data-type="number"] ~ span > span:nth-child(2):before
{
transform: scaleY(0.6);
display: block;
}
input[data-type="number"] ~ span > span:nth-child(1):before
{
content: "▲";
}
input[data-type="number"] ~ span > span:nth-child(2):before
{
content: "▼";
}
input[data-type="number"] ~ span > span:nth-child(1),
input[data-type="number"] ~ span > span:nth-child(2)
{
color: #505050;
width: 100%;
text-align: center;
}
input[data-type="number"] ~ span > span:nth-child(1):hover,
input[data-type="number"] ~ span > span:nth-child(2):hover
{
background-color: #D2D2D2;
}
input[data-type="number"] ~ span > span:nth-child(1):active,
input[data-type="number"] ~ span > span:nth-child(2):active
{
color: white;
background-color: #787878;
}
input[data-type="number"] ~ span > span:nth-child(1)
{
top: 0;
}
input[data-type="number"] ~ span > span:nth-child(2)
{
bottom: 0;
}
<table>
<tr>
<td>Native type="number"</td>
<td><input class="input-price" type="number"></td>
</tr>
<tr>
<td>Any number</td>
<td><input class="input-price" data-type="number"><span><span></span><span></span></span></td>
</tr>
<tr>
<td>Min 11 / max 123</td>
<td><input class="input-price" data-type="number" min="11" max="123"><span><span></span><span></span></span></td>
</tr>
<tr>
<td>No decimal point</td>
<td><input class="input-price" data-type="number" nopoint=""><span><span></span><span></span></span></td>
</tr>
</table>