views:

73

answers:

4

I'm asked that a click anywhere in a div does a specific action (say collapse it) unless the click was on a link, a button, etc...

Basically, I'd like the reverse of event.preventDefault().

Is there a good and easy way to do this?

I'm looking for a generic solution here; I would like to not assume much about the content of the div.

Might look like:

<div id="click_me>
<p>Clicking here should do stuff
   <a href="http://stackoverflow.com"&gt;but not here, nor on the following image</a>
   <a href="/xyz"><img url="/icon.png"/></a>
   imagine buttons... input areas, ...
</p>
</div>

With the following Javascript:

$("#click_me").click(function(){
  if(black_magic) {
    $("#click_me").toggleClass("collapsed");
  }
});
+1  A: 

Event bubbling is the keyword here. Bind an event handler to the div and check the event target to specify what to do.

$('div.mydivclass').bind('click', function(event){
    switch(event.target.id){
        case 'id_of_an_anchor':{
              alert('anchor was clicked');
              break;
        }
        case 'id_of_a_span':{
              alert('span was clicked');
              break;
        }
        default: {
              alert('something else was clicked within me');
        }
    }
});

Of course you can even check for the targets tagName or nodeType.

jAndy
What if the target is inside an `a`, for example?
Marc-André Lafortune
+2  A: 

You just have to make sure the event target is not a link nor a button.

$('#click_me').click(function(e) {
  interactive = 'a, button, input, textarea, video, map, object';
  if($(event.target).closest(interactive).length == 0) ) {
    $("#click_me").toggleClass("collapsed");
  }
});
remi
Doesn't work, for example if the click is on an image inside an `a`
Marc-André Lafortune
That's true, I'll update my code.
remi
Getting closer! I edited your answer to make it more complete. Still, I still wish there was an easier way, and one that didn't force me to stop bubbling if I want to attach event handlers to some nested divs. Maybe someone else can come up with a better answer.
Marc-André Lafortune
instead of interactive="a,button,input" etc, why not just use .closest('.keepDefault') and add that class to every element that needs to do the default behavior?
resopollution
@resopollution: I feel it's not the responsability of the content that the container behaves in a certain way. The container can be very generic, like a piece of GUI that can be used in many places on the site.
Marc-André Lafortune
+2  A: 

Just add this handler to your link:

$("#click_me a,button,input,textarea,video,map,object").click(function(e){
   e.stopPropagation();
});

To prevent the event to get to the div (bubble up). It will stop so the link will behave correctly.

See it in action. (click preview)

galambalazs
Gorgeous preview! What about stuff inside `a`, for instance?
Marc-André Lafortune
Stuff inside <a> "bubbles up" all the way to <a>, so if a span inside <a> gets clicked, <a> also gets clicked, but e.stopPropagation() prevents further bubbling up, so in this case your div outside of <a> will not collapse. If you don't want <a> to be clicked if something inside <a> like a <span> is clicked, you can bind a click handler to the <span> tag inside <a> and do e.stopPropagation to stop the bubbling.
resopollution
@galambalaz: First time on jsbin, very nice. No cynicism at all. Indeed, that works well even for embedded stuff. I've modified it to allow for buttons, etc...
Marc-André Lafortune
it works for me: http://jsbin.com/areju3/3/edit
galambalazs
I'm glad then:)
galambalazs
@resopollution: Good explanation, thanks
Marc-André Lafortune
A: 
function onClick(e){
    var gates = "A,INPUT,TEXTAREA,SELECT";
    var bound = this;
    var isCollapse = function ( node ){ 
    if( node == bound ){ return true; }
        var re = new RegExp( "\\b" +node.nodeName+ "\\b", "g" );
        return re.test( gates ) ? false : isCollapse( node.parentNode );
    };

    if( isCollapse( event.srcElement || e.target ) ){
        alert( "collapse" )
        // collapse()
    }
}
document.getElementById("click_me").onclick = onClick;

*fixed * for cases like: <a href="_"><span><strike> a link </strike></span></a>

yulerz
I prefer the style of the other solutions, plus I think it won't work when clicking on me: `<select>Pick <b>me</b></select>`, for example.
Marc-André Lafortune
Thanks to remind me of the condition of nested tags. :)
yulerz