State updating for onkeyup, but not for onclick in React - javascript

I am working on an app that uses both onkeyup and onclick events. onkeyup is working as expected, however, onclick is not working as expected. See the code below...
//the state
const [currentGuess, setCurrentGuess] = useState("");
//the callback function
const handleKeyup = (e) => {
let pressedKey = "";
if (typeof e == "string") {
pressedKey = e;
}
const checkPressedKey = pressedKey ? pressedKey : e.key;
setCurrentGuess((prev) => prev + checkPressedKey);
console.log(currentGuess.length);
}
//triggering callback when user types on keyboard
useEffect(() => {
window.addEventListener("keyup", handleKeyup);
return () => window.removeEventListener("keyup", handleKeyup);
}, [handleKeyup]);
//trigger the callback when user types in the fake keyboard
useEffect(() => {
setKeyOnClick();
}, []);
function setKeyOnClick() {
const allkey = document.querySelectorAll(".key");
allkey.forEach((btnKey) => {
btnKey.addEventListener("click", function () {
handleKeyup(btnKey.dataset.key);
});
});
}
***for onKeyup**
output: 0, 1, 2, 3, 4 ....
***for onclick**
output: 0 (it doesn't increase);
-HTML-
<button className="key" data-key="Q">Q</button>
<button className="key" data-key="W">W</button>
<button className="key" data-key="E">E</button>
<button className="key" data-key="R">R</button>
<button className="key" data-key="T">T</button>
<button className="key" data-key="Y">Y</button>
As you can see above i have to pass the dataset-key in the handlekeyup function for onclick event.

Your buttons have className="key", so if you want to get elements by class name you have to use a dot in querySelectorAll, try this:
const allkey = document.querySelectorAll(".key");

Related

document.addEventListener in function not working

I'm trying to hide the "modal" box when the user press Esc key.
So, I first check where the box contains class - 'hidden', which
technically hide the box in UI.
Then if it's not hidden (the box does not contain class - 'hidden') and
appearing on screen, the function will wait for the Esc key for the
box to be disappeared.
Showing and hiding the box parts working just fine, but document.addEventListener part is not working.
const btnopenModal = document.querySelectorAll('.show-modal');
const btnCloseModal = document.querySelector('.close');
const overlay = document.querySelector('.overlay');
const modal =document.querySelector('.modal');
const showModal = function() {
modal.classList.remove('hidden');
overlay.classList.remove('hidden');
};
const hideModal = function() {
modal.classList.add('hidden');
overlay.classList.add('hidden');
}
for(let i = 0; i < btnopenModal.length; i++)
btnopenModal[i].addEventListener('click', showModal);
btnCloseModal.addEventListener('click', hideModal);
overlay.addEventListener('click', hideModal);
if(!overlay.classList.contains('hidden')) {
document.addEventListener('keypress', function(e) {
console.log(e.key);
if(e.key === 'Escape') {
hideModal();
}
})
};
Any other way around for this to work?
I would think that your if statement is evaluated when the webpage first runs, and my guess is that the if statement evaluates to false as it probably does contain the class "hidden" at first. I don't understand why you put it the key handler inside of an if statement, if it is for safety you should put it inside your function like so:
document.addEventListener('keypress', function(e) {
if(!overlay.classList.contains('hidden')) {
console.log(e.key);
if(e.key === 'Escape') {
hideModal();
}
};
})
Move if condition into callback. You want to always add keypress listener, just do not execute hideModal() if !overlay.classList.contains('hidden')
const btnopenModal = document.querySelectorAll('.show-modal');
const btnCloseModal = document.querySelector('.close');
const overlay = document.querySelector('.overlay');
const modal =document.querySelector('.modal');
const showModal = function() {
modal.classList.remove('hidden');
overlay.classList.remove('hidden');
};
const hideModal = function() {
modal.classList.add('hidden');
overlay.classList.add('hidden');
}
for(let i = 0; i < btnopenModal.length; i++)
btnopenModal[i].addEventListener('click', showModal);
btnCloseModal.addEventListener('click', hideModal);
overlay.addEventListener('click', hideModal);
document.addEventListener('keypress', function(e) {
console.log(e.key);
if(e.key === 'Escape' && !overlay.classList.contains('hidden')) {
hideModal();
}
});

Enter key for Route another page

I have a input for searchbox. I must make like; Write my words fors search then after i press enter it must need go another page with input value. So i can access that value with query string. So how can i route another page with value of input after i press enter ? Thank you for help! I Just add That codes for catch enter press.
useEffect(() => {
const listener = (event) => {
if (event.code === "Enter" || event.code === "NumpadEnter") {
alert("Enter key was pressed. Run your function.");
event.preventDefault();
}
};
document.addEventListener("keydown", listener);
return () => {
document.removeEventListener("keydown", listener);
};
}, []);
You don't necessarily have to set an event listener, using onKeyDown event handler will also do. Enter key has a code of 13, so we just have to detect that.
Keep your value in a state (here, myValue), detect that you've pressed Enter key (here, using keyPressHandler method), and finally, pass the parameter to your route.
import {useHistory} from "react-router-dom"
function App() {
let history = useHistory();
const [myValue, setMyValue] = useState("");
const handleChange = ({ target: { value } }) => {
setMyValue(value);
};
const keyPressHandler = (e) => {
if (e.which === 13) {
// alert("You pressed enter!");
history.push("/process/" + myValue);
}
};
return (
<div className="App">
<input value={myValue} onKeyDown={keyPressHandler} onChange={handleChange} />
</div>
);
}
UPDATE:
According to MDN Web Docs, e.which is non-standard [Source] and e.keyCode is deprecated [Source], so you should be using e.key instead like:
const keyPressHandler = (e) => {
if (e.key=== 'Enter') {
// alert("You pressed enter!");
history.push("/process/" + myValue);
}
};
Working CodeSandbox Link

JavaScript function cannot recognise input.checked elment correctly

I have a problem with my "CheckCheck" function. The following part of the code should generate a to-do task. The input tag dynamically created in JS provides an option to set the priority to the given task. There is an option to set the task to "normal" or "priotity". However, the code sets the fisk task to "on" and after continues with the imposed "priority" and "normal" but inversely. How to prevent this from happening?
The code:
let tasklist = [];
function Apply() {
const Tasktask = document.querySelector(".task-form");
const Taskdate = document.querySelector(".date");
const Taskpriority = document.querySelector(".check-box");
function Prevent() {
if (Tasktask.value.length === 0 || Taskdate.value === "") {
alert("Fields cannot be empty!");
} else {
Pushed();
render();
clear();
}
}
Prevent();
function Pushed() {
let newTasks = new Tasks(Tasktask.value, Taskdate.value, Taskpriority.value);
tasklist.push(newTasks);
updateLocalStorage();
}
function render() {
CheckCheck();
insertTd();
}
function CheckCheck() {
if (Taskpriority.checked === true) {
Taskpriority.value = "priority"
} else {
Taskpriority.value = "normal"
}
}
function clear() {
Tasktask.value = "";
Taskdate.value = "";
Taskpriority.checked = false;
}
function insertTd() {
checkLocalStorage();
const parent2 = document.querySelector(".table-body");
parent2.innerHTML = "";
tasklist.forEach((item) => {
const table = `<tr>
<td>${item.task}</td>
<td>${item.date}</td>
<td>${item.priority}</td>
<td><a class="delete">delete</a></td>
</tr>`;
parent2.insertAdjacentHTML("afterbegin", table);
});
}
function deleteItem() {
const Table = document.querySelector("table").addEventListener("click", (e) => {
const currentTarget = e.target.parentNode.parentNode.childNodes[1];
if (e.target.innerHTML == "delete") {
if (confirm(`Are you sure you want to delete ${currentTarget.innerText}?`))
deleteTask(findTask(tasklist, currentTarget.innerText));
}
if (e.target.classList.contains("status-button")) {
findTask(tasklist, currentTarget.innerText);
}
updateLocalStorage();
insertTd();
});
}
deleteItem();
function deleteTask(currentTask) {
tasklist.splice(currentTask, currentTask + 1);
}
function findTask(taskArray, task) {
if (taskArray.length === 0 || taskArray === null) {
return;
}
for (let item of taskArray)
if (item.task === task) {
return taskArray.indexOf(item);
}
}
}
The other thing which is not working as intended is the confirm prompt. The more tasks I add, the more confirm prompts I get. I.e. for 1 task it is only one confirm window, for 3 tasks - 3 windows etc. Why is that?
I also attach below a JSFiddle link how better understanding.
Link
Thanks in advance for answers.
You don't get the state of a checkbox by reading its value but its checked property. Try document.querySelector('.check-box').checked
You keep reusing the same buttons and add an event listener to them each time. Either clone them every time, or add the listener once right after creating them.
Simple illustration of the problems here
document.querySelector('#readstates').addEventListener('click', e => {
e.preventDefault();
const disp = `Checked\n 1: ${chk1.checked}, 2: ${chk2.checked} \n`
+ `Value\n 1: ${chk1.value}, 2: ${chk2.value}`;
alert(disp);
});
const spawnBut = document.createElement('button');
spawnBut.id = 'spawned';
spawnBut.textContent = 'Spawned';
document.querySelector('#spawnDirty').addEventListener('click', e => {
const previous = document.querySelector('form #spawned');
if (previous) previous.remove();
document.querySelector('#spawnHere').append(spawnBut);
spawnBut.addEventListener('click', e => {
e.preventDefault();
alert('click!');
});
});
document.querySelector('#spawnClone').addEventListener('click', e => {
const previous = document.querySelector('form #spawned');
if (previous) previous.remove();
const nSpawnBut = spawnBut.cloneNode(true);
document.querySelector('#spawnHere').append(nSpawnBut);
nSpawnBut.addEventListener('click', e => {
e.preventDefault();
alert('click!');
});
});
<form>
<p class="inputs">
<label for="chk1">chk1:</label> <input type="checkbox" id="chk1" />
<label for="chk2">chk2:</label> <input type="checkbox" id="chk2" value="mycheckedvalue" />
<button id="readstates">Read chks</button>
</p>
<p class="button-spawners">
Try spamming those then click below:
<button type="button" id="spawnDirty"
title="Each time you click this one, the button below is respawned and a new handler is attached">
Spawn button
</button>
<button type="button" id="spawnClone"
title="This one will spawn a clone each time, so the click handler is attached only once">
Spawn button clone
</button>
</p>
<p id="spawnHere">
New button will spawn here
</p>
</form>

how to identify which button clicked in react?

I have two buttons that execute the same function, and that function needs to evaluate go backward or go forward as I show in the following image
and in the following code I want to evaluate what function next() or prev() to execute depending on which button was touched, this is built in react
const onFinish = (values) => {
if (values.users) {
const Operator = values.users.map((item) => ({
ruleAttributeName: item.ruleAttributeName,
isoAttributeId: item.isoAttributeId,
ruleOperationId: item.ruleOperationId,
mandatoryValue: item.mandatoryValue,
}));
setAttributes(Operator);
}
if (values.users) {
const attributesSave = values.users.map((item) => ({
ruleAttributeName: item.ruleAttributeName,
isoAttributeEntity: {
isoAttributeId: item.isoAttributeId,
},
ruleOperationEntity: {
ruleOperationId: item.ruleOperationId,
},
mandatoryValue: item.mandatoryValue,
}));
console.log('mandatory';
setAttributesSave(attributesSave);
setMandatoryValue();
next();
prev();
};
and in this form I pass by parameter the function
<Form
name='dynamic_form_nest_item'
onFinish={onFinish}
autoComplete='off'
>
You can identify them by assigning unique Id to both button like this
<button onClick={onFinish} Id={'1'}>Next</button>
<button onClick={onFinish} Id={'2'}>Next</button>
And in you listener check id which button clicked.
const onFinish = (event) => {
Let id = event.target.id;
if(id=== "1") {
// Do for one
} else {
// For second
}
}
I don't know the parameters that you got on that custom buttons and how is it triggering onClick event. But here is the solution for HTML buttons.
You can set a value to the button like this.
<button onClick={onFinish} value="next">Next</button>
const onFinish = (ev) => {
ev.preventDefault() // this is to prevent the refresh
const { value } = ev.target // equals with const value = ev.target.value
if(value === "next") {
next()
} else {
prev()
}
}

Hide popup box function reverting open popup box function

I want to make a "popup" box activated by a button that reduces the opacity of all other elements. When the user clicks out of the box, it should disappear and the opacity should go back to normal. However, these two functions are conflicting with each other. It requires me to click the button TWICE in order for showBox() to be called. And clicking out of the box does nothing unless I reinvoke hideOnClickOutside(document.querySelector('div')); in the browser's console.
Why do I have to click "New Audio" twice and why does hideOnClickOutside() not work unless reinvoked?
function showBox() {
document.body.style.opacity = "0.5";
document.querySelector('div').style.display = "block";
}
document.querySelector('button').addEventListener('click', showBox);
const isVisible = elem => !!elem && !!(elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length); // source (2018-03-11): https://github.com/jquery/jquery/blob/master/src/css/hiddenVisibleSelectors.js
function hideOnClickOutside(element) {
const outsideClickListener = event => {
if (!element.contains(event.target) && isVisible(element)) { // or use: event.target.closest(selector) === null
element.style.display = 'none';
removeClickListener()
document.body.style.opacity = "1";
}
}
const removeClickListener = () => {
document.removeEventListener('click', outsideClickListener)
}
document.addEventListener('click', outsideClickListener)
}
hideOnClickOutside(document.querySelector('div'));
<button>New Audio</button>
<div style="display: none">
<button>Record Directly</button>
</div>
hideOnClickOutside() function was taken from another StackOverflow answer
Edit
I figured out that it requires two clicks because on the first click, showBox() is called, but immediately after, so is outsideClickListener, and at this point the element is NOW visible AND the user has clicked "outside" the element. This reverts the style changes of showBox().
The easiest fix is to store the reference to the "New Audio" button and check to see if that is the target of the click on document. If so, then return from the function without updating DOM.
const button = document.querySelector('button')
button.addEventListener('click', showBox);
// ..
function hideOnClickOutside(element) {
const outsideClickListener = event => {
if (event.target === button) return
// ..
Keep in mind, with the current code you have, the hideOnClickOutside function only gets until the first time isVisible is true and the target is not button, since you remove the event listener on that condition.
function showBox(e) {
document.body.style.opacity = "0.5";
document.querySelector('div').style.display = "block";
}
const button = document.querySelector('button')
button.addEventListener('click', showBox);
const isVisible = elem => !!elem && !!(elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length); // source (2018-03-11): https://github.com/jquery/jquery/blob/master/src/css/hiddenVisibleSelectors.js
function hideOnClickOutside(element) {
const outsideClickListener = event => {
if (event.target === button) return
if (!element.contains(event.target) && isVisible(element)) { // or use: event.target.closest(selector) === null
element.style.display = 'none';
removeClickListener()
document.body.style.opacity = "1";
}
}
const removeClickListener = () => {
document.removeEventListener('click', outsideClickListener)
}
document.addEventListener('click', outsideClickListener)
}
hideOnClickOutside(document.querySelector('div'));
<button>New Audio</button>
<div style="display: none">
<button>Record Directly</button>
</div>
The other problem is that once the showBox function is called, you actually probably want the button to be considered outside. Let's refactor your code to store references to the showButton and box, add a flag to disable the showButton and only add the event listener to the document if the showButton is clicked and remove the event listener only when the box is displayed.
You can later refactor this to fit your particular use case. The idea is to think of the various states this application can be in and create functions to manage that state.
const box = document.querySelector('#box');
const showButton = document.querySelector('#show-button');
showButton.addEventListener('click', showBox);
let isDisabled = false;
const isVisible = elem => !!elem && !!(elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length); // source (2018-03-11): https://github.com/jquery/jquery/blob/master/src/css/hiddenVisibleSelectors.js
function toggleDisabled(bool) {
showButton.attributes.disabled = bool;
isDisabled = bool;
}
function toggleDisplay(display, opacity) {
document.body.style.opacity = opacity;
box.style.display = display;
}
function showBox(event) {
if (!isDisabled) {
event.preventDefault();
event.stopPropagation();
toggleDisplay("block", 0.5);
toggleDisabled(true);
document.addEventListener('click', outsideClickListener);
}
}
function outsideClickListener(event) {
if (!box.contains(event.target) && isVisible(box)) { // or use: event.target.closest(selector) === null
toggleDisplay("none", 1);
toggleDisabled(false);
document.removeEventListener('click', outsideClickListener)
}
}
<button id="show-button">New Audio</button>
<div id="box" style="display: none">
<button>Record Directly</button>
</div>

Categories