How to stop propagation of a bound function not the entire event? - javascript

I have a click function bound to many elements. It is possible that sometimes these elements may sit within one another. So, the click event is bound to a child and also bound to its parent. The method is specific to the element clicked. Naturally, because of event bubbling, the child's event is fired first, and then the parents. I cannot have them both called at the same time because the parents event overwrites the event of the child. So I could use event.stopPropagation() so only the first element clicked receives the event. The problem is that there are other click events also attached to the element, for example, I am using jQuery's draggable on these elements. If I stop the propagation of the click event, then draggable doesn't work, and the following click events are not called.
So my question is: Is there a way to stop the event bubbling of the method the event will call and not the entire event?
Brilliant John, but here is the problem:
<div id="Elm1"><!-- relative -->
<div class="Elmchildren"></div><!-- absolute-->
<div class="Elmchildren"></div><!-- absolute-->
<div class="Elmchildren"></div><!-- absolute-->
<div id="Elm2"><!-- relative -->
<div class="Elmchildren"></div><!-- absolute-->
<div class="Elmchildren"></div><!-- absolute-->
<div class="Elmchildren"></div><!-- absolute-->
</div>
</div>
Click event is bound to #Elm1 and #Elm2. The .Elmchildren are width and height 100%. So they are actually the current targets.

try someting like this
$(mySelector).click(function(evt) {
if (evt.target == evt.currentTarget) {
///run your code. The if statment will only run this click event on the target element
///all other click events will still run.
}
});

The suggested solution
evt.target == evt.currentTarget
is nice, but there are cases where it does not help.
Example: A (suckerfish-style) menu structure with nested ul/li lists.
The mousemove event comes from a link inside a list item, which is a child of an ul-list, which is again a child of another list item. Typical for a html menu structure with submenus.
The evt.target would be the link tag, but we are interested in the mousemove on the list item.
Even worse: The link tag could contain span or img tags or other nested stuff. Then evt.target would be this span or img.
What seems to work here is to catch the event on a parent / root item, and then check the parents of evt.target.
Like this (with jQuery),
var $menu = $('div#menu');
$('body').mousemove(function(evt){
var element = evt.target;
// find the deepest list item that was affected by this mouseover event.
var list_item;
var in_menu = false;
while (element) {
if (element == $menu[0]) {
in_menu = true;
break;
}
else if (!list_item && element.tagName == 'LI') {
// we found a list item, but we are not sure if we are inside the menu tree.
list_item = element;
}
}
// do something with the result.
if (!in_menu) {
.. // close all submenus
}
if (list_item) {
.. // open the submenu for this list item.
}
else {
// mouse in menu, but not hovering an item.
// leave the submenus open. (?)
}
});
Maybe some of this could be abbreviated with jQuery like $(evt.target).parents().is($menu), but I did not get this to work. Also, I would guess that this explicit loop with element.tagName is faster.

Related

Launch event on click of element but on things inside element

Hello I made drawer which appears when you click on it and then expands it's width, after second click, it back to it's minimalized form, inside this drawer I have table with checkboxes etc which I will use later to filter things. Thing is that if I click on checkbox inside the table, drawer is hiding. How to do it that event which allow for toggling of width of drawer would work only if I click on it except place where is table? As far as I know it should do something with stopPropagation but somehow I was unabled to implement it in working form.
<div class="filter_drawer" onclick="show_stat()">
<Table class="stat_table">
...some content
</Table>
</div>
function show_stat() {
event.stopPropagation()
var stats = document.querySelector('.filter_drawer')
var table = stats.querySelector('.stat_table')
var panels = document.querySelector('.main_container')
stats.classList.toggle('expanded_stat')
table.classList.toggle('show')
panels.classList.toggle('expanded_stat_panel')
}
Attach the listener to the element properly using Javascript instead, and then you can check if the event.target is the .filter_drawer element. If so, then it was a click directly on the element, and not one of its descendants:
document.querySelector('.filter_drawer').addEventListener('click', (e) => {
if (e.target.matches('.filter_drawer')) {
showStat();
}
});
(If the click was on a descendant element, and not the filter_drawer itself, then the e.target test won't match)
No need for stopPropagation.

Two OnClick events overlap

I have an element inside an element, when I click the element underneath I want the slider to open. When I click on the outermost element I want the slider to close.
Unfortunately when I click on the outermost it clicks the underneath element as well. Is there a way to click only on the outermost element ignoring the click on the underneath element? The events are triggered on click and executed with javascript.
I tried with z-index but it still captures the underneath element clicked as well, and because the functions are contrary to one another nothing happens.
edit: on a "code is worth 1000 words" tip
var $target = $(this).data('pos', i) //give each li an index #
$target.data('hpaneloffsetw', $target.find('.hpanel:eq(0)').outerWidth()) //get offset width of each .hpanel DIV (config.dimensions.fullw + any DIV padding)
$target[haccordion.ismobile? "click" : "mouseenter"](function(){
haccordion.expandli(config.accordionid, this)
config.$lastexpanded=$(this)
})
if (config.collapsecurrent){ //if previous content should be contracted when expanding current
$('.close').click(function(){
$target.stop().animate({width:config.paneldimensions.peekw}, config.speed) //contract previous content
})
$target.dblclick(function(){
$(this).stop().animate({width:config.paneldimensions.peekw}, config.speed) //contract previous content
})
}
Because the code is borrowed, I don't understand much of it. But basically I want the "click" : "mousteenter" function to work on click, without interfering with the .close().click
It sounds like you need to stop the click event bubbling up the DOM to be caught by parent elements. You can use stopPropagation() to do this:
$('.close').click(function(e) {
e.stopPropagation();
$target.stop().animate({ width: config.paneldimensions.peekw }, config.speed);
})
$target.dblclick(function(e) {
e.stopPropagation();
$(this).stop().animate({ width: config.paneldimensions.peekw }, config.speed);
})
Try the following fiddle
$("#outer").click(function(){alert("outer clicked")});
$("#inner").click(function(e){
alert("inner clicked")
e.stopPropagation();
});
To identify the element you have "really" clicked on, you can try to identify it through accessing the target property of the jquery-event-object.
After you identified the target you clicked on, you could prevent other event handlers from firing.
Use CSS specific jquery to point exact element like below, use > to point exact child
table > tbody > tr > td > input[type='text']
like this.

Masonry - deleting elements by clicking on something else, than the element itself

In Masonry, it is possible to delete an element by clicking on it. The catch is, that You have to click directly on that element - so if you use these "bricks" as an image gallery (as long as these photos are included as a background image) You can delete them, by clicking on the element. The problem is, when you use these as some messages/info/other content containers. Then, due to formatting-related stuff the parent element gets "hidden" behind other tags, and You can't actually click on it.
The problem is shown here:
http://jsfiddle.net/dan1410/SfU5T/
You can close red boxes, but not green ones, as they are overlapped by another elements.
I've tried code like:
eventie.bind( container, 'click', function( event ) {
// don't proceed if item was not clicked on
if ( !classie.has( event.target, 'closeable' ) ) {
return;
}
// remove clicked element
msnry.remove( event.target );
// layout remaining item elements
msnry.layout();
});
});
and
var todelete = document.querySelector('.closeable');
eventie.bind( container, 'click', function( event ) {
// remove clicked element
msnry.remove( todelete );
// layout remaining item elements
msnry.layout();
});
});
but You still have to click directly on the element You'd like to close...
My masonry content structure looks like
<div id="masonry" >
<div class="item blue closeable">
<div id="itheader"><h2 class="secsectiontitle">Space available</h2></div>
<div id="itcontent">
some statistics here...<br/>
and here, too
</div>
</div>
Only elements with .closeable class are supposed to be closeable.
So, the question is: how to close an element using a button/a link?
I'm not very familiar with JS, so I'd like to ask You guys for help. Thank You in advance!
Unless there are handlers that stops the propagation of the click event on children elements, the click event should bubble up without any issues.
Also, if you are using jQuery, you should use the jQuery Masonry's API.
Note: I couldn't access your fiddle and couldn't test the solution
var $container = $('#masonry').on('click', '.closeable', function (e) {
$container.masonry('remove', e.currentTarget);
$container.masonry(); //layout
$container.masonry('reloadItems'); //OP said it was also required
});

jquery event bubbling and event.targets

I'm trying to get my head around how to stop a click event from bubbling up,out,down when a particular element is clicked.
<div class="clickable">
clicking here shouldn't affect parents or children
<div class="clickable">
clicking here shouldn't affect parents or children
<div class="clickable">
clicking here shouldn't affect parents
</div>
</div>
</div>
Basically, if an element with the class "clickable" is clicked, I only want that item to be affected, toggling an even/odd class.
Here's the fiddle: http://jsfiddle.net/LDaA7/
When you run the fiddle and click a particular item, you'll see that various parents and children also get toggled.
From experimenting, event.stopPropagation(), returning false/true, .one, etc. impacts click events not targets per se. I may want other (unrelated) click events to work.
How do I target only the element I'm clicking?
You can just check if the target equals the bound element (this):
$("body").on "click", ".clickable", (event) ->
if this is event.target
clickTarget = $(event.currentTarget).closest(".clickable")
clickTarget.addClass "clicked"
if clickTarget.hasClass "odd"
clickTarget.addClass("even").removeClass("odd")
else
clickTarget.addClass("odd").removeClass("even")
FIDDLE
You want to use $(this) to target the element you have clicked. Also, .stopPropagation() so it won't bubble up the events.
See Demo here
$('.clickable').click(function (e) {
e.stopPropagation();
var elem = $(this);
elem.addClass('clicked');
if (elem.hasClass('odd')) {
elem.addClass('even');
elem.removeClass('odd');
} else {
elem.removeClass('even');
elem.addClass('odd');
}
});

Div slide up when click in items inside it

I have a div that slides down (opens) when I click a certain input select. Inside this div, I have some other input selects and my objective is to slide up the div when I click on the rest of the page.
My problem:
The div slides up when I select some item in the input selects inside it, and I don't want this to happen.
Is there some way to slide up the div only when I click outside it?
Why are the selects inside the div making it to slide up as well?
This is my javascript to slide up the div:
$(document).mouseup(function (e) {
var container = $('.myDiv');
if (container.has(e.target).length === 0) {
$(container).removeClass('close');
$(container).addClass('open');
$(container).next().slideUp(200);
}
});
The div slides up when I select some item in the input selects inside
it, and I don't want this to happen.
Is there some way to slide up the div only when I click outside it?
Why are the selects inside the div making it to slide up as well?
Use event.stopPropagation() on the child element(s) to prevent the slide event being triggered by them.
event.stopPropagation - Prevents the event from bubbling up the DOM
tree, preventing any parent handlers from being notified of the event.
Here's a simple jsFiddle and the basic example below.
jQuery:
$('div').click(function(){
$('#slideMeUp').slideUp();
});
$('select').click(function(e){
e.stopPropagation();
});
HTML:
<div>Clicking here will trigger it!
<select>
<option>This won't trigger the click event anymore!</option>
</select>
Clicking here will also trigger it!
</div>
<div id="slideMeUp">Only clicking the outer div will slide me up!</div>
you can check you e.target.id with your container id
$(document).mouseup(function (e) {
var container = $('.myDiv');
if(e.target.id==$(this).attr('id'))
{
if (container.has(e.target).length === 0) {
$(container).removeClass('close');
$(container).addClass('open');
$(container).next().slideUp(200);
}
}
});
If it's specific items that are causing problems, perhaps something like this?
On click tell it to to slideUp your div as long as the clicked item is "not" in the list.
('body').click(function(event) {
if (!$(event.target).is('#id_of_item .class_to_ignore')) {
var container = $('.myDiv');
$(container).removeClass('close');
$(container).addClass('open');
$(container).next().slideUp(200);
}
});
I'd suggest binding mousedown rather than mouseup. There are inconsistencies in behaviour between browsers, this may have something to do with your issue.
You should also add some logic to check the clicked element isn't the div itself, clicking just outside of an input, but still inside of the div would currently cause the slideUp to occur.
if (!container.has(e.target).length && !container.is(e.target)) {
...
}
Other than that, it should work fine.
Have a fiddle

Categories