How to use keypress event correctly - javascript

I have this code working perfectly fine with mouse click events.
var nodes = document.querySelectorAll('.myclass');
for(var i=0; i < nodes.length; i++){
var node = nodes[i];
node.addEventListener('click', function () {
if(this.className == 'sample') {
document.getElementById("massage").innerHTML="Hello";
}
}, false);
}
Now if I just replace 'click' with 'keypress' all small letters, then the event is not triggered. What is the correct approach to do it.
And If I want to use Lambda's then what is the correct approach for the same task?
document.querySelectorAll('.myclass').addEventListener('mouseover', () => {
document.addEventListener('keypress', ({ key }) => {
if(this.className == 'sample') {
document.getElementById("massage").innerHTML="Hello";
}
});
});

MDN Documentation says do not use keypress any more. Also says it does not work for all key presses. Also it is for KEY press, not mouse CLICK. Different events!

Why are you using keypress for mouse?
This is what i got from your snippet
let el = document.querySelectorAll('.myclass');
let massage = document.getElementById('massage');
for(let i = 0; i < el.length; i++){
el[i].addEventListener('mouseover', e => {
e.target.classList.add('is-hover');
})
el[i].addEventListener('mouseout', e => {
e.target.classList.remove('is-hover');
})
}
document.body.addEventListener('mousedown', () => {
for(let i = 0; i < el.length; i++){
if (el[i].classList.contains('sample') && el[i].classList.contains('is-hover')){
massage.innerHTML = 'Hello';
}
}
})

Related

Dot pagination with pure JavaScript

i m very much new to programming with JavaScript and would love to gain more experience, my problem isn't actually a problem, it more like of optimizing a code, I've been working on making what so called a "pagination" a dot navigation. you can find my code example in this code pen https://codepen.io/Tarek-Chentouf/pen/ajqXpW . My code goes as follow:
"use strict";
var buttons = document.querySelectorAll('.button__outline');
function reset() {
for (let i = 0; i < buttons.length; i++) {
buttons[i].classList.remove('active');
}
}
function addActive() {
for (let i = 0; i < buttons.length; i++) {
buttons[i].addEventListener('click', function() {
if (i == 0) {
reset();
buttons[0].classList.add('active');
}
if (i == 1) {
reset();
buttons[1].classList.add('active');
}
if (i == 2) {
reset();
buttons[2].classList.add('active');
}
if (i == 3) {
reset();
buttons[3].classList.add('active');
}
});
}
}
addActive();
my Question goes as follow is there a better way to achieve the same result without having to repeat the if statement?.
Thank you all in advance.
For the general case, you could simply access buttons[i] instead of if (i == 0) ... buttons[0] ... if (i == 1) ... buttons[1] ...:
function addActive() {
for (let i = 0; i < buttons.length; i++) {
buttons[i].addEventListener('click', function() {
reset();
buttons[i].classList.add('active');
});
}
}
But you could make the code cleaner and DRY-er with a forEach - instead of accessing an index of the buttons collection, abstract the button being iterated over into a variable of its own:
function addActive() {
buttons.forEach(button => {
button.addEventListener('click', () => {
reset();
button.classList.add('active');
});
});
}
Or, as Patrick Roberts suggested, you might move all the classList changes into the reset function and use event delegation on the container (that way you only need one listener, rather than many):
document.querySelector('.container').addEventListener('click', ({ target }) => {
if (!target.matches('.button__outline')) return;
reset(target);
});
const buttons = document.querySelectorAll('.button__outline');
function reset(showButton) {
buttons.forEach(button => {
button.classList.remove('active');
})
showButton.classList.add('active');
}
reset(buttons[0]);

I have repeated JS code, how do I turns these into functions

Here is the JSFiddle link for my project, you can see the full app.js here... https://jsfiddle.net/be4pLh7s/1/
Basically I am trying to build a contact diary where you can create new contacts and then be able to edit or delete them at a later stage...
on line 85 onwards is the click event for the Edit and Delete buttons, I THINK that the way I have written this part of the code maybe incorrect because I have repeated loops and if statements.
I have tried putting these repeated parts of the code into functions but then the app crashes and I receive different errors. I have tried a couple of things to overcome these errors but still cant get it to work and or get my head around this.
Please could you advise if the code is correct... have I repeated myself? If I have then please can you show/tell me how to make this code better in terms of DRY, and readability. Thank you.
How do I code separate functions for the "Edit", "Save" and "Delete" buttons.
This line of code is repeated 3 times -
for (var i = 0; i < contactsBook.length; i++) {
if (contactsBook[i].firstName === ul.getAttribute('data-person')) {}
This line of code is repeated 2 times -
const ulChild = ul.childNodes;
for (var j = 0; j < ulChild.length; j++) {
if (ulChild[j].tagName === "LI") {}
Here is the part of the code in question -
//Click event for Edit and Delete buttons.
contacts.addEventListener("click", (e) => {
if (e.target.tagName === "BUTTON") {
const button = e.target;
const ul = button.parentNode;
if (button.textContent === "Edit") {
for (var i = 0; i < contactsBook.length; i++) {
if (contactsBook[i].firstName === ul.getAttribute('data-person')) {
const ulChild = ul.childNodes;
for (var j = 0; j < ulChild.length; j++) {
if (ulChild[j].tagName === "LI") {
const items = ulChild[j];
const input = document.createElement('input');
input.type = 'text';
input.value = items.textContent;
items.textContent = "";
items.insertBefore(input, ulChild.childNodes);
button.textContent = 'Save';
};
};
};
};
} else if (button.textContent === "Save") {
for (var i = 0; i < contactsBook.length; i++) {
if (contactsBook[i].firstName === ul.getAttribute('data-person')) {
const ulChild = ul.childNodes;
for (var j = 0; j < ulChild.length; j++) {
if (ulChild[j].tagName === "LI") {
console.log(ulChild[j]);
};
};
};
};
} else if (button.textContent === "Delete") {
contacts.removeChild(ul);
for (var i = 0; i < contactsBook.length; i++) {
if (contactsBook[i].firstName === ul.getAttribute('data-person')) {
contactsBook.splice(i,1);
localStorage.setItem('addbook', JSON.stringify(contactsBook));
};
};
};
};
});
Thank you for your help!
Not much of repeated code. Only hint I could give is if you want to use more javascrpt flare, e.g. .forEach instead of standard for loop and .filter since you have a for and if statement.
For an example
for (var i = 0; i < contactsBook.length; i++) {
if (contactsBook[i].firstName === ul.getAttribute('data-person'))
....
Can be done with:
contactsBook.filter(book => book.firstName === ul.getAttribute('data-person'))
Not that it makes any difference.

Pass Argument to EventListener function

I'm building a simple calculator app. I'm trying to accomplish three things:
Assign an event listener to the buttons.
When the button is clicked, fire an event.
Use the eventListener function to display the value of the clicked button.
for (i = 0; i < btn.length; i++) {
var btnVal = btn[i].value;
btn[i].addEventListener("click", function() { displayNumber(btnVal) }, false);
}
function displayNumber(param) {
displayedNum.innerHTML = param;
}
It seems btnVal is not accessible when passed to the event listener function.
The assignment won't work like that. Instead, use the target's value
for (i = 0; i < btn.length; i++) {
btn[i].addEventListener("click", function(e) {
displayNumber(e.target.value)
}, false);
}
Defining event listeners (or other asynchronous things) inside of a loop is tricky. You may think you're creating several different btnVal variables, one for each time through the loop, but you're not. That var btnVal gets hoisted to the top and reused, so your code ends up behaving like this:
var btnVal;
for (i = 0; i < btn.length; i++) {
btnVal = btn[i].value;
btn[i].addEventListener("click", function() { displayNumber(btnVal) }, false);
}
So all of your event listeners are interacting with the very same variable, and when they eventually get clicked, they'll only see the very last value that was assigned to btnVal, which should be btn[btn.length -1].value. All the values earlier in the array are lost.
There are a few ways you can address this:
1) Rather than depending on a closure variable, you could pull it from the element itself when the event goes off.
for (i = 0; i < btn.length; i++) {
btn[i].addEventListener("click", function (event) {
displayNumber(event.target.value);
});
}
2) Move the event listener creation into a function, and pass btnVal in. Since it's now a function parameter, it gets a new binding.
for (i = 0; i < btn.length; i++) {
createListener(btn[i], btn[i].value);
}
function createListener(element, val) {
element.addEventListener("click", function () { displayNumber(val) }, false);
}
3) you can do it inline using an IIFE.
for (i = 0; i < btn.length; i++) {
(function (button) {
button.addEventListener("click", function () { displayNumber(button.value) }, false);
})(btn[i]);
}
EDIT: added option 4
4) If you can use ES2015, use let. Let has block scope, and will get a new binding each time through the loop
for (i = 0; i < btn.length; i++) {
let btnVal = btn[i].value;
btn[i].addEventListener("click", function() { displayNumber(btnVal) }, false);
}
You cant use btnVal inside the event.
It should look like this
for (i = 0; i < btn.length; i++) {
btn[i].addEventListener("click", function(event) {
var clickedButton = event.target || event.srcElement;
console.log(clickedButton.value)
}, false);
}

when I quit element, and return after, behavior not same. Probably event.target

I've with inspect with my navigator my script and I finded the problem but I can't resolve this.
When, I've click on my element, he open a list, if click outside of this dropdown (On the document.body) and once again I click on this dropdown I can't selected a new value on my list. I think it's probably on my object.
let open = document.querySelectorAll('.sub-container div');
for (let loop = 0; loop < open.length; loop++) {
open[loop].addEventListener('click', () => {
let tabLi = {};
let list = open[loop].querySelectorAll('.tryit li')
let cross = open[loop].querySelector('h3 .clear-filters');
for (let l = 0; l < list.length; l++) {
list[l].addEventListener('click', (e) => {
e.stopPropagation();
if (!list[l].classList.contains('active-dropdown')) {
list[l].classList.add('active-dropdown');
tabLi[l] = list[l];
console.log(tabLi)
cross.style.display = 'block';
} else {
list[l].classList.remove('active-dropdown');
delete tabLi[l];
if (Object.keys(tabLi).length === 0) {
cross.style.display = 'none';
}
}
});
cross.addEventListener('click', (e) => {
e.stopPropagation()
list[l].classList.remove('active-dropdown');
delete tabLi[l];
cross.style.display = 'none';
})
}
})
}

Carry values from an attribute on multiple elements to another attribute

I have this JavaScript which detects whether the browser supports touch events, and then sets a touch event with every element that has a click event:
function isTouchEnabled() {
if ('ontouchstart' in window) {
var all = document.querySelectorAll('[onclick]');
for (var i = 0; i < all.length; i++) {
all[i].setAttribute('ontouchstart', car);
var car = all[i].getAttribute('onclick');
}
}
}
This works up until this bit: all[i].setAttribute('ontouchstart', car);
With this bit, I aimed to carry the attribute value from the click event to the touch event, but this does not work, because the touch event attribute value ends up being undefined. Can anybody solve this ?
Try this:
function hitMe() {
alert('I am hit !');
}
function isTouchEnabled() {
if ('ontouchstart' in window) {
var all = document.querySelectorAll('[onclick]');
for (var i = 0; i < all.length; i++) {
var car = all[i].getAttribute('onclick');
all[i].setAttribute('ontouchstart', car);
}
}
}
isTouchEnabled();
<div onclick="hitMe()">Click Me!</div>

Categories