503

I'm currently using jQuery to make a div clickable and in this div I also have anchors. The problem I'm running into is that when I click on an anchor both click events are firing (for the div and the anchor). How do I prevent the div's onclick event from firing when an anchor is clicked?

Here's the broken code:

JavaScript

var url = $("#clickable a").attr("href");

$("#clickable").click(function() {
    window.location = url;
    return true;
})

HTML

<div id="clickable">
    <!-- Other content. -->
    <a href="http://foo.example">I don't want #clickable to handle this click event.</a>
</div>
Stephen Ostermiller
  • 23,933
  • 14
  • 88
  • 109
Jonathon Watney
  • 20,248
  • 9
  • 38
  • 40

25 Answers25

626

Events bubble to the highest point in the DOM at which a click event has been attached. So in your example, even if you didn't have any other explicitly clickable elements in the div, every child element of the div would bubble their click event up the DOM to until the DIV's click event handler catches it.

There are two solutions to this is to check to see who actually originated the event. jQuery passes an eventargs object along with the event:

$("#clickable").click(function(e) {
    var senderElement = e.target;
    // Check if sender is the <div> element e.g.
    // if($(e.target).is("div")) {
    window.location = url;
    return true;
});

You can also attach a click event handler to your links which tell them to stop event bubbling after their own handler executes:

$("#clickable a").click(function(e) {
   // Do something
   e.stopPropagation();
});
muffinrain
  • 40
  • 7
Rex M
  • 142,167
  • 33
  • 283
  • 313
  • 9
    +1! Attaching a click handler with stopPropagation is a very nice trick, thanks! – Thomas Mar 27 '12 at 12:45
  • 3
    if you had a bunch of elements you wanted to prevent propagation on, you could find their parent element and head prevent it from bubbling up there too. So rather than intercepting all of the a links in a div let's say for example-- just intercept the click even on the div itself and prevent it from going any higher and you're set. – sucitivel Nov 09 '12 at 15:28
  • Thank you so much. I came across this recently and this really helped me out. This is an example in js fiddle. http://jsfiddle.net/FgwQz/ – Butter Beer Jan 21 '13 at 14:14
  • For some odd reason `return false;` worked and `e.stopPropagation();` didn't. – Don Thomas Boyle Sep 10 '13 at 16:44
  • how do we stop propagation on css, for example, if i've made the entire container and do not want child elements to also share the styling?: $('#container').css('cursor', 'pointer'); – vulgarbulgar Dec 18 '13 at 02:17
  • Mmmmmm that e.stopPropagation(); solved everything for me. I basically had a parent that was clickable then child that was also clickable and manipulated the parent. But the parent .click ran straight after the childs click event and undone everything the childs .click event did. e.stopPropagation(); stopped the parents .click from being caught in the bubbling. – daniel blythe Jul 29 '16 at 10:02
  • 37
    Using `if( e.target !== this) return;` in the parent is better than `e.stopPropagation()` in the child since you never know if someone else attaches some handler to the children, or if a library must attach a handler to a child (and you don't want to mess with the library code). It's a better separation of concerns. – UTF_or_Death Oct 21 '16 at 17:01
  • 10
    Putting the `if( e.target !== this) return;` check in the parent means that if any of the other children of that parent that don't have onClick handlers of their own are clicked, then nothing happens. So it's useless for a situation where you have e.g. a clickable parent div. `e.stopPropagation()` works fine however. – derf26 Apr 25 '18 at 16:26
  • 1
    It might be worth noting that stopPropagation() doesn't stop other mouse events from being detected on the parent so a "`click`" event with stopPropagation wont stop an event attached to the parent with "`mouseup`" instead. It makes sense and is useful but it had me a little confused for a while – wunth Jun 26 '18 at 01:19
  • For anyone coming from react, you can use event.nativeEvent.stopImmediatePropagation to reach the native event – Gabriel Petersson Jul 25 '20 at 20:17
  • 1
    one can also use this condition `event.currentTarget === event.target` it returns false in case event is propagated from child element. – mukuljainx Jul 03 '21 at 16:24
  • To anyone still learning about bubbling and so on: For these answers to work you need to have 'event bubbling', to have the child eventlistener executed BEFORE the parent element's (it's basically a list). To have this functionality if you want or (for some reason) need to set the last value of addEventListener, set it to 'false', which is the default, for more see https://stackoverflow.com/a/14807507/14824067 . – LuckyLuke Skywalker May 11 '22 at 12:42
147

Use stopPropagation method, see an example:

$("#clickable a").click(function(e) {
   e.stopPropagation();
});

As said by jQuery Docs:

stopPropagation method prevents the event from bubbling up the DOM tree, preventing any parent handlers from being notified of the event.

Keep in mind that it does not prevent others listeners to handle this event(ex. more than one click handler for a button), if it is not the desired effect, you must use stopImmediatePropagation instead.

Cleiton
  • 17,663
  • 13
  • 46
  • 59
  • Watch out so you dont accidentaly have set the event listeners to be passive – Gabriel Petersson Jul 25 '20 at 20:00
  • **Avoid** at all costs the use of `Event.stopPropagation()`. An application (or third party code) should **never** stop or prevent an Event to propagate through layers, components. Rather check for `Event.target.closest("selector")` matches and act accordingly. [Example](https://stackoverflow.com/a/74812690/383904) – Roko C. Buljan Dec 15 '22 at 14:04
83

Here my solution for everyone out there looking for a non-jQuery code (pure javascript)

document.getElementById("clickable").addEventListener("click", function(e) {
    e = window.event || e; 
    if(this === e.target) {
      // put your code here
    }
});

Your code wont be executed if clicked on parent's children

Kurt
  • 678
  • 1
  • 7
  • 24
Sabaz
  • 4,794
  • 2
  • 18
  • 26
  • 1
    This worked for me, I needed a non-JS/jQuery solution. 10x! – L A Sep 27 '16 at 14:08
  • 5
    Great answer. I just passed the event directly. So I did not use `window.event`, which MDN discourages: https://developer.mozilla.org/en-US/docs/Web/API/Window/event. If you can spare the time I would appreciate it if you can comment why you did use `window.event` ( maybe it is an issue that existed in 2015 or I failed to understand the reason ). – Rob Monhemius May 03 '19 at 12:30
  • it doesnt work, tested it – showtime Mar 25 '22 at 11:25
  • if ( this !== e.target ) return; works better than the positive form, since it avoids nesting and makes it clearer that we're stopping when the current case doesn't pertain us. – zakmck Jul 18 '23 at 08:32
36

If you do not intend to interact with the inner element/s in any case, then a CSS solution might be useful for you.

Just set the inner element/s to pointer-events: none

in your case:

.clickable > a {
    pointer-events: none;
}

or to target all inner elements generally:

.clickable * {
    pointer-events: none;
}

This easy hack saved me a lot of time while developing with ReactJS

Browser support could be found here: http://caniuse.com/#feat=pointer-events

Hooman Askari
  • 1,486
  • 16
  • 30
  • what if you want to have other event listeners in child elements? Your approach would prevent you from doing that. – showtime Mar 25 '22 at 11:31
  • As mentioned in the answer, this is "If you do not intend to interact with the inner element/s in any case". Otherwise you have to seek other solutions. – Hooman Askari Apr 14 '22 at 13:51
28

Inline Alternative:

<div>
  <!-- Other content. -->
  <a onclick='event.stopPropagation();' href="http://foo.example">I don't want #clickable to handle this click event.</a>
</div>
Kurt
  • 678
  • 1
  • 7
  • 24
Matt Wyeth
  • 1,124
  • 2
  • 10
  • 19
18

You can also try this

$("#clickable").click(function(event) {
    var senderElementName = event.target.tagName.toLowerCase();
    if(senderElementName === 'div') {
        // Do something here 
    } else {
        // Do something with <a> tag
    }
});
Michael M.
  • 10,486
  • 9
  • 18
  • 34
Rashad Valliyengal
  • 3,132
  • 1
  • 25
  • 39
11

Writing if anyone needs (worked for me):

event.stopImmediatePropagation()

From this solution.

Community
  • 1
  • 1
Bahadir Tasdemir
  • 10,325
  • 4
  • 49
  • 61
9

Using return false; or e.stopPropogation(); will not allow further code to execute. It will stop flow at this point itself.

albertjan
  • 7,739
  • 6
  • 44
  • 74
Imamudin Naseem
  • 1,604
  • 18
  • 21
  • Yes, this is important - ran into this myself. But the answer above from Rex is helpful - can get the element that was clicked, and in some cases use this in the logic that you are trying to stop. .target.nodeName was also helpful to get a clear idea of what was hit. – Watercayman May 09 '16 at 20:33
9

I compare to ev.currentTarget when this is not available (React, etc).

$("#clickable").click(function(e) {
    if (e.target === e.currentTarget) {
        window.location = url;
        return true;
    }
})
Szalai Laci
  • 540
  • 2
  • 5
  • 12
8

If you have multiple elements in the clickable div, you should do this:

$('#clickable *').click(function(e){ e.stopPropagation(); });
Glorious Kale
  • 1,273
  • 15
  • 25
3

Here's an example using Angular 2+

For example, if you wanted to close a Modal Component if the user clicks outside of it:

// Close the modal if the document is clicked.

@HostListener('document:click', ['$event'])
public onDocumentClick(event: MouseEvent): void {
  this.closeModal();
}

// Don't close the modal if the modal itself is clicked.

@HostListener('click', ['$event'])
public onClick(event: MouseEvent): void {
  event.stopPropagation();
}
Steve Brush
  • 2,911
  • 1
  • 23
  • 15
3

var inner = document.querySelector("#inner");
var outer = document.querySelector("#outer");
inner.addEventListener('click',innerFunction);
outer.addEventListener('click',outerFunction);

function innerFunction(event){
  event.stopPropagation();
  console.log("Inner Functiuon");
}

function outerFunction(event){
  console.log("Outer Functiuon");
}
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>Pramod Kharade-Event with Outer and Inner Progration</title>
</head>
<body>
<div id="outer" style="width:100px;height:100px;background-color:green;">
  <div id="inner" style="width:35px;height:35px;background-color:yellow;"></div>
</div>
</body>
</html>
Pramod Kharade
  • 2,005
  • 1
  • 22
  • 41
3

If it is in inline context, in HTML try this:

onclick="functionCall();event.stopPropagation();
Gleno
  • 85
  • 4
3

e.stopPropagation() is a correct solution, but in case you don't want to attach any event handler to your inner anchor, you can simply attach this handler to your outer div:

e => { e.target === e.currentTarget && window.location = URL; }
govizlora
  • 79
  • 1
  • 4
1

You need to stop the event from reaching (bubbling to) the parent (the div). See the part about bubbling here, and jQuery-specific API info here.

Matt Ball
  • 354,903
  • 100
  • 647
  • 710
0

To specify some sub element as unclickable write the css hierarchy as in the example below.

In this example I stop propagation to any elements (*) inside td inside tr inside a table with the class ".subtable"

$(document).ready(function()
{    
   $(".subtable tr td *").click(function (event)
   {
       event.stopPropagation();
   });

});
user3202819
  • 131
  • 1
  • 2
0

You can check whether the target is not your div-element and then issue another click event on the parent after which you will "return" from the handle.

$('clickable').click(function (event) {
    let div = $(event.target);
    if (! div.is('div')) {
       div.parent().click();
       return;
    }
    // Then Implement your logic here
}
nafiu
  • 69
  • 1
  • 4
0

Here is a non jQuery solution that worked for me.

<div style="background:cyan; width:100px; height:100px;" onclick="if (event.srcElement==this) {console.log('outer');}">
    <a style="background:red" onclick="console.log('inner');">Click me</a>
</div>
Duncan
  • 209
  • 1
  • 6
0

for those that are not using jQuery

document.querySelector('.clickable').addEventListener('click', (e) =>{
    if(!e.target.classList.contains('clickable')) return
    // place code here
})
  • Hello & thank you! this appears to largely duplicate an existing answer ( https://stackoverflow.com/a/29499060 ) Please [edit] to explain how this is different / better than existing answers! – Edward Jan 20 '23 at 20:42
-1

In case someone had this issue using React, this is how I solved it.

scss:

#loginBackdrop {
position: absolute;
width: 100% !important;
height: 100% !important;
top:0px;
left:0px;
z-index: 9; }

#loginFrame {
width: $iFrameWidth;
height: $iFrameHeight;
background-color: $mainColor;
position: fixed;
z-index: 10;
top: 50%;
left: 50%;
margin-top: calc(-1 * #{$iFrameHeight} / 2);
margin-left: calc(-1 * #{$iFrameWidth} / 2);
border: solid 1px grey;
border-radius: 20px;
box-shadow: 0px 0px 90px #545454; }

Component's render():

render() {
    ...
    return (
        <div id='loginBackdrop' onClick={this.props.closeLogin}>
            <div id='loginFrame' onClick={(e)=>{e.preventDefault();e.stopPropagation()}}>
             ... [modal content] ...
            </div>
        </div>
    )
}

By a adding an onClick function for the child modal (content div) mouse click events are prevented to reach the 'closeLogin' function of the parent element.

This did the trick for me and I was able to create a modal effect with 2 simple divs.

Claudio
  • 92
  • 2
  • 11
-1

If a child element is clicked, then the event bubbles up to the parent and event.target !== event.currentTarget.

So in your function, you can check this and return early, i.e.:

var url = $("#clickable a").attr("href");
$("#clickable").click(function(event) {
    if ( event.target !== event.currentTarget ){
        // user clicked on a child and we ignore that
        return;
    }
    window.location = url;
    return true;
})
zıəs uɐɟəʇs
  • 1,728
  • 3
  • 14
  • 27
-2

This is what you are looking for

mousedown event. this works on every DOM elements to prevent javascript focus handler like this:

$('.no-focus').mousedown(function (e) {
   e.prevenDefault()

   // do stuff
}

in vue.js framework, you can use modifier like this:

<span @mousedown.prevent> no focus </span>

Note that using on the input will prevent text selection handler

Javad Ebrahimi
  • 333
  • 5
  • 13
-3

add a as follows:

<a href="http://foo.example" onclick="return false;">....</a>

or return false; from click handler for #clickable like:

  $("#clickable").click(function() {
        var url = $("#clickable a").attr("href");
        window.location = url;
        return false;
   });
Stephen Ostermiller
  • 23,933
  • 14
  • 88
  • 109
TheVillageIdiot
  • 40,053
  • 20
  • 133
  • 188
-4

All solution are complicated and of jscript. Here is the simplest version:

var IsChildWindow=false;

function ParentClick()
{
    if(IsChildWindow==true)
    {
        IsChildWindow==false;
        return;
    }
    //do ur work here   
}


function ChildClick()
{
    IsChildWindow=true;
    //Do ur work here    
}
Nate
  • 12,499
  • 5
  • 45
  • 60
ratnesh
  • 1
  • 1
-5
<a onclick="return false;" href="http://foo.example">I want to ignore my parent's onclick event.</a>
Stephen Ostermiller
  • 23,933
  • 14
  • 88
  • 109
andres descalzo
  • 14,887
  • 13
  • 64
  • 115
  • 6
    This also prevents the link from navigating. – Rex M Sep 02 '09 at 17:28
  • Yes, or do not what is needed?. Or need to be nevegar? – andres descalzo Sep 02 '09 at 18:12
  • 1
    Please read [How do I write a good answer?](https://stackoverflow.com/help/how-to-answer). While this code block may answer the OP's question, this answer would be much more useful if you explain how this code is different from the code in the question, what you've changed, why you've changed it and why that solves the problem without introducing others. – Saeed Zhiany Jun 25 '22 at 13:41