I have an input of type number that is rendered using the following code:
tl;dr:
Having been asked in private about the following setup quite a few times, I decided to add a demo for it (Bootstrap 4 + jQuery + Font Awesome input-group setup):
$('.btn-plus, .btn-minus').on('click', function(e) {
const isNegative = $(e.target).closest('.btn-minus').is('.btn-minus');
const input = $(e.target).closest('.input-group').find('input');
if (input.is('input')) {
input[0][isNegative ? 'stepDown' : 'stepUp']()
}
})
.inline-group {
max-width: 9rem;
padding: .5rem;
}
.inline-group .form-control {
text-align: right;
}
.form-control[type="number"]::-webkit-inner-spin-button,
.form-control[type="number"]::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
long (initial) answer:
The native input[type=number] controls are not style-able cross-browser. The easiest and safest way to achieve what you want cross-browser/cross-device is to hide them using:
input[type="number"] {
-webkit-appearance: textfield;
-moz-appearance: textfield;
appearance: textfield;
}
input[type=number]::-webkit-inner-spin-button,
input[type=number]::-webkit-outer-spin-button {
-webkit-appearance: none;
}
...which allows you to use your custom buttons, which could be linked to execute the functions the spinners (arrows) would (.stepUp() and .stepDown()), provided you keep the input's type="number".
For example:
input[type="number"] {
-webkit-appearance: textfield;
-moz-appearance: textfield;
appearance: textfield;
}
input[type=number]::-webkit-inner-spin-button,
input[type=number]::-webkit-outer-spin-button {
-webkit-appearance: none;
}
.number-input {
border: 2px solid #ffffd;
display: inline-flex;
}
.number-input,
.number-input * {
box-sizing: border-box;
}
.number-input button {
outline:none;
-webkit-appearance: none;
background-color: transparent;
border: none;
align-items: center;
justify-content: center;
width: 3rem;
height: 3rem;
cursor: pointer;
margin: 0;
position: relative;
}
.number-input button:before,
.number-input button:after {
display: inline-block;
position: absolute;
content: '';
width: 1rem;
height: 2px;
background-color: #212121;
transform: translate(-50%, -50%);
}
.number-input button.plus:after {
transform: translate(-50%, -50%) rotate(90deg);
}
.number-input input[type=number] {
font-family: sans-serif;
max-width: 5rem;
padding: .5rem;
border: solid #ffffd;
border-width: 0 2px;
font-size: 2rem;
height: 3rem;
font-weight: bold;
text-align: center;
}
Note: In order to change the input's value, one needs to find it. To provide flexibility, in the example above I grouped buttons and the under a common parent and used that parent to find the (choosing not to rely on their proximity or particular order in DOM). The above method will change any input[type=number] sibling to the buttons. If that's not convenient, one could use any other methods to find the input from the buttons:
.querySelector('#some-id'):
.querySelector('.some-class'):
Also note the above examples only search inside the .parentNode, not in the entire document, which is also possible:
i.e: onclick="document.getElementById('#some-id').stepUp()"
previousElementSibling | nextElementSibling)
An important note when using jQuery is that the stepUp() and stepDown() methods are placed on the DOM element, not on the jQuery wrapper. The DOM element is found inside the 0 property of the jQuery wrapper.
Note on preventDefault(). Clicking a inside a will trigger the form submission. Therefore, if used as above, inside forms, the onclick should also contain preventDefault();. Example:
However, if one would use tags instead of s, this is not necessary. Also, the prevention can be set globally for all form buttons with a small JavaScript snippet:
var buttons = document.querySelectorAll('form button:not([type="submit"])');
for (i = 0; i < buttons.length; i++) {
buttons[i].addEventListener('click', function(e) {
e.preventDefault();
});
}
... or, using jQuery:
$('form').on('click', 'button:not([type="submit"])', function(e){
e.preventDefault();
})