Increment input value using mouse wheel - javascript

I'm trying to increase/decrease the value of input field using mouse wheel. I've put together the following code. It's working fine, but there's a small problem.
The behaviour I want is to be able to increment/decrement the input value using mouse wheel once I focus on the element. Mouse doesn't have to be hovering the element. The following code performs this. But if I use wheel while hovering the input element, the value is incremented/decremented by 2 instead of 1.
var hoveredInput = null;
$('input[type="number"]').on("focus", function(e) {
hoveredInput = this;
});
$('input[type="number"]').on("blur", function(e) {
hoveredInput = null;
});
$(window).on("wheel", function(e) {
if (hoveredInput) {
if (e.originalEvent.deltaY < 0) {
var currentValue = parseInt(hoveredInput.value, 10);
var newValue = currentValue + 1;
if (newValue > parseInt(hoveredInput.max, 10)) {
newValue = hoveredInput.max;
}
hoveredInput.value = newValue;
} else {
var currentValue = parseInt(hoveredInput.value, 10);
var newValue = currentValue - 1;
if (newValue < parseInt(hoveredInput.min, 10)) {
newValue = hoveredInput.min;
}
hoveredInput.value = newValue;
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="number" value="0" min="0" max="255" />
After some experimenting, I figured that there's a similar behaviour for up and down arrow keys. Up and down arrow keys, on a number input, increments/decrements the value. And I suppose, this behaviour conflicts with my code. Causes it to increment by 2, even though the code doesn't execute twice.
I've just realized that this might be a Chrome specific problem. Chrome let's you increment/decrement number input value using mouse wheel if you focus and hover the element. However, it works in a really weird way.
If I just add <input type="number" /> in a blank HTML page, this mouse wheel increment doesn't work. To make it work, I simply add window.onwheel = function() {};. This doesn't make any sense. Also this seems to work on JSFiddle and JSBin without onwheel assignment on the window.
Going back to the actual problem, can I disable the default mouse wheel increment on the element, so that I can use my custom one? Or is there another approach that I can use?

I'm not sure why you would be considering not using preventDefault() to prevent the default action. You are changing what the UI action will be under these circumstances. You should, of course, use preventDefault() to prevent the default action. If you don't use preventDefault() then there would be some unexpected consequences to using the scroll wheel when the <input type="number"> is focused. Without preventDefault(), what combination of unexpected consequences would occur under those conditions will depend on the browser that is being used to view the page.
I am unable to duplicate a conflict with using the cursor keys to change the input value. Obviously, if all you are using to limit the minimum and maximum values of the <input> is the code for the mouse wheel, then those limits will not function for any other method of entry. You could use the min and max attributes for limiting values. Doing so would be better for multiple reasons, including that it affects all methods of entering a value and as it allows defining those ranges per <input> instead of one set of limits for all <input type="number">. I have changed the code so that your code also uses these attributes.
If you do this, you may want to consider adding a CSS style to indicate that the <input type="number"> element has focus. Doing so will make it more clear to the user why the mouse wheel is not doing what they normally expect from their browser's UI.
I suggest you try this with multiple browsers to see if it is something you desire. Personally, at least in the testing I have done on this page, I like the behavior.
NOTE:
Your use of the focus and blur events is overly complex. I have changed the code to directly find the focused element using document.activeElement.
//Exclude one specific element for comparing this UI vs. the browser's default.
var excludedEl = document.getElementById('exclude');
$(window).on("wheel", function(e) {
focusedEl = document.activeElement;
if(focusedEl === excludedEl){
//Exclude one specific element for UI comparison
return;
}
if (focusedEl.nodeName='input' && focusedEl.type && focusedEl.type.match(/number/i)){
e.preventDefault();
var max=null;
var min=null;
if(focusedEl.hasAttribute('max')){
max = focusedEl.getAttribute('max');
}
if(focusedEl.hasAttribute('min')){
min = focusedEl.getAttribute('min');
}
var value = parseInt(focusedEl.value, 10);
if (e.originalEvent.deltaY < 0) {
value++;
if (max !== null && value > max) {
value = max;
}
} else {
value--;
if (min !== null && value < min) {
value = min;
}
}
focusedEl.value = value;
}
});
input[type="number"]:focus {
box-shadow: 0px 0px 1.5px 1px cyan;
}
/*For comparing UIs: May conflict with browser default, or user's theme.*/
#exclude:focus {
box-shadow: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
NOTE: Events are only caught while the mouse stays within the test portion of the stackoverflow page:<br/><br/><br/>
Uses changed UI (if focused, mouse wheel will increment/decrement):<br/>
<input type="number" value="0" id="firstNumberInput" min="0" max="255"/>
<br/>Uses browser default UI:
<input id="exclude" type="number" value="0" min="0" max="255" style="display:block"/>

Related

How to be able to use HTML slider with arrow keys

I have this code written up to and the issue I am having is moving the slider with arrow keys, it works if I first click the slider but I want it to work on the page load without having to click the slider first and am not sure how to accomplish that.
<script>
function updateImage(sliderValue) {
document.getElementById("image").src =
"img"
+ sliderValue + ".png";
}
document.getElementById('slider').addEventListener(function() {
this.focus();
});
</script>
<div class="slider-container">
<input type="range" min="0" max="384" value="0" step="6"
oninput="updateImage(this.value)" id="slider"/>
</div>
<img id="image" src="img.png" width="1144" height="898">
Add a keydown listener on the document that checks the event key code (from which it determines the key pressed), and changes the slider value accordingly:
document.addEventListener('keydown', function(e) {
if (e.keyCode == 39) { //right
slider.value = +slider.value + +slider.step;
} else if (e.keyCode == 37) { //left
slider.value = +slider.value - +slider.step;
}
updateImage(slider.value)
})
function updateImage(sliderValue) {
console.log(sliderValue)
document.getElementById("image").src =
"img" +
sliderValue + ".png";
}
<div class="slider-container">
<input type="range" min="0" max="400" value="384" step="6" id="slider" oninput="updateImage(this.value)" />
</div>
<img id="image" src="img.png">
The first thing, you can do your job with two different ways, one is simple and not entirely active, and the second is great but more complicated:
The First way:
You can add a onload event for the window, and when the window is loaded you can focus on the slider input, and this will makes it possible to use arrow keys for move your slides right and left.
and instead of saying:
document.getElementById('slider').addEventListener(function() {
this.focus();
});
You can say that:
window.onload = function() {
document.getElementById("slider").focus()
}
And this will put the focus on your input directly when the window is finishes loading.
This is the easy way to use arrow buttons to navigate your slider,
But there is another advanced way to do this.
The Second way:
You can use the keyEvents on the window itself again but not for pushing the focus to the input range, you will use it for getting what's clicked whatever where, and if the clicked button is one of the arrow left or right buttons, increase or decrease the value of the slider, and run the updateImage with the new value
and this is the code that you need to put after the updateImage function:
window.addEventListener("keydown", (e) => {
let slider = document.getElementById("slider")
if( e.key === "ArrowLeft" ) {
e.preventDefault()
slider.value -= 1
updateImage(slider.value)
}
else if( e.key === "ArrowRight" ) {
e.preventDefault()
slider.value = Number(slider.value) + 1
updateImage(slider.value)
}
})
Here in this code, you will find that I apply the function whenever a keydown event happens in the window, whatever where.
And inside the function, I got the slider, then check for the arrow left and right keys, inside the two condition I prevent default actions of the buttons, but you can delete this part, then I increase or decrease the slider value by one, "In your case add or remove 6, because you set the step to 6",
then I updateImage mySelf with the new value, and that's because changing the input value from javascript will not run your oninput event attached to the input.
And now, you can click on the right and left arrows while you are not even focus on the input, and the images will still changed and the slider itself will still changed.
I hope I managed to solve your problem.
My solution of your problem.
const slider = document.getElementById('slider');
slider.addEventListener(e => {
switch(e.key) {
case "ArrowLeft":
// Left arrow logic
break;
case "ArrowRight":
// Right arrow logic
break;
}
});

Range slider - avoid moving forward when reaching a value specified in another element

lets say i have a range slider going from value 1 to 100
by default i would be able to slide it going from 1 to 100.
I would like the slider to block, when reaching a value specified in another element.
I've been trying it like this (where 20 would be the value in the element, just easier for the example), but that isn't perfect. the Thumb does indeed stop, but i think internally if you keep sliding, it will remember that value, even if you don't see it.
EDIT:
would a for loop be something good here?
for(i=slider.value;i<20;i++)
...
if (slider.value >=20)
slider.value=20
Anyone better at this who can help?
Wrap everything in a <form> if you haven't already then assuming your limit is set by another <input>, assign an event handler to the <form> so it will trigger whenever there's an "input" event. Have the event handler change the [max] attribute value of the range input to the [value] of the limit <input>.
const limit = e => {
const io = e.currentTarget.elements;
let max = io.limit.value;
io.range.max = max;
io.view.value = io.range.value;
};
const ui = document.forms.ui;
ui.oninput = limit;
input {
display: inline-block;
width: 5ch;
text-align: right;
}
#range {
width: 20ch
}
<form id='ui'>
<input id='limit' type='number' max='100' value='100'>
<output id='view'>0</output><br>
<input id='range' type='range' min='0' max='100' value='0'>
</form>

Change input to uppercase without the cursor jumping to the end of the text

I'm using the following code to change my input value to uppercase:
<script>
function uppercase(z){
v = z.value.toUpperCase();
z.value = v;
}
</script>
<input type="text" id="example" onkeyup="uppercase(this)">
The problem is that when I type something in the middle of the text, the cursor jumps to the end of it. Searching on Google I tried to following code but it didn't work at all:
function uppercase(z){
document.getElementById(z).addEventListener('input', function (e) {
var target = e.target, position = target.selectionStart; // Capture initial position
target.value = target.value.replace(/\s/g, ''); // This triggers the cursor to move.
v = z.value.toUpperCase();
z.value = v;
target.selectionEnd = position; // Set the cursor back to the initial position.
});
}
The first code is working fine, but I still don't know how to prevent the cursor from jumping.
You can also set the cursor position onkeyup (or whatever you are using, as long you get a reference to the input element)
function withSelectionRange() {
const elem = document.getElementById('working');
// get start position and end position, in case of an selection these values
// will be different
const startPos = elem.selectionStart;
const endPos = elem.selectionEnd;
elem.value = elem.value.toUpperCase();
elem.setSelectionRange(startPos, endPos);
}
function withoutSelectionRange() {
const elem = document.getElementById('notWorking');
elem.value = elem.value.toUpperCase();
}
<div style="display: flex; flex-direction: column">
<label for='working'>Uppercase text with selection range</label>
<input id='working' type='text' onkeyup="withSelectionRange()"></input>
<label for='notWorking'>Uppercase text input without selection range</label>
<input id='notWorking' type='text' onkeyup="withoutSelectionRange()"></input>
</div>
Link to codepen
You can achieve this by simply adding some CSS styling:
#example {
text-transform: uppercase;
}
This will make all the letters in the input field appear as uppercase, but the value would still be the same. If you need the value to be uppercase, transform it to uppercase the moment you need it (right before a submit for example)
I have been searching hours after hours for an solution for this same issue.
Adding CSS did the trick for me, except there is a specific requirement that our backend api only accepts upper-cased string.
So besides:
#example {
text-transform: uppercase;
}
I also added callbacks that listen to onBlur and keydown.enter and converts the input value to upper case when those events get triggered.
P.S.:
No sample code as I'm just sharing my thoughts for people who had the same headaches and doesn't want to hack on HTMLInputElement.setSelectionRange.

Restrict character/string/word in input

My function, addthisTxt, is not checking the length. It should not exceed 11. Below is what I have tried so far; I'd like to keep on adding text until it reaches the max length, otherwise it should restrict the user from adding more.
HTML
<input type="checkbox" name="chkbxr" value="add this offer on wrapper"
(change)="addthisTxt($event.target.getAttribute('txt'), $event.target.checked)">
JavaScript
addthisTxt(txt, checked): void {
if (checked) {
if((this.v.offerName.length +txt.length) >= 55){
this.v.offerName = this.v.offerName + txt;
}
this.v.offerName = this.v.offerName;
}
}
You are setting the value on this.v.offerName. The UI element is not bound to this JavaScript variable and you need to set the value of the UI input element to restrict the value.

HTML5 : Input field with the type of 'number' will still accept higher value than its 'max' using keyboard inputs

Hello guys need some help here. i want to have limit the numbers inputted in my input field by putting max attribute to it. i have no problem with that until i use my keyboard to input data on it. seems like the max attribute is not filtering the input coming from the keyboard.
e.g
<input type="number" max="5" />
i can't go until 6 using the up and down arrow but when i manually put 6 using keyboard it's accepts it. how can i prevent? thank you
You would need to use JavaScript to do it. This will not let the user enter a number higher than 5:
<input type="number" max="5" onkeyup="if(this.value > 5) this.value = null;">
Another possible solution is to completely block the keyboard input by replacing onkeyup=".." event in the code above with onkeydown="return false".
have no problem with that until i use my keyboard to input data on it.
seems like the max attribute is not filtering the input coming from
the keyboard
This is how HTML5 validation/constraint work. However, it will invalidate when the form submits. Alternatively, you can validate it yourself. To validate yourself, you need to wire up Javascript and call the checkValidity() on the input element.
checkValidity() of the constraints API will check the validity state of the element and will return the state of whether the input element validate or not. This will also set the validity object on the input so that you can query more details.
Ref: https://html.spec.whatwg.org/multipage/forms.html#constraints and https://html.spec.whatwg.org/multipage/forms.html#form-submission-algorithm
You can also use the :invalid selector in CSS to highlight invalid inputs.
Example Snippet:
var input = document.getElementById('test'),
result = document.getElementById('result');
input.addEventListener('blur', validate);
function validate(e) {
var isValid = e.target.checkValidity();
result.textContent = 'isValid = ' + isValid;
if (! isValid) {
console.log(e.target.validity);
}
}
input[type=number]:invalid {
border: 2px solid red;
outline: none;
}
<label>Enter value and press tab: </label><br/>
<input id="test" type="number" min="1" max="10" />
<hr/>
<p id="result"></p>
You can use javascript to restrict the maximum input value to 5.
HTML
using oninput as a event handler
<input type="number" max="5" oninput="checkLength(this)" />
JS
function checkLength(elem) {
// checking if iput value is more than 5
if (elem.value > 5) {
alert('Max value is 5')
elem.value = ''; // emptying the input box
}
}
DEMO
An Utility Function to Solve Two Problem
Problem 1: Limit user input to maximum n digit
For this use n number of 9 as max parameter. As an example if you want to limit user input in 4 digit then max param value will be 9999.
Problem 2: Limit user input at a maximum value
This is intuitive. As an example If you want restrict the user input to maximum 100 then max param value will be 100.
function getMaxInteger(value, max) {
if(!value) return;
if( parseInt(value) <= max ) {
return value;
}
return getMaxInteger(value?.substr(0, value?.length-1), max);
}
function maxInt(value, max) {
return getMaxInteger(value?.replace(/\D/,''), max);
}
Use this maxInt method on input change handler
ngModelChange for Angular
onChange for React
v-on:change or watch for Vue
onkeyup="if(this.value > <?=$remaining?>) this.value = null; else if(this.value < 1) this.value = null;"

Categories