Prevent parent event from triggering child events - javascript

So there are plenty of questions asking how to keep child events from triggering parent events, but I can't seem to find any for the opposite.
I toggle a parent event on and off like so:
var body_event = function(e){
console.log('test');
};
$('#btn').toggle(
function(e){
$('body').bind('click', body_event);
},
function(){
$('body').unbind('click', body_event);
}
);
Currently if I run this on a page like this:
<body>
<div id="btn">click to activate</div>
<div id="example" onclick="alert('do not show this when event is bound')">
try the body event by clicking here
</div>
</body>
the toggle works fine, but when I click on "try the body event by clicking here" the alert will still show up. I want to be able to ignore all child events when the body event is bound without individually referencing the child events.
In other words I'm looking for a better solution than doing something like this on toggle:
$('#example").attr('onclick', '');

This is close, but not quite there. It doesn't check whether the specific function is bound, just whether there are any events bound to the jQuery object.
There must be a way to query the events to find if one is bound to click, and then subsequently what function it points too. Hopefully this gets you started though.
http://jsfiddle.net/PhjB8/
var body_event = function(e) {
alert('test');
};
$('#btn').toggle(
function(e) {
$('body').addClass('active').bind('click', body_event);
}, function() {
$('body').removeClass('active').unbind('click', body_event);
});
$('#example').click(function() {
//debugger;
if ($('body').data('events') == undefined) {
alert('do not show this when event is bound');
}
});​

event.stopPropagation() can solve this problem, inside body_event?

Related

How to wrap multiple dynamic eventListeners into one?

I just started to learn js and need a little help: I have the following function:
//SET CHAT BEHAVIOR
function chatSettings() {
console.log('ChatSettings called')
function BtnAndScrollBar(texteditor) {
console.log('BTNAndScrollBar called');
const sendBtn = $('.cl.active').find('.sendBtn');
const attachBtn = $('.cl.active').find('.attachBtn');
console.log(sendBtn)
}
function sendAndDeleteMessage(send) {
console.log(send);
}
var sendBtn = $('.cl.active').find('.sendBtn');
sendBtn.mousedown(function () {
sendAndDeleteMessage(this);
});
var textEditor1 = $('.cl.active').find('.chatTextarea');
textEditor1.on('focus change mousedown mouseout keyup mouseup', function (){
console.log(this);
BtnAndScrollBar(this)
});
}
$('document').ready(function () {
console.log('hello');
$('.tabs').tabs();
chatSettings();
});
I prepared a js.fiddle - As you can see from console.log when clicking into the textarea, the eventListener always listens to #cl1, even if .cl.active switches along with the according TAB.
The events in the textarea are just relevant, if .cl is active. My target is to wrap all three eventListener into one and apply the event to the textarea in the active stream, but all I tried went wrong... Can anyone help? #Dontrepeatyourself #DRY
$(".chatTextarea").on(
'focus change mousedown mouseout keyup mouseup',
function (this) {
//this.id can contain the unique id
greatFunction(this);
});
This will bind event individually with unique id found with this keyword and also wraps all event listener into one function but this is better when you want to process each event with same functionality
please let me know if this helps.
Peace
$(".cl textarea").on('focus change mousedown mouseout keyup mouseup', function () {
greatFunction(this)
});
Tada!
P.S. Is there a reason greatFunction is defined inside window.onload?
Try using $(document).ready function to load code when the page loads.
Also use $('textarea #cl1').on to get the textarea with the #cl1 or whichever id you want to use and then call the function after using the .on.
Hope this helps!
Let me know if it works!
$(document).ready(function () {
function greatFunction(elem) {
//do stuff
}
$('textarea').on('focus change mousedown mouseout keyup mouseup', function () {
greatFunction(this)
});
}
First off, I changed the onload to bind with jQuery, so all your logic is doing jQuery bindings, rather than swapping back and forth between jQuery and vanilla javascript. Also, doing an actual binding removes an inline binding.
Next, the binding has been condensed into a single delegate event listener. Since you eluded in your comments that it wasn't working for the active element after the active was moved or added, this reflected that you were dealing with dynamic elements. Delegate event listeners are one way to handle such things.
Delegate event listeners bind on a parent element of the elements that will change, or be created. It then waits for an event to happen on one of it's children. When it gets an event it is listening for, it then checks to see if the element that it originated from matches the child selector (second argument) for the listener. If it does match, it will then process the event for the child element.
Lastly, I added some buttons to swap around the active class, so you could see in the snippet that the event handler will start working for any element that you make active, regardless of it starting out that way.
$(window).on('load', function () {
function greatFunction (elem) {
console.log(elem.value);
}
$(document.body).on(
'focus change mousedown mouseout keyup mouseup',
'.cl.active .chatTextarea',
function () {
greatFunction(this);
}
);
$('.makeActive').on('click', function () {
$('.active').removeClass('active');
$(this).closest('div').addClass('active');
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="cl1" class="cl active"><textarea class="chatTextarea">aa</textarea><button class="makeActive">Make Active</button></div>
<div id="cl2" class="cl"><textarea class="chatTextarea">bb</textarea><button class="makeActive">Make Active</button></div>
<div id="cl3" class="cl"><textarea class="chatTextarea">cc</textarea><button class="makeActive">Make Active</button></div>

Jquery toggle div, allow clicking outside of div to close, also allow everything inside the div clickable

I have a toggle event on a div, and I've seen many questions regarding mine but nothing seems to work for what I'm trying to do, it either makes my div disappear in a few seconds, or it just doesn't work. Here's my jsfiddle.
I have a div that needs to toggle when another <div> is clicked. The toggled div has inputs in it that need to be filled out, and a submit button inside it as well. So I need clicks inside the div to be allowed, but only inside my div. So I want the div to show unless the user clicks outside of this div.
I'm using this query which toggles fine:
$('#MyDiv').click(function (event) {
$("#ToggledDiv").slideToggle();
});
And then this coding to hide it when clicked outside of the div which doesn't work:
$(window).click(function () {
$("ToggledDiv").hide();
});
I've tried solutions with e.preventDefault(); but that doesn't work, or $(document).click, even mousedown but it just doesn't flow how I want, it'll hide it within a few seconds, or it will prevent the toggle from even working so I'm lost.
The reason behind this behavior is Event Bubbling and Capturing of HTML DOM API. You can use event.stopPropagation() OR event.cancelBubble = true to prevents the event from bubbling up to the DOM tree, preventing any parent handlers from being notified of the event.
Another good article: events order
$('#MyDiv').click(function(event) {
$("#ToggledDiv").show();
disabledEventPropagation(event);
//console.log('2nd event');
});
$('#ToggledDiv').click(function(event) {
disabledEventPropagation(event);
//console.log('3rd event');
});
$(document).click(function() {
$("#ToggledDiv").hide();
//console.log('1st event');
});
function disabledEventPropagation(event) {
if (event.stopPropagation) {
event.stopPropagation();
} else if (window.event) {
window.event.cancelBubble = true;
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="MyDiv" style="background-color:yellow">
click me to open
</div>
<div id="ToggledDiv" style="display: none;background-color:yellow">
<input type="text" />
<input type="submit" />
</div>
Take a look at the event when you click inside the #targetDiv. There are two properties you can use to evaluate what action to perform: event.target and event.currentTarget. In this case:
$('#ToggledDiv').on('click', function(event) {
console.log(event.target, event.currentTarget);
});
This is a good way to see if what clicked is actual target or a child element in the target.
To add to Chris' answer, you can see here that I check that the e.target is not inside the form using vanilla Node.contains, and also not the button...
https://jsfiddle.net/jmLdp45s/3/
var $button = $('button');
var $form = $('form');
$button.click(function() {
$form.slideToggle();
});
$(window).click(function(event) {
if (
!$form.get(0).contains( event.target ) // target is not inside form
&& event.target !== $button.get(0) // target is not button
) $form.hide();
});

If else statement for javascript click event

I have two statements. What I am trying to do is when someone clicks on #area_a then hide then entire #area_b div without activating the focusout for the #area_b_textbox. But I've tried different code (which I am not including here because it is incorrect and want to get your suggestions) and what is happening is it is activating the focusout everytime I click on the #area_a div.
JQuery base actions
$("#area_a").click(function() { $("#area_b").hide(); });
$("#area_b_textbox").focusout(function() {$("#area_b_error").show();});
HTML:
<div id="area_a"></div>
<div id="area_b">
<input id="area_b_textbox">
<div id="area_b_error"></div>
</div>
Thanks!
You could hack around the problem with a timer. Timers usually smell bad but I think it is your safest bet here. If you try using hover or other mouse events you might run into trouble with keyboard navigation and activation or the lack of "hoverish" events on touch interfaces (and we can't pretend those don't exist anymore).
Something like this:
var timer_kludge = {
start: function(fn) {
this.id = setTimeout(fn, 200);
},
stop: function() {
if(this.id)
clearTimeout(this.id);
this.id = null;
},
id: null
};
$('#area_a').click(function() {
timer_kludge.stop();
$('#out').append('<p>click</p>');
});
$('#area_b_textbox').focusout(function() {
timer_kludge.start(function() {
$('#out').append('<p>textarea focusout</p>');
});
});
$('#area_b_textbox').focusin(function() {
timer_kludge.stop();
});
Demo: http://jsfiddle.net/ambiguous/s8kw8/1/
You'd want to play with the 200 timeout a bit to see what works best in your circumstances.
Why not just add a flag to ignore next focusout (blur?) event.
ignoreNextFocus = false;
$("#area_a").click(function() { ignoreNextFocus=true; $("#area_b").hide(); });
$("#area_b_textbox").focusout(function() { if(!ignoreNextFocus)$("#area_b_error").show();ignoreNextFocus=false;});
On that note setting the flag on click event might be too late. If it is the case, try mousedown event.
this is not possible since you loose the focus automatically when you click somewhere else...
What you need to do is to unbind the focusout event on hover of the #area_a and rebind it later on...
$("#area_a").click(function() {
$("#area_b").hide()
}),hover(
function(){
$("#area_b_textbox").unbind("focusout")
},
function(){
$("#area_b_textbox").focusout(function() {$("#area_b_error").show();});
}
)
PS: what is your ultimate goal here?
I'm not sure this is possible since by definition the focus has to leave the #area_b_textbox if the user is going to click a button.

Jquery - hide on click anywhere on document

I have a div that hides whenever you click outside of it but I'm having some trouble getting certain links inside the div to work (and not hide the div).
$(document).click(function() {
fav.hide();
});
theDiv.click(function(e) {
e.stopPropagation();
});
That is what I have for the whole clicking outside and closing event. Heres the thing: I have two types of links in my div, one regular link and another which is a javascript one. The regular one redirects OK but the javascript one doesn't do anything.
Could somebody help me out? Thanks.
EDIT:
Here are the bits of my code that might help out
var fav = $('#favorites');
// Open & close button
$('#favorites_a').click(function(e) {
e.stopPropagation();
e.preventDefault();
fav.toggle();
});
$('a.c',fav).live('click', function(e) {
alert('hey');
});
$(document).click(function() {
fav.hide();
});
fav.click(function(e) {
e.stopPropagation();
});
HTML (built after page load):
<div id="favorites">
<div class="wrap">
<ul><li>AB</li></ul>
</div>
</div>
This could be a problem with the live() click event handler. When you use live the event handler is actually attached to the document. So the event needs to bubble up to the document, but your click event handler on fav prevents bubbling up.
It works with delegate though:
fav.delegate('a.c', 'click', function(e) {
alert('hey');
});
Here, the event handler is added to fav.
DEMO
I think your code for fav is preventing the 'B' link from working. Instead of .live(), try:
$('a.c').click(function() { ... });
instead.

Add click event to Div and go to first link found

I think I've been too much time looking at this function and just got stuck trying to figure out the nice clean way to do it.
It's a jQuery function that adds a click event to any div that has a click CSS class. When that div.click is clicked it redirects the user to the first link found in it.
function clickabledivs() {
$('.click').each(
function (intIndex) {
$(this).bind("click", function(){
window.location = $( "#"+$(this).attr('id')+" a:first-child" ).attr('href');
});
}
);
}
The code simply works although I'm pretty sure there is a fairly better way to accomplish it, specially the selector I am using: $( "#"+$(this).attr('id')+" a:first-child" ). Everything looks long and slow. Any ideas?
Please let me know if you need more details.
PS: I've found some really nice jQuery benchmarking reference from Project2k.de here:
http://blog.projekt2k.de/2010/01/benchmarking-jquery-1-4/
Depending on how many of these div.click elements you have, you may want to use event delegation to handle these clicks. This means using a single event handler for all divs that have the click class. Then, inside that event handler, your callback acts based on which div.click the event originated from. Like this:
$('#div-click-parent').click(function (event)
{
var $target = $(event.target); // the element that fired the original click event
if ($target.is('div.click'))
{
window.location.href = $target.find('a').attr('href');
}
});
Fewer event handlers means better scaling - more div.click elements won't slow down your event handling.
optimized delegation with jQuery 1.7+
$('#div-click-parent').on('click', 'div.click', function () {
window.location.href = $(this).find('a').attr('href');
});
Instead of binding all the clicks on load, why not bind them on click? Should be much more optimal.
$(document).ready(function() {
$('.click').click(function() {
window.location = $(this).children('a:first').attr('href');
return false;
});
});
I would probably do something like;
$('.click').click(function(e){
window.location.href = $(this).find('a').attr('href');
});

Categories