155

Trying to wrap my head around the jQuery ".not()" function, and running into a problem. I would like to have the parent div to be "clickable" but if a user clicks on a child element, the script is not called.

$(this).not(children()).click(function(){
   $(".example").fadeOut("fast");
});

the html:

<div class="example">
   <div>
      <p>This content is not affected by clicks.</p>
   </div>
</div>
Min
  • 410
  • 2
  • 8
superUntitled
  • 22,351
  • 30
  • 83
  • 110

6 Answers6

213

To do this, stop the click on the child using .stopPropagation:

$(".example").click(function(){
  $(this).fadeOut("fast");
}).children().click(function(e) {
  return false;
});

This will stop the child clicks from bubbling up past their level so the parent won't receive the click.

.not() is used a bit differently, it filters elements out of your selector, for example:

<div class="bob" id="myID"></div>
<div class="bob"></div>

$(".bob").not("#myID"); //removes the element with myID

For clicking, your problem is that the click on a child bubbles up to the parent, not that you've inadvertently attached a click handler to the child.

Evan Nagle
  • 5,133
  • 1
  • 26
  • 24
Nick Craver
  • 623,446
  • 136
  • 1,297
  • 1,155
  • thanks nick, I do not think that this is what i was looking for. I am sorry that I was not clear in my question. I want the entire div.example to fadeOut, only when the parent element is clicked, not when the child div is clicked. The reason for this is I would like the user to be able to click on the paragraph text and not have the text fadeOut. If the user clicks on the outer div (parent div), then everything will fade out. – superUntitled Mar 16 '10 at 19:49
  • @superUntitled - Thanks for the clarification...answer's updated to do this, give it a try. – Nick Craver Mar 16 '10 at 20:23
  • this is problematic when I have other events tied to the elements (onsubmit of the child elements doesn't work anymore for some reason) – Asaf May 03 '11 at 11:52
  • 19
    @Asaf - use `e.stopPropagation();` instead of `return false;` for that case. – Nick Craver Jun 20 '11 at 13:04
  • 2
    You can also use `}).find('.classes-to-ignore').click(function(e) {` to pick specific child elements – Paul Mason Feb 14 '13 at 19:22
  • 6
    I don't understand why you tell him to use `e.stopPropagation()` and then use `return false` instead. `return false` is equivalent to `e.preventDefault(); e.stopPropagation()` so it might have unexpected side-effects. – Steen Schütt Aug 12 '15 at 15:17
  • @NickCraver What if I want to do it reverse? like if I use stopPropagation() on the #dropdown-menu then the "Click Me" button gets dumb. How to avoid it getting dumb? Normally can be solved using $(document).on('click','dropdown-menu',function(){e.stopPropgation();}); But then when loaded dynamically it's dire situation. the solution does not work anymore! What can I do? [See my question please](http://stackoverflow.com/questions/35812137/how-to-handle-bootstrap-2-default-dropdown-menu-and-jquery-stoppropagation-issue) – edam Mar 05 '16 at 10:12
  • Doing a `return false` actually causes things like form elements to stop working, be warned. – NoobishPro Jan 18 '17 at 19:25
  • This is a bad answer, any click event listeners that are attached to the children will be ignored. – Richie Jul 07 '21 at 09:01
  • What about you update your answer? You mention that you must use `stopPropagation()` but your answer shows otherwise. – MrUpsidown Aug 11 '22 at 13:16
193

I'm using following markup and had encoutered the same problem:

<ul class="nav">
    <li><a href="abc.html">abc</a></li>
    <li><a href="def.html">def</a></li>
</ul>

Here I have used the following logic:

$(".nav > li").click(function(e){
    if(e.target != this) return; // only continue if the target itself has been clicked
    // this section only processes if the .nav > li itself is clicked.
    alert("you clicked .nav > li, but not it's children");
});

In terms of the exact question, I can see that working as follows:

$(".example").click(function(e){
   if(e.target != this) return; // only continue if the target itself has been clicked
   $(".example").fadeOut("fast");
});

or of course the other way around:

$(".example").click(function(e){
   if(e.target == this){ // only if the target itself has been clicked
       $(".example").fadeOut("fast");
   }
});
starball
  • 20,030
  • 7
  • 43
  • 238
MatCarey
  • 2,409
  • 1
  • 16
  • 12
  • 28
    I think this is the better solution. It doesn't interfere with the children in any way and it should perform better as it doesn't potentially register thousands of callbacks if there are thousands of children. – LucasB Mar 20 '12 at 23:57
  • 4
    @l2aelba - you should be using `.on("click", ...)` in recent versions of jQuery as `.live()` has been deprecated since v1.7. See http://api.jquery.com/live/ – Chris Apr 11 '13 at 17:27
  • Yes, @Chris I posted on Nov 16 '12 at 10:12 – l2aelba Apr 11 '13 at 17:45
  • Sorry, probably didn't need to reference your ID. I added the comment largely for anyone else reading this answer. – Chris Apr 11 '13 at 17:56
  • 2
    This answer is better than the accepted answer as it doesn't interrupt any click events on child elements – cameronjonesweb Mar 25 '17 at 02:34
  • This is not the accepted answer but it is the correct answer. – Richie Jul 07 '21 at 09:01
26

Or you can do also:

$('.example').on('click', function(e) { 
   if( e.target != this ) 
       return false;

   // ... //
});
dani24
  • 2,108
  • 2
  • 26
  • 28
  • You must to return false, to avoid the click event in the children elements. The revision was wrong – dani24 Feb 25 '16 at 18:55
10

My solution:

jQuery('.foo').on('click',function(event){
    if ( !jQuery(event.target).is('.foo *') ) {
        // code goes here
    } 
});
npl len
  • 101
  • 1
  • 5
8

I personally would add a click handler to the child element that did nothing but stop the propagation of the click. So it would look something like:

$('.example > div').click(function (e) {
    e.stopPropagation();
});
Noahone
  • 91
  • 1
  • 2
  • what's the difference between `stopPropagation` and returning false like other answers suggest? – Crashalot Mar 16 '20 at 22:56
  • I believe in this case they’d be functional the same, but I think stop propagation is clearer to the purpose of what’s being done and leaves a better chance of understanding why it’s being done later. But it’s debatable. – Noahone Mar 16 '20 at 23:03
  • 2
    thanks for the clarification. it seems like `stopPropagation` may be cleaner as it won't stop events on child elements from occurring, which may happen with `return false`. – Crashalot Mar 16 '20 at 23:20
0

Here is an example. Green square is parent and yellow square is child element.

Hope that this helps.

var childElementClicked;

$("#parentElement").click(function(){

  $("#childElement").click(function(){
     childElementClicked = true;
  });

  if( childElementClicked != true ) {

   // It is clicked on parent but not on child.
      // Now do some action that you want.
      alert('Clicked on parent');
   
  }else{
      alert('Clicked on child');
    }
    
    childElementClicked = false;
 
});
#parentElement{
width:200px;
height:200px;
background-color:green;
position:relative;
}

#childElement{
margin-top:50px;
margin-left:50px;
width:100px;
height:100px;
background-color:yellow;
position:absolute;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="parentElement">
  <div id="childElement">
  </div>
</div>
Nole
  • 796
  • 7
  • 11