JavaScript 2 onclick Events but just do the first - javascript

I have a JavaScript functions which get run 2 times when I click on one element. Take a look at my code, I only want that the first call will be done not the second also:
<p class="chat" onclick="Open('chat')">
<img class="chatpicture" src="jpg/1.jpg" onclick="Open('user')">
</p>
When I click on the image then also the p element with his onclick function will be run. Because the image onclick is inside the p element which also have onclick.
What is the best way to only let the image onclick run and not at same time also run the p element onclick?

You can try using Event.stopPropagation():
The stopPropagation() method of the Event interface prevents further propagation of the current event in the capturing and bubbling phases. It does not, however, prevent any default behaviors from occurring; for instance, clicks on links are still processed. If you want to stop those behaviors, see the preventDefault() method.
Demo:
function Open(inputVal){
event.stopPropagation();
console.log(inputVal);
}
<p class="chat" onclick="Open('chat')">
<img class="chatpicture" src="jpg/1.jpg" onclick="Open('user')">
</p>

Delegate and test the class
const Open = str => console.log(str);
document.getElementById("container").addEventListener("click", e => {
const tgt = e.target;
if (tgt.classList.contains("chat")) Open('chat');
else if (tgt.classList.contains("chatpicture")) Open('user');
})
img { height:50px; }
<div id="container">
<p class="chat">
Start chat
<img alt"User details" class="chatpicture" src="https://cdn1.iconfinder.com/data/icons/managers-15/494/Untitled-32-512.png">
</p>
<p class="chat">
Start chat
<img alt"User details" class="chatpicture" src="https://cdn1.iconfinder.com/data/icons/managers-15/494/Untitled-32-512.png">
</p>
<p class="chat">
Start chat
<img alt"User details" class="chatpicture" src="https://cdn1.iconfinder.com/data/icons/managers-15/494/Untitled-32-512.png">
</p>
</div>

Related

How to disable an onClick event when another OnClick event is also triggered?

Im trying to disable the
<div className="ContainerTitle1" onClick={() => handleAddComponent()}>
when
<div className="accordion1" onClick={(e) => handleExpandItem(e)}>
is clicked becuase it causes both events to happen. How do I create a function to disable the first event from happening when the second happens?
thanks. Ill attach a photo for more clarity.
return (
<div className="Item1">
<div className="ContainerTitle1" onClick={() => handleAddComponent()}>
<div className="buttonTitleWrapper1">
<p className="Item-Title1">{title}</p>
</div>
<div className="ItemIconsContainer1">
<TypeTag entry={item}></TypeTag>
{item?.category && <CatagoryTag entry={item}></CatagoryTag>}
<div
className="accordion1"
onClick={(e) => handleExpandItem(e)}
>
<img className="arrowIcon" src={AssetExport.BottomIcon}></img>
</div>
</div>
</div>
<div
className="panel1"
style={{
height: !expanded && "0px",
overflow: !expanded && "hidden",
}}
>
<p className="desc">{description}</p>
</div>
</div>
);
};
Welcome to this community! I'm new too, so I'll try to do my best to answer your question:
What's happening is that when you're clicking
<div className="accordion1" onClick={(e) => handleExpandItem(e)}></div>
You're also clicking
<div className="ContainerTitle1" onClick={() => handleAddComponent()}></div>
Because it is his parent, so both get clicked, but your accordion gets firstly fired since it is the closer one to your screen (capturing phase), then their parents get clicked too (it's like your click event getting propagated to the parents) and their event handlers get fired in consequence.
So, what you are looking for is to prevent the event from propagating from the event target (which is the closest element to your screen) to the parent's event handlers, and you can do so by using event.stopPropagation() inside your handleExpandItem(e) handler, e.g
function handleExpandItem(e){
e.stopPropagation();
//Some other stuff you want to do
}
Here's an article about this issue: https://www.freecodecamp.org/news/event-propagation-event-bubbling-event-catching-beginners-guide/
Hope it helps!
In handleExpandItem add e.stopPropagation(). That way the event will not "bubble up" (propagate) to the parent event handler.

I want an event listener for each element inside a card

I have a card div in my application. When the user clicks this card element, it should fire a modal. However, I can't seem to attach the eventlistener for all elements inside the card element.
The card element looks like this:
<div class="card" data-id=${index}>
<div class="card-img-container">
<img class="card-img" src="${thumbnail}" alt="profile picture">
</div>
<div class="card-info-container">
<h3 id="name" class="card-name cap">${firstname} ${lastname}</h3>
<p class="card-text">${email}</p>
<p class="card-text cap">${city}, ${state}</p>
</div>
</div>
How do I create an eventlistener that listen for all the elements inside the card element? Here's what my eventlistener looks like:
document.querySelector('.card').addEventListener('click', (e) => {
let dataId = parseInt(e.target.dataset.id);
let user = users[dataId];
showModal(user);
document.getElementById('modal-close-btn').addEventListener('click', () => {
const modal = document.querySelector('.modal-container');
document.body.removeChild(modal);
});
});
There is no reason to add an event listener for every element in the card. Instead bind an event listener to the .card element. Then use event.currentTarget to get your element.
Remember, target is the element you clicked and currentTarget is the current element in the bubbling process (the element that the event is bound to).
In your example e.target.dataset.id will most likely return undefined because you click on an element inside the .card div. parseInt(undefined) will return NaN and users[NaN] will most likely also return undefined. This means most of the time you will be calling showModal(undefined) unless you click on the card padding (if there is any).
Using currentTarget should resolve the issue.
document.querySelector(".card").addEventListener("click", event => {
console.log("target:", event.target);
console.log("currentTarget:", event.currentTarget);
// this is how you get your ".card" element ^
});
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
<div class="card" data-id=${index}>
<div class="card-img-container">
<img class="card-img" src="${thumbnail}" alt="profile picture">
</div>
<div class="card-info-container">
<h3 id="name" class="card-name cap">${firstname} ${lastname}</h3>
<p class="card-text">${email}</p>
<p class="card-text cap">${city}, ${state}</p>
</div>
</div>
I think you are on track already - though your question is not very clear.
You do not need to bind every element to a listener only capture the parent (in this case ".card") and determine which element was clicked just like you have done.
document.querySelector(".card").addEventlistener("click", function(el){ el.target; //was clicked });
If you really want to bind all child element in the card (not recommended), then
document.querySelector(".card *").addEventListener("click", function(){ });
If this is not what you need then maybe I do not understand the question
IF you need to set the event listener to several elements with the same class, you could use a for loop to loop though each element.. Attached is your code with a for loop around it.
//Create a for loop and loop through all elements that coorespond with (document.querySelectorAll('.card'))
//Sidenote: querySelectorAll() returns an array of all matching elements, so you can simply add an event listsner to each element in that array by it's index or by adding "[i]"
for(let i =0; i < document.querySelectorAll('.card').length; i++){
document.querySelectorAll('.card')[i].addEventListener('click', (e) => {
let dataId = parseInt(e.target.dataset.id);
let user = users[dataId];
showModal(user);
document.getElementById('modal-close-btn').addEventListener('click', () => {
const modal = document.querySelector('.modal-container');
document.body.removeChild(modal);
});
});
}
//This should attach your "click" event listener to any elements with the class of "card"
This sets the event listener to all "cards" but you can apply the same method/for loop for any element. Hope this helps

Prevent default link behaviour when click somewhere in <a> element

Lets say we have a like this structure:
document.querySelector(".container").addEventListener("click", e => {
e.stopImmediatePropagation();
}
<a href="page2.html" class="container">
<div class="preventGoToLinkWhenClick">
button for open video modal
</div>
</a>
When I click on the div inside, it should not go to the link. Is this possible? I have tried stopImmediatePropagation() but does not works.
But should work <a> element when click outside the div
Use an event listener to prevent default action if the clicked element contains class preventGoToLinkWhenClick (or any criterium you define), otherwise just return
document.addEventListener("click", evt => {
if (evt.target.classList.contains("preventGoToLinkWhenClick")) {
evt.preventDefault();
}
return;
});
<a href=//www.google.com" class="container">
<div class="preventGoToLinkWhenClick">
button for open video modal
</div>
[click to open]
</a>

jQuery click event removing first element

Im working on a project and on my .ejs file I have a popup:
<div id="just-claimed-popup2" class="popup">
<h6>You just claimed:</h6>
<h2 id="card-just-claimed"></h2>
<p class="show-message">Show this Screen!</p>
<button id="deletePromoFromHome" class="close-button">Close</button>
</div>
On my javascript file I have a code that creates cards on a loop:
$('#promotion-container footer').before(`
<div class="promo card promo${i}">
<div class="promo-wrapper">
<div class="promo-header">
<h2 class="promo-title">${eventName}</h2>
<span class="close-promo-wrapper"><span class="close-promo"></span></span>
</div>
<div class="promo-info">
<span class="promo-details">
<p class="promo-detail promo-location">${eventLocation}</p>
<p class="promo-detail promo-date">${eventDate}</p>
<p class="promo-detail promo-time">${eventTime}
<span class="promo-description"></span>
<span class="buttonRedemp${i}">
<button class="redddButt load-button2" data="Reedem Card">Reedem Card</button>
</span>
</div>
</div>
</div>
`)
I want the card to disappear when people click 'redddButt', this is my code:
$(`#promotion-container .promo${i} .redddButt`).on('click', function(e){
e.stopPropagation();
$(`div.promo${i}`).addClass('toDelete')
var esc = $.Event("keyup", { keyCode: 27 });
$(document).trigger(esc);
$('#just-claimed-popup2').addClass('reveal');
$('#card-just-claimed').text(eventName);
$('#deletePromoFromHome').click(function(){
$('div.toDelete').fadeOut("slow")
})
})
PROBLEM: it always removes just the first card clicked and if you click the button in another one it stops working, so it only works once. If I console.log something the click event is happening, it's just not running the code inside of it.
Try changing your handler to:
$('body').on('click', `#promotion-container .promo${i} .redddButt`, function(e){
//function stuff here
}
The problem might be that elements are generated after the handler is attached.
Your code is missing some few closing tags. Since the cards are dynamically generated, try using (not tested):
var buttonContext;
$(document).on('click', '#promotion-container .promo .redddButt', function() {
buttonContext = $(this);
// Something
});
$('#deletePromoFromHome').click(function(){
buttonContext.closest('.promo').fadeOut("slow");
});
You can omit this line: $(div.promo${i}).addClass('toDelete');
The cards may have a single class (.promo) instead of (.promo#), unless may be you want to do further manipulation (say different styling etc).
Check this for more details on $(document): https://stackoverflow.com/a/32066793/3906884

Using one event listener for two objects

I am trying to create a very simple form. My objective is to create two clickable buttons, each containing a question. When clicked, the DOM will output the answer below the each button. The tricky part is getting both buttons to respond with only one addEventListener. Here is my code:
HTML:
<div id="wrapper">
<header>
<h1>Have you ever wondered...</h1>
</header>
<div>
<button id="myBtn">How many licks it takes to get to the center of a tootsie pop?</button>
<p id="first"></p>
<h3>Or what about...</h3>
<button id="myBtnTwo">How tall the Eiffel Tower is?</button>
<p id="second"></p>
</div>
</div>
JS:
document.getElementById("myBtn").addEventListener("click", function(){
document.getElementById("first").innerHTML= "Approximately 364 licks!";
});
document.getElementById("myBtnTwo").addEventListener("click", function(){
document.getElementById("second").innerHTML= "984 feet!";
});
You can only bind one element to every event listener in JS. However, you can have multiple different events associated with one element.
Now, to solve your question: One way to address this situation is to attach the event handler to the parent element and then find the element that was the target of the event.
<div id="wrapper">
<header>
<h1>Have you ever wondered...</h1>
</header>
<div id="btn-wrapper">
<button id="myBtn">How many licks it takes to get to the center of a tootsie pop?</button>
<p id="first"></p>
<h3>Or what about...</h3>
<button id="myBtnTwo">How tall the Eiffel Tower is?</button>
<p id="second"></p>
</div>
As you can see, I added the id btn-wrapper to the parent div. And then in the JS, I attached the handler to said parent element. Then you can find the event target and run your code according to which it was.
var theParent = document.querySelector("#btn-wrapper");
theParent.addEventListener("click", doSomething, false);
function doSomething(e) {
if (e.target !== e.currentTarget) {
if (e.target === document.querySelector('#myBtn')) {
document.querySelector("#first").innerHTML= "Approximately 364 licks!";
} else if (e.target === document.querySelector('#myBtnTwo')) {
document.querySelector("#second").innerHTML= "984 feet!";
}
}
e.stopPropagation();
}
To read some more on the topic: https://www.kirupa.com/html5/handling_events_for_many_elements.htm
You can add a listener for the container div and then check which element is clicked:
document.querySelector('#container').addEventListener('click', (e) => {
if (e.target.id === 'myBtn') {
document.querySelector('#first').innerText = 'answer 1'
}
if (e.target.id === 'myBtnTwo') {
document.querySelector('#second').innerText = 'answer 2'
}
})
<div id="wrapper">
<header>
<h1>Have you ever wondered...</h1>
</header>
<div id='container'>
<button id="myBtn">How many licks it takes to get to the center of a tootsie pop?</button>
<p id="first"></p>
<h3>Or what about...</h3>
<button id="myBtnTwo">How tall the Eiffel Tower is?</button>
<p id="second"></p>
</div>
</div>
const findBtns = [...document.querySelectorAll("button")];
const handleClick = (event) => {
console.log(event.target.textContent);
};
findBtns.forEach((el) => el.addEventListener("click", handleClick));
You can just simply use another selector (.querySelectorAll) and work with the pseudo array further. If you just need to extract the text from a button it is better to use /.textContent/. /.innerHTML/ could be harmful if you not 100% sure that you need it in your code.

Categories

Resources