I need some help with a school assignment. I have two buttons, one for increment and one for decrement. I have four functions, where two of them is for clicking and two for pressing arrow keys. Clicking works fine, but to use arrowUp and ArrowDown i need to click and select the button with the mouse once for it to work. Can i add something to my two lower arrow key functions so that i dont need to click them once before i can start using arrow keys?
let element = document.querySelector('#value')
let buttonIncrease = document.querySelector('#increase')
element.textContent = '1'
let buttonDecrease = document.querySelector('#decrease')
buttonDecrease.disabled = true;
//Increment number by 1 on click
buttonIncrease.addEventListener('click', () => {
element.textContent = Number(element.textContent) + 1
if (element.textContent > 1) {
buttonDecrease.disabled = false;
}
})
//Decrement number by 1 on click
buttonDecrease.addEventListener('click', () => {
element.textContent = Number(element.textContent) - 1
if (element.textContent < 2) {
buttonDecrease.disabled = true;
}
})
//Increment number by 1 on keydown
buttonIncrease.addEventListener('keydown', (up) => {
if (up.key === 'ArrowUp' ) {
element.textContent = Number(element.textContent) + 1
}
if (element.textContent > 1) {
buttonDecrease.disabled = false;
}
})
//Decrement number by 1 on keydown
buttonDecrease.addEventListener('keydown', (down) => {
document.getElementById('decrease')
if (down.key === 'ArrowDown') {
element.textContent = Number(element.textContent) - 1
}
if (element.textContent < 2) {
buttonDecrease.disabled = true;
}
})
you dont need to define muse click on increment and decrement buttons
but to use arrowUp and ArrowDown i need to click and select the button
with the mouse once for it to work.
you can detect whene arrowUp and arrowDown clicked by add event lisner directly to keyboard:
addEventListener("keydown", function (e) { // Do something }
To do what you require you can attach the keydown event handler to the window, so that no matter what element in the DOM has focus, so long as the event can bubble to the window element, the value will be changed.
Also note that you can combine the keydown event handlers in to one, and you can also make the code more succinct and DRY by extracting the repeated logic in to functions:
let buttonIncrease = document.querySelector('#increase');
let buttonDecrease = document.querySelector('#decrease');
let element = document.querySelector('#value');
buttonDecrease.disabled = true;
element.textContent = '1';
const setButtonState = () => buttonDecrease.disabled = parseInt(element.textContent, 10) <= 1;
const updateValue = increment => {
value.textContent = Math.max(1, Number(element.textContent) + increment);
setButtonState();
}
buttonIncrease.addEventListener('click', () => updateValue(1))
buttonDecrease.addEventListener('click', () => updateValue(-1))
window.document.addEventListener('keydown', e => {
e.preventDefault();
let value = Number(element.textContent);
let increment = e.key === 'ArrowUp' ? 1 : e.key === 'ArrowDown' ? -1 : 0;
updateValue(increment);
})
#value {
padding: 3px;
}
<button id="decrease">-</button>
<span id="value">1</span>
<button id="increase">+</button>
Related
So I am trying to building a simple OPT forum which I have build but now I want to add reverse functionality for example when user press backspace input should focus on previous element.
const inputs = document.querySelectorAll('.input input')
inputs.forEach((e, i) => {
e.dataset.index = i;
e.addEventListener("input", () => {
if (e.value.length >= 1) {
if (e.dataset.index >= i && inputs.length - 1 > i) {
e.nextElementSibling.focus();
}
} else {
if (inputs.length - 1 > i || e.dataset.index < inputs.length) {
e.previousElementSibling.focus();
}
}
})
})
You would have to detect the press of the backspace key.
It looks like you want each input to take exactly one character. In that case I would suggest that you keep the current character selected, and to make arrow keys also move the focus.
Don't name the input element e, as e is usually used as name for the event object -- just to avoid confusion for someone reading your code.
Here is one way to do it:
const inputs = [...document.querySelectorAll('.input input')];
inputs.forEach((input, i) => {
const previous = () => inputs[i-1]?.focus?.();
const next = () => inputs[i+1]?.focus?.();
input.addEventListener("input", () => {
if (input.value.length > 1) input.value = input.value[0];
if (input.value.length) next();
});
input.addEventListener("keydown", (e) => {
if (e.key == "ArrowRight") next();
if (e.key == "ArrowLeft") previous();
});
input.addEventListener("keyup", (e) => {
if (e.key == "Backspace") {
previous();
e.preventDefault();
}
});
});
inputs[0].focus();
// Ensure that current input has its content selected
document.addEventListener("selectionchange", () => {
const input = document.activeElement;
if (inputs.includes(input)) input.setSelectionRange(0, 1);
});
.input input { width: 1em }
<div class="input">
<input><input><input><input><input>
</div>
Im currently trying to make a website where if i press q the p tag will change from "Q" to "A". This currently works with the code below. However the problem is that when pressing q it needs to go back to "A". I've tried making it work with removeEventListeners but it doesn't seem to work.
<script>
document.addEventListener("keypress", event => {
if (event.key == "q") {
document.getElementById("P1").innerText = "Q"
}
});
</script>
Like #diegod suggested in the comments you should check which character is currently shown in the p element and change the innerText accordingly:
const p1 = document.getElementById("P1");
document.addEventListener("keypress", event => {
if (event.key == "q") {
if (p1.innerText == "A") {
p1.innerText = "Q";
} else {
p1.innerText = "A";
}
}
});
Using ternary operator example:
const p1 = document.getElementById("P1");
document.addEventListener("keypress", event => {
if (event.key == "q") {
p1.innerText = p1.innerText == "A" ? "Q" : "A";
}
});
const $ = (prop) => document.querySelector(prop)
const changeToQ = () => {
$('p').innerText = 'Q'
}
const reset = () => {
$('p').innerText = 'A'
}
document.addEventListener('keydown', (e) => {
if (e.key === 'q') changeToQ()
})
document.addEventListener('keyup', (e) => {
if (e.key === 'q') reset()
})
I think this is what are you looking for
have your tried to make else statement
else{ document.getElementById("P1").innerText = "A"}
I want to disable the button when number is 1, using setAttribue
let element = document.createElement('div')
document.body.appendChild(element)
element.setAttribute('id', 'bet-size')
let text = document.createTextNode('1')
element.appendChild(text)
let click = document.createElement('input')
click.setAttribute('type', 'button')
click.setAttribute('id', 'decrease')
document.body.appendChild(click)
click.disabled = true
click.addEventListener('click', () => {
element.textContent = Number(element.textContent) - 1
if (element.textContent !== 1) {
click.setAttribute('disabled', true)
} else if (element.textContent > 1)
click.setAttribute('disabled', false)
})
Which way would be optimal to remove the attribute disabled when number is > 1?
Since disabled is either true or false, you can just check the value to see if its > 1 or not.
click.addEventListener('click', (e) => {
element.textContent = Number(element.textContent) - 1
e.target.setAttribute("disabled",(Number(element.textContent) - 1 > 1));
})
I'm wondering why my Backspace touch increment my countLetters variable.
I ask him to return the number of characters, without ever manually incrementing the variable. Why does the backspace key increment my variable once, before working properly?
const $textareas = document.querySelectorAll('.js-textarea');
$textareas.forEach(function($textarea) {
$textarea.addEventListener('keydown', function(event) {
let max = 100;
let countLetters = $textarea.value.length;
let $meta = this.nextSibling.nextSibling; // = meta
$meta.innerHTML = countLetters + ' / ' + max;
if (countLetters >= max) {
$textarea.value.toString().substring(0, max);
}
if (event.which != 46) {
return;
}
// Disabled <textarea>
if (countLetters >= max) {
event.preventDefault();
}
});
});
Demo is available on Here's Codepen!
Thanks
Keydown fires before letter was added or removed in case of backspace. Use keyup instead of keydown.
The value of the text area is changed after your keydown handler runs. You could use the input event instead.
Here is an example:
const max = 100;
$textarea.addEventListener('input', function() {
const countLetters = $textarea.value.length;
const $meta = this.nextSibling.nextSibling;
$meta.innerHTML = countLetters + ' / ' + max;
});
$textarea.addEventListener('keydown', function(event) {
if ($textarea.value.length >= max && event.key === 'Backspace') {
event.preventDefault();
}
});
Given a list of divs, I need to be able to use the up and down arrow keys to navigate between items in the list, apply an .ag-menu-option-active class to the currently selected item, and on Enter key, trigger a click on the currently selected list item.
I'm currently able to iterate over the list of items, but on down arrow, it only logs out the second item in the list. As stated above, the user should be able to move up and down the list and have the active class applied to the currently selected item. When the user hits the Enter key, the associated list item should be clicked.
let columnMenuItems = document.querySelectorAll('.ag-menu-option');
document.addEventListener('keydown', e => {
if (e.key === 'ArrowDown') {
for (let i = 0; i < columnMenuItems.length; i++) {
if (columnMenuItems[i].classList.contains('ag-menu-option-active') === true) {
console.log("MENU OPTION SELECTED: ", columnMenuItems[i + 1])
columnMenuItems[i].click();
break;
}
}
}
});
JSFiddle link: link
if(columnMenuItems[i+1] !== undefined){
//add class to next
columnMenuItems[i+1].className= "ag-menu-option ag-menu-option-active"
columnMenuItems[i].className= "ag-menu-option"
}
you can write some other logic to add and remove class but this should work for arrow down.
You were missing the +/- when you are calling .click() method;
Also to prevent the undefined error you should add columnMenuItems-1 in first for loop.
Code should look like this -
if (e.key === 'ArrowDown') {
for (let i = 0; i < columnMenuItems.length-1; i++) {
if (columnMenuItems[i].classList.contains('ag-menu-option-active') === true) {
console.log("MENU OPTION SELECTED: ", columnMenuItems[i + 1]);
columnMenuItems[i].classList.remove('ag-menu-option-active');
columnMenuItems[i + 1].click();
columnMenuItems[i+1].classList.add('ag-menu-option-active');
break;
}
}
}
if (e.key === 'ArrowUp') {
for (let i = columnMenuItems.length - 1; i > 0; i--) {
//console.log("MENU OPTIONS: ", columnMenuItems[i]);
if (columnMenuItems[i].classList.contains('ag-menu-option-active') === true) {
columnMenuItems[i].classList.remove('ag-menu-option-active');
//if (e.key === 'Enter') {
columnMenuItems[i - 1].click();
columnMenuItems[i-1].classList.add('ag-menu-option-active');
//}
break;
}
}
}
Test it here (jsfiddle)