How to avoid event from div below object? - javascript

I have this code:
<div style="width:200px;height:100px;border:solid 1px" onclick="alert(1)">
Title
<br />
Subtitle
<div style="float:right">
<input type="checkbox" />
</div>
</div>
I want to redirect the user to a url when he clicks on the div (where the alert is now) but I want to allow the user with functionality when he clicks on the checkbox.
Is it possible to allow the checkbox to be clicked and change status without invoking the alert(1) from the div below ?

You need to use event.stopPropagation(); function. This function prevents further propagation of the current event in the capturing and bubbling phases.
<div style="width:200px;height:100px;border:solid 1px" onclick="alert(1)">
Title
<br />
Subtitle
<div style="float:right">
<input type="checkbox" onclick="onCheckBoxCicked(event)"/>
</div>
function onCheckBoxCicked(event) {
alert(2)
event.stopPropagation();
};

You will need to use stopPropagation() method on the checkbox click event handler, so the click of the checkbox won't trigger the click of its parents divs:
HTML:
<input type="checkbox" onclick="avoidAlert(event)" />
JS:
function avoidAlert(event) {
event.stopPropagation();
}
function avoidAlert(event) {
event.stopPropagation();
}
<div style="width:200px;height:100px;border:solid 1px" onclick="alert(1)">
Title
<br /> Subtitle
<div style="float:right">
<input type="checkbox" onclick="avoidAlert(event)" />
</div>
</div>
MDN Ref for stopPropagation() method:
The stopPropagation() method of the Event interface prevents further propagation of the current event in the capturing and bubbling phases.

You could either try event.stopPropagation() inside an extra event handler on the checkbox, or simply check inside the div's event handler if the target of the click has been the checkbox:
var theDiv = document.querySelector('.outer'),
theCheckbox = document.querySelector('input');
theDiv.addEventListener('click', divClicked);
function divClicked(e) {
if (e.target !== theCheckbox) {
alert('You clicked the div!');
}
}
<div class="outer" style="width:200px;height:100px;border:solid 1px">
Title
<br />
Subtitle
<div style="float:right">
<input type="checkbox" />
</div>
</div>

Related

why this eventListner callback function is running twice

const addons = document.querySelectorAll('.addon');
const toggleAddon = (e, addon) => {
console.log(addon);
console.log(addon.querySelector('input').checked);
if (addon.querySelector('input').checked)
addon.classList.remove('selected-plan');
else addon.classList.add('selected-plan');
};
addons.forEach((addon) => {
addon.addEventListener('click', (e) => toggleAddon(e, addon));
});
<label>
<div class="addon addon-2 selected-addon">
<input type="checkbox" name="addon-2" class="addon-chkbox" id="larger-storage">
<div class="addon-info">
<h3 class="addon-name">Larger Storage</h3>
<p class="addon-features">Extra 1TB of cloud save</p>
</div>
<div class="addon-pricing-box">
<h3 class="addon-pricing">$2/mo</h3>
</div>
</div>
</label>
Why when I click on this element, the function toggleAddon() runs twice and in first run console.log(addon.querySelector('input').checked) comes false and on second it comes true.
Thanks for the help.
This is another simplified example that will better show what I'm pointing out:
let counter = 0;
document.querySelector('.addon')
.addEventListener('click', event =>{
console.log(`[${++counter}]`);
console.log(event.target);
});
<label>
<div class="addon">
<input type="checkbox">
<div class="addon-info">
<h3 class="addon-name">Click here</h3>
</div>
</div>
</label>
The problem is with the <label> element, when it is clicked, it trigger another click event on the input element within.
I suggest using change event instead.
check it in codesandbox.
The whole label is for the input? so why not listen to the input click event only which will automatically be handled by the label? like:
let counter = 0;
document.querySelector('.addon-input')
.addEventListener('click', event =>{
console.log(`[${++counter}]`);
console.log(event.target);
});
<label>
<div class="addon">
<input class="addon-input" type="checkbox">
<div class="addon-info">
<h3 class="addon-name">Click here</h3>
</div>
</div>
</label>

Why this event listener not apply to the button?

I have this HTML, and from what I understand about Event Delegation it bubbles up and the same event is applied to an element’s ancestors.
In my case clicking on the UL works and it console.logs a message, however why is this not delegated to the button and other items in the form that are above it?
<div class="shopping-list">
<form class="shopping" autocomplete="off">
<input type="text" name="item" id="item">
<button type="submit">+ Add Item</button>
</form>
<ul class="list">click me</ul>
</div>
const list = document.querySelector('.list');
list.addEventListener('click', function () {
console.log('works');
});
Events can only bubble up to parent/ancestor elements (i.e. elements that it is contained inside of). So for example, events of your ul can get bubbled up to its containing div, .shopping-list.
However for the form, your ul is not inside of it, it is just a sibling to the form.
With event bubbling, an event will "bubble" up from its target to its ancestors, like you say - but the button is not an ancestor of the list. If you format your HTML properly, it'll be pretty obvious:
<div class="shopping-list">
<form class="shopping" autocomplete="off">
<input type="text" name="item" id="item">
<button type="submit">+ Add Item</button>
</form>
<ul class="list">click me</ul>
</div>
The event dispatched to the .list bubbles up to the .shopping-list, it's parent. The button is not an ancestor, so any listeners attached to the button won't see it.
If you attach a listener to the .shopping-list, they'll see the event, as expected:
const list = document.querySelector('.list');
list.addEventListener('click', function () {
console.log('<ul> click listener');
});
document.querySelector('.shopping-list').addEventListener('click', () => {
console.log('div.shopping-list click listener - delegation worked');
});
<div class="shopping-list">
<form class="shopping" autocomplete="off">
<input type="text" name="item" id="item">
<button type="submit">+ Add Item</button>
</form>
<ul class="list">click me</ul>
</div>
In your case the parent of your element <ul> it's <div class="shopping-list">,
So the Event bubbling can not reach your element <button type="submit">+ Add Item</button> cause <ul class="list">click me</ul> and <form><input></input><button></button> are the children of your element <div class="shopping-list">
So we have to understand that the event bubbling begin from the child to the parent and you can reverse this direction "event capturing" by adding true after your callback function in your listener =>
list.addEventListener('click', function () {
console.log('works');
},true)
Finally I think that you have put your <ul> element into your <form> element :
<div class="shopping-list">
<form class="shopping" autocomplete="off">
<input type="text" name="item" id="item">
<button type="submit">+ Add Item</button>
<ul class="list">click me</ul>
</form>
</div>

jQuery Click event not attaching to Knockout template

I have a block of code that is attached to a jQuery click event. Here's the element:
<!-- Profiles -->
<div class="profiles">
<h1>Profiles</h1>
<div class="profile">
<div class="name">
<input type="text" maxlength="14" value="Default" />
<span class="rename">q</span>
</div>
<div class="controls">
<span class="edit">EDIT</span>
<span class="duplicate">COPY</span>
<span class="delete default">J</span>
<div class="alert-box">
<p>Are you sure you want to delete this profile?</p>
<div>Y</div>
<div>N</div>
</div>
</div>
<div class="saved">
<span class="cancel-button">Cancel</span><span class="save-button">Save</span>
</div>
</div>
</div>
When the item is selected, it becomes available for editing. Here's the event listener:
$('.rename').click(function () {
$('.selected .rename').fadeIn(80);
$(this).fadeOut(80);
$(this).parent().addClass('selected');
});
There's another event that listens for a click anywhere else on the page to deselect the item being edited:
$(document).click(function () {
$(".selected .rename").fadeIn(80);
$('.name').removeClass('selected');
});
When it is clicked on, it should be selected to allow for editing. When I move the code from the profile into a knockout template, it doesn't listen to the click event anymore. When I inspect the Event Listeners in Chrome's tools, the listener is nowhere to be found. Here is what my template looks like:
<div class="profiles">
<h1>Profiles</h1>
<div data-bind="template: { name: 'profilestempl', foreach: $root.profiles }"></div>
</div>
<script type="text/html" id="profilestempl">
<div class="profile">
<div class="name">
<input type="text" maxlength="14" data-bind="value: name" />
<span class="rename">q</span>
</div>
<div class="controls">
<span class="edit">EDIT</span>
<span class="duplicate">COPY</span>
<span class="delete">J</span>
<div class="alert-box">
<p>Are you sure you want to delete this profile?</p>
<div>N</div><div>Y</div>
</div>
</div>
<div class="saved">
<span class="cancel-button">Cancel</span><span class="save-button">Save</span>
</div>
</div>
</script>
Can someone explain to me why the event listener no longer works on the dynamically added elements? I would also like help in solving this problem. Thanks!
You have to add click event listener on the outer element which is always visible (since it doesn't work on hidden elements). And then add other selector for template code (which is hidden)
Sample code would be:
function addClickEventToCloseButton(){
$("#outerAlwaysVisible").on('click','#templateHiddenInitially',function(){
alert('works')
});
}
If you want the handler to work on elements that will be created in the future you should use on : http://api.jquery.com/on/

Click event triggering on children?

I have the following HTML code
<div id="id0_0" class="clearfix" style="margin-left:40px">
<div id="id0_1" class="choice">
<span id="spanradio_1" class="radio"> </span>
<span id="spanlabel_1" class="label"><label id="label_1">Indoor</label></span>
</div>
<div id="id0_2" class="choice">
<span id="spanradio_2" class="radio"> </span>
<span id="spanlabel_2" class="label"><label id="label_2">Outdoor</label></span>
</div>
</div>
with the following jQuery:
jQuery('.choice').click( function(e) {
alert('clicked!! ' + e.target.id);
var src_ele = $(e.target);
e.stopPropagation();
return false;
});
However, my click is triggering on the spanradio's & label's and not the div's. Any reason what is causing this?
Thanks!
Basically:
this is the element you bound the event to (i.e. one of the elements in the set that you called .on on)
e.target is the deepest element you clicked on
So you want this.id.
e.target is the element that the user actually clicked on.
If the user clicked on a nested element, e.target will be that element.
because the label is the ONLY content in a ".choice"-element and therefore is on the TOP --> it's actually the element, you clicked on...
check out event.relatedTarget if this is the right one for you.

How to use jQuery to assign a class to only one radio button in a group when user clicks on it?

I have the following markup. I would like to add class_A to <p class="subitem-text"> (that holds the radio button and the label) when user clicks on the <input> or <label>.
If user clicks some other radio-button/label in the same group, I would like to add class_A to this radio-button's parent paragraph and remove class_A from any other paragraph that hold radio-buttons/labels in that group. Effectively, in each <li>, only one <p class="subitem-text"> should have class_A added to it.
Is there a jQuery plug-in that does this? Or is there a simple trick that can do this?
<ul>
<li>
<div class="myitem-wrapper" id="10">
<div class="myitem clearfix">
<span class="number">1</span>
<div class="item-text">Some text here </div>
</div>
<p class="subitem-text">
<input type="radio" name="10" value="15" id="99">
<label for="99">First subitem </label>
</p>
<p class="subitem-text">
<input type="radio" name="10" value="77" id="21">
<label for="21">Second subitem</label>
</p>
</div>
</li>
<li>
<div class="myitem-wrapper" id="11">
<div class="myitem clearfix">
<span class="number">2</span>
<div class="item-text">Some other text here ... </div>
</div>
<p class="subitem-text">
<input type="radio" name="11" value="32" id="201">
<label for="201">First subitem ... </label>
</p>
<p class="subitem-text">
<input type="radio" name="11" value="68" id="205">
<label for="205">Second subitem ...</label>
</p>
<p class="subitem-text">
<input type="radio" name="11" value="160" id="206">
<label for="206">Third subitem ...</label>
</p>
</div>
</li>
</ul>
DEMO: http://jsbin.com/ebaye3
since you are putting all inside P you can use it!
$(function($) {
$('.subitem-text').click(function() {
$(this).parent().find('.subitem-text').removeClass('class_A');
if ( $(this).children(':radio').is(':checked') ) // for sake! ;-)
$(this).addClass('class_A');
});
//you can also write it like this:
$('.subitem-text :radio').click(function() {
$(this).parent().parent().children().removeClass('class_A');
if ( $(this).is(':checked') )
$(this).parent().addClass('class_A');
});
});
You can do something like this:
// find all the radio inputs inside a subitem-text
$('.subitem-text input[type=radio]').bind('change', function() {
// find our parent LI
var $li = $(this).closest('li');
// remove any "class_A"
$li.find('.class_A').removeClass('class_A');
// find the subitem with the checked input and add "class_A"
$li.find('.subitem-text:has(input[checked])').addClass('class_A');
});
jsbin preview/demo
Like this:
$(':radio').click(function() {
$(this).closest('ul').find('.subitem-text').removeClass('active');
$(this).closest('.subitem-text').addClass('active');
});
There is no set way to do this since it is dependent on the html in your document. The simplest way to do this is to bind to each of your radio input elements but you can also use event delegation and bind to the div.myitem-wrapper, li, parent ul or event body tag.
Just binding click handlers to each of the input elements we are interested in:
$("div.myitem-wrapper input[type=radio]").bind('change', function (event) {
$(event.target).is(':checked').closest('p')
.addClass('class_A')
.siblings('p.subitem-text').removeClass('class_A');
});
Same thing but using event delegation to reduce the number of handlers to one. This can really speed things up if you find that you are binding to a large number of elements.
$("#id_of_the_parent_UL").bind('change', function (event) {
var $target = $(event.target);
if ($target.is('input[type=radio]:checked')) {
$target.closest('p')
.addClass('class_A')
.siblings('p.subitem-text').removeClass('class_A');
}
});
Note that it is perfectly valid to say $(this) instead of $(event.target) but it will give different results if and when you start moving to event delegation. An added advantage is that it will be easier for you or the next guy to understand the code in in two months if you go with event.target.

Categories

Resources