ChatGPT解决这个技术问题 Extra ChatGPT

How do I prevent a parent's onclick event from firing when a child anchor is clicked?

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>

m
muffinrain

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();
});

Great answer. Nice to know there are options too.
+1! Attaching a click handler with stopPropagation is a very nice trick, thanks!
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.
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.
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.
C
Cleiton

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.


Watch out so you dont accidentaly have set the event listeners to be passive
S
Sabaz

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 childs


This worked for me, I needed a non-JS/jQuery solution. 10x!
Great answer. I just passed the event directly. So I did not use window.event, which MDN discourages: 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 ).
it doesnt work, tested it
H
Hooman Askari

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


what if you want to have other event listeners in child elements? Your approach would prevent you from doing that.
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.
R
Rashad Valliyengal

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
   }
});

S
Stephen Ostermiller

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>

C
Community

Writing if anyone needs (worked for me):

event.stopImmediatePropagation()

From this solution.


G
Glorious Kale

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

$('#clickable *').click(function(e){ e.stopPropagation(); });

a
albertjan

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


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.
S
Szalai Laci

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;
    }
})

S
Steve Brush

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();
}

P
Pramod Kharade

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"); } Pramod Kharade-Event with Outer and Inner Progration


G
Gleno

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

onclick="functionCall();event.stopPropagation();

g
govizlora

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; }

M
Matt Ball

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.


Cool. Thanks for the info about event bubbling.
n
nafiu

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
}

D
Duncan

Here is a non jQuery solution that worked for me.


u
user3202819

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();
   });

});

C
Claudio

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.


F
Fom

ignoreParent() is a pure JavaScript solution.

It works as an intermediary layer that compares the coordinates of the mouse click with the coordinates of the child element/s. Two simple implementation steps:

1. Put the ignoreParent() code on your page.

2. Instead of the parent's original onclick="parentEvent();", write:

onclick="ignoreParent(['parentEvent()', 'child-ID']);"

You may pass IDs of any number of child elements to the function, and exclude others.

If you clicked on one of the child elements, the parent event doesn't fire. If you clicked on parent, but not on any of the child elements [provided as arguments], the parent event is fired.

ignoreParent() code on Github


z
zıəs uɐɟəʇs

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;
})

J
Javad Ebrahimi

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


N
Nate

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    
}

i think you made a mistake in line "IsChildWindow==false;" - shouldn't it be "IsChildWindow = false;"?
S
Stephen Ostermiller

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;
   });

S
Stephen Ostermiller
<a onclick="return false;" href="http://foo.example">I want to ignore my parent's onclick event.</a>

This also prevents the link from navigating.
Yes, or do not what is needed?. Or need to be nevegar?
Please read How do I write a good 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.