Why `event.preventDefault()` is not working for two nested divs? - javascript

function clickOut(event) {
event.preventDefault();
alert('click on out');
}
function clickIn(event) {
event.preventDefault();
alert('click on in');
}
#out {
width: 100px;
height: 100px;
border: 1px solid red;
}
#in {
width: 50px;
height: 50px;
border: 1px solid blue;
}
<div id="out" onclick="clickOut(event)">
<div id="in" onclick="clickIn(event)">
</div>
</div>
Live demo: http://jsbin.com/qomuteyoke/1/edit?html,css,js,output
Why when I click on the inner div, there still pops two alerts, even if I've called event.preventDefault()? First is click on in, and 2nd is click on out?

Try using event.stopPropagation():
jQuery def:
Description: Prevents the event from bubbling up the DOM tree,
preventing any parent handlers from being notified of the event.
MDN def:
Prevents further propagation of the current event.
function clickIn(event) {
event.stopPropagation();
alert('click on in');
}

It's because there is no default action to prevent. Prevent default will work on anchor tags, which are used to prevent the default behaviour. What you want to do is to stop the propagation of the click, so it doesn't bubble up from the inner div. That way, if you click on the inner div, only the inner div click fires.
Replace your event.preventDefault() with event.stopPropagation().

Prevent default does not stop the event from propagating to the parents. You want to use event.stopPropagation(); which prevents further propagation of the current event. MDN
function clickOut(event) {
event.stopPropagation();
alert('click on out');
}
function clickIn(event) {
event.stopPropagation();
alert('click on in');
}
#out {
width: 100px;
height: 100px;
border: 1px solid red;
}
#in {
width: 50px;
height: 50px;
border: 1px solid blue;
}
<div id="out" onclick="clickOut(event)">
<div id="in" onclick="clickIn(event)">
</div>
</div>
css

Related

Why the event trigger immediately when I just dispatch it?

I would like do something when i click element #b then dispatch an other event listener before handleB() finished
When I was clicked #b the event click on #a has dispatch successful and it has also triggered but I don't wanna trigger it
What's going on here??
document.getElementById('b').addEventListener('click', handleB)
function handleB() {
alert('Handle B!');
document.getElementById('a').addEventListener('click', handleA)
}
<div id="a">
AAA
<div id="b">BBB</div>
</div>
This is happening because of the event of #b bubbles up to #a and since you have added the click event listener inside handleB. It triggers immediately with the event of #b. You have to stop the event to bubble up. Below is the working example:
document.getElementById('b').addEventListener('click', handleB);
function handleB(event) {
event.stopPropagation();
console.log('b clicked');
document.getElementById('a').addEventListener('click', handleA)
}
function handleA() {
console.log('a clicked');
}
#a {
width: 200px;
height: 200px;
background-color: red;
}
#b {
width: 100px;
height: 100px;
background-color: green;
}
<div id="a">
<div id="b"></div>
</div>

Keep the event, but avoid on links(or inner content)

This is probably asked a thousand of times, but I can't find an answer...
Here is an example:
var div = $('.div');
div.on('click', function(e) {
e.stopPropagation();
e.preventDefault();
div.toggleClass('red');
});
.div {
width: 50vh;
height: 50vh;
background: #eee;
}
.red {
background: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="div">
link
</div>
The problem: event triggered when link(any content) is clicked.
I want remain the event, but avoid when content is clicked/selected/whatever. What are my options here?
Thanks
You just need to set up a "click" event handler for any child element of the div.div that prevents that event from bubbling up to any ancestor elements. This can be done with the * (universal) CSS selector.
Unless, you have reason to that you haven't described here, you don't need:
e.stopPropagation();
e.preventDefault();
in the div event handler.
var div = $('.div');
div.on('click', function(e) {
div.toggleClass('red');
});
// When any child element of the div with class .div is clicked...
$("div.div > *").on("click", function(evt){
// Don't allow the event to bubble up to the ancestor elements
evt.stopPropagation();
console.log("You clicked some child element of the div, but the event won't bubble up to it.");
});
.div {
width: 50vh;
height: 50vh;
background: #eee;
}
.red {
background: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="div">
link
<span>Clicking me will do nothing</span>
</div>

How to remove Jquery event if another element has certain class?

I am trying to build a simple dropdown plugin for small project of mine. I do not want to use ready plugins, I want to learn by making one on my own.
html:
<div>
<span class="dropdown_triger">press</span>
<div class="content dropdown-closed">
</div>
</div>
css:
span{
display:inline-block;
background: green;
padding: 5px;
}
.content{
position: absolute;
top: 40px;
border: solid 1px black;
width: 200px;
height: 100px;
}
.dropdown-closed { display: none; }
.dropdown-open { display: block; }
and JS:
$(document).ready(function(){
$('body').on('click', '.dropdown_triger', function(e){
var $wrapper = $(this).parent();
var $content = $(this).next();
var $triger = $(this);
if($triger.hasClass('selected')){
$(document).off('mouseup.dropdownDocClick');
console.log('hasClass');
}
$triger.toggleClass('selected');
$content.toggleClass('dropdown-closed dropdown-open');
$(document).on('mouseup.dropdownDocClick',function (e){
console.log('fire');
if (!$wrapper.is(e.target) && $wrapper.has(e.target).length === 0){
if($content.hasClass('dropdown-open')){
$content.toggleClass('dropdown-closed dropdown-open');
$(document).off('mouseup.dropdownDocClick');
}
}
});
});
});
Everything works except for this place:
if($triger.hasClass('selected')){
$(document).off('mouseup.dropdownDocClick');
console.log('hasClass');
}
I expect that mouseup event would not fire anymore but it does. Here is a fiddle, just try it. If I open dropdown, mouseup event is attached to document and keeps firing until I have clicked outside container thus closed dropdown.
But if I close dropdown by clicking again on triger button(span in my example) event is not removed and I can not understand why?

multiple div's display content with click, prevent event bubbling

I have 3 div's: div1, div2, div3. When the user clicks on each div, I want to console log the content of that particular div only. I read about event bubbling and understood that the click event of the child will also be registered at the parent level. I tried the below code but couldn't get it to work correctly.
$(document).ready(function() {
$('#parent').on('click', 'div', function() {
console.log($(this).text());
})
})
#div1 {
width: 300px;
height: 200px;
padding: 25px;
background-color: red;
text-align: center;
}
#div2 {
width: 250px;
height: 150px;
background-color: blue;
padding: 25px;
text-align: center;
}
#div3 {
width: 200px;
height: 100px;
background-color: green;
text-align: center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body>
<div id="parent">
<div id="div1">div1
<div id="div2">div2
<div id="div3">div3</div>
</div>
</div>
</div>
</body>
How can I make it to print div1 when I click div1, div2 when I click div2 and div3 when I click div3?
is jQuery text() the right method to print the content? I can see it is printing spaces and all
The issue is not just related to event bubbling, but also the fact that the text() of an element contains the text of itself and all its child elements.
To do what you require you would need to filter() the contents() of the current element to retrieve only the text nodes - assuming you want to discard the text which is wrapped in any child element.
Try this:
$('#parent').on('click', 'div', function(e) {
e.stopPropagation();
var nodes = $(this).contents().filter(function() {
return this.nodeType == 3 && this.nodeValue.trim() != '';
});
console.log(nodes[0].nodeValue.trim());
});
Working example
Try this, according to this solution
$('#parent').on('click', 'div', function(e){
e.stopPropagation();
console.log($(this)
.clone()
.children()
.remove()
.end()
.text()
.trim()
);
})

Is this possible to force execution of .click() for all elements, even those that are underneath?

Consider this code:
HTML:
<div class='a'>a</div>
<div class='b'>b</div>
<div id='log'></div>
CSS:
.a, .b {
width: 100px;
height: 100px;
border: 1px solid black;
position: absolute;
text-align: right;
}
.a {
left: 100px;
top: 100px;
}
.b {
left: 150px;
top: 150px;
}
JS:
$('*').click(function(e) {
log(this.nodeName + ' ' + this.className);
});
function log(s) {
$('#log').append(s + '<br />');
}
If the intersection is clicked, .click() for .a is not called.
Is there any built-in method to force the execution of click() for all elements, and not only the top one and its parents, or I must implement this myself ?
I think that the behavior you are observing is correct. When clicking the topmost element the one beneath won't get a click event despite it looks it is in the "clicked" area. As a workaround you can manually trigger it's click event:
$('.a').click().
Calling click without arguments is equivalent to trigger('click') which raises the specified event. You can find more info in the trigger help topic.

Categories