Mastering event bubbling - javascript

Lets say we have a HTML structure like this
<div id="container">
<div id="nested">
<span id="someElement"></span>
</div>
</div>
...and our goal is to have an event listener on the #container only ! So, we bind a listener (jQuery code)
$("#container").on('click', function(event) {
alert("container was clicked");
});
That works of course, but my problem with this approach is that, since events usually bubble up, that listener will also fire if we actually click on #nested or #someElement. My current solution to only handle the click when the #container is clicked is to compare this with event.target
$("#container").on('click', function(event) {
if(this === event.target) {
alert("container was clicked");
}
});
My question: Is that considered "best practice" ? Is there a better way with jQuery to accomplish the same result "out of the box" ?
Example in action: http://jsfiddle.net/FKX7p/

An alternative way to prevent events from bubbling up is to use event.stopPropagation();
$("#container").on('click', function(event) {
alert("container was clicked");
})
.children().on('click', function(event) {
event.stopPropagation();
});
I think the advantage of using this approach is that if you want to attach another event to the nested div, you can just use
$("#nested").on('click', function(event) {
event.stopPropagation();
// some action
});
$("#container").on('click', function(event) {
alert("container was clicked");
});​

I'm not sure which one work's faster but it make sense that the next code will be better:
$("#container").click(function (e) {
if (e.target.id && e.target.id !== "container") return false;
});

Alternative solution:
$("#container").click(function(){
alert("container was clicked");
}).children().click(function(e) {
return false;
});
But your solution is better. jsfiddle.net/FKX7p/2/ (with return false) OR jsfiddle.net/FKX7p/3/ (using stopPropagation)
I prefer use return in your example (code becomes easier to read):
if(this !== event.target) return;

Related

How to determine that two HTML events have the same origin (and then determine "event was processed already")

How to determine that two HTML events have the same origin? What I need is to process click event only once (without stopPropogation because stopPropogation of clicks is "not friednly to dropdowns, modals, etc")
Comparision by originalEvent (using jQuery) if (event.originalEvent != processedEvent.originalEvent) works well, but I want to do the same in vanilla js.
Do we have convinient pure js method to do the same?
var processedEvent = null;
$("#myButton").click(
function(event){
console.log("this should be processed once")
processedEvent = event;
}
)
$("#myPanel").click(
function(event){
if (event.originalEvent == processedEvent.originalEvent)
console.log("nice. we are going to ingore event")
else
console.log("error. event processed second time")
processedEvent=null;
}
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="myPanel">
<button id="myButton">Click Me jQuery</button>
</div>
</div>
PreventDefault is a pretty standard way to do this. You call event.preventDefault() in your innermost handler, then your outer one should check if (!event.isDefaultPrevented()) { for jquery, or if (!event.defaultPrevented) { for vanilla js.
You can add custom properties to the event object as flags, like event._customWasHandled = true, then check for those properties in later handlers.
I was blind. In vanilla javascript opposite to jQuery events are the same...
var processedEvent = null;
document.addEventListener('click', function (event) {
if (!event.target.matches('#myButton2')) return;
console.log("this should be processed once")
processedEvent = event;
}, false);
document.addEventListener('click', function (event) {
if (event == processedEvent)
console.log("nice. we are going to ingore event")
else
console.log("error. event processed second time")
processedEvent=null;
}, false);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="myPanel2">
<button id="myButton2">Click Me Vanilla</button>
</div>

Prevent event firing for checkbox change

I have two event handlers for one single checkbox. I want to first handler to prevent the second one from firing. Here is an example:
$("#address_cb").change(function(event) {
alert('foo');
event.preventDefault();
event.stopPropagation();
return false;
});
$("#address_cb").change(function(event) {
alert('should never display');
});
$("#address_cb").trigger("change");
https://jsfiddle.net/zxzzLkky/5/
How can I achieve it?
You need to use Even.stopImmediatePropagation()
$("#address_cb").change(function(event) {
alert('foo');
event.preventDefault();
event.stopImmediatePropagation(); //Works
return false; //would call event.preventDefault() and event.stopPropagation() but not stopImmediatePropagation()
});
as both of your events fire on the same event level. As an alternative you might just return false for your callback as jQuery will care about the rest.
See event.preventDefault() vs. return false for differences on return false method
Try using event.stopImmediatePropagation() instead of event.stopPropagation();. Please test it propely. Hope this work. Reference
https://api.jquery.com/event.stopimmediatepropagation/
$("#address_cb").change(function(event) {
alert('foo');
event.preventDefault();
event.stopImmediatePropagation();
return false;
});

How to better handle events

If I have multiple events on an element I am currently handling those events as written here:
$("body").on("click", ".element", function(e) {
// Do something on click
});
$("body").on("change", ".element", function(e) {
// Do something on change
});
Is there a way to combine all the events on an element in one on() call? What is the best practice if there are multiple events associated with one element?
$("body").on("change click", ".element", function(e) {
// Can I detect here if it was change or click event and perform an action accordingly?
});
You can use the type property of the event to determine which logic to execute:
$('body').on('change click', '.element', function(e) {
if (e.type == 'click') {
// do something...
}
else if (e.type == 'change') {
// do something else...
}
});
Alternatively you can provide an object to on which contains the functions to bind with the event type names as the keys:
$('body').on({
click: function() {
// do something on click...
},
change: function() {
// do something on change...
}
}, '.element');
Personally I would use the latter method. The whole point of having a unified on() handler is negated when using a rather ugly if statement to split the event types.
Yes! jQuery passes the event object which contain the event information:
$("body").on("change click", ".element", function(e) {
console.log(e.type);
});
You can use the event.type. Some will say it's bad practice and others may find it useful.
$("body").on("change click", ".element", function(event) {
switch (event.type) {
case 'click':
break;
case 'change':
break;
default:
}
});
jQuery event.type
$('#element').on('keyup keypress blur change', function(event) {
alert(event.type); // keyup OR keypress OR blur OR change
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text" id="element" />

check multiple events - jQuery

Suppose I want to run a function myFunction at each of the events $(document).ready, $(sometag).on('click',....). How can I construct a function that checks if any of those two events are triggered, and then run the method. Can I pass $(document) as an argument and then check $(document).isReady or check $(document).click(function(e){if (e.target.is($(some tag))) ...}). Is this correct ?
It's not easy to understand what the heck you are talking about, but it sounds like you're trying to attach an event handler and trigger it on document ready, and if so you'd do that like this :
$(document).ready(function() {
$(sometag).on('click', function() {
// do stuff
}).trigger('click');
});
If I understand you correctly:
function myFunc(event) {
if (event.type == 'ready')
console.log('It is a document.ready');
else if (event.type == 'click')
console.log('It is a click');
}
$(document).on('ready', myFunc).on('click', 'a', myFunc);
jsfiddle
from what i could understand from your question...
Jsfiddle: http://jsfiddle.net/patelmilanb1/eR4wG/1/
$('.checkbox').change(function (e) {
if (e.isTrigger) {
alert('not a human');
} else {
alert("manual check by human");
}
});
$('.checkbox').trigger('change'); //alert not a human because it is automatically triggered.
I may not understand your question very much, but try this:
$(function(){
$('div1,div2,#id1,#id2,.class1,.class2').click(function(){
// do something
yourFunction();
});
});
triggering function on multiple events of an element, you may try:
$('#element').on('keyup keypress blur change', function() {
...
});
and multiple function on multiple elements, try:
$('#element #element1 #element2').on('keyup keypress blur change', function() {
...
});

html div onclick event

I have one html div on my jsp page, on that i have put one anchor tag, please find code below for that,
<div class="expandable-panel-heading">
<h2>
<a id="ancherComplaint" href="#addComplaint"
onclick="markActiveLink(this);">ABC</a>
</h2>
</div>
js code
$('.expandable-panel-heading:not(#ancherComplaint)').click(function () {
alert('123');
});
function markActiveLink(el) {
alert($(el).attr("id"));
}
here I when I click on div I got alert with 123 message, its fine but when I click on ABC I want message I want to call markActiveLink method.
JSFiddle
what is wrong with my code? please help me out.
The problem was that clicking the anchor still triggered a click in your <div>. That's called "event bubbling".
In fact, there are multiple solutions:
Checking in the DIV click event handler whether the actual target element was the anchor
→ jsFiddle
$('.expandable-panel-heading').click(function (evt) {
if (evt.target.tagName != "A") {
alert('123');
}
// Also possible if conditions:
// - evt.target.id != "ancherComplaint"
// - !$(evt.target).is("#ancherComplaint")
});
$("#ancherComplaint").click(function () {
alert($(this).attr("id"));
});
Stopping the event propagation from the anchor click listener
→ jsFiddle
$("#ancherComplaint").click(function (evt) {
evt.stopPropagation();
alert($(this).attr("id"));
});
As you may have noticed, I have removed the following selector part from my examples:
:not(#ancherComplaint)
This was unnecessary because there is no element with the class .expandable-panel-heading which also have #ancherComplaint as its ID.
I assume that you wanted to suppress the event for the anchor. That cannot work in that manner because both selectors (yours and mine) select the exact same DIV. The selector has no influence on the listener when it is called; it only sets the list of elements to which the listeners should be registered. Since this list is the same in both versions, there exists no difference.
Try this
$('.expandable-panel-heading:not(#ancherComplaint)').click(function () {
alert('123');
});
$('#ancherComplaint').click(function (event) {
alert($(this).attr("id"));
event.stopPropagation()
})
DEMO
Try following :
$('.expandable-panel-heading').click(function (e) {
if(e.target.nodeName == 'A'){
markActiveLink(e.target)
return;
}else{
alert('123');
}
});
function markActiveLink(el) {
alert($(el).attr("id"));
}
Here is the working demo : http://jsfiddle.net/JVrNc/4/
Change your jQuery code with this. It will alert the id of the a.
$('.expandable-panel-heading:not(#ancherComplaint)').click(function () {
markActiveLink();
alert('123');
});
function markActiveLink(el) {
var el = $('a').attr("id")
alert(el);
}
Demo
You need to read up on event bubbling and for sure remove inline event handling if you have jQuery anyway
Test the click on the div and examine the target
Live Demo
$(".expandable-panel-heading").on("click",function (e) {
if (e.target.id =="ancherComplaint") { // or test the tag
e.preventDefault(); // or e.stopPropagation()
markActiveLink(e.target);
}
else alert('123');
});
function markActiveLink(el) {
alert(el.id);
}
I would have used stopPropagation like this:
$('.expandable-panel-heading:not(#ancherComplaint)').click(function () {
alert('123');
});
$('#ancherComplaint').on('click',function(e){
e.stopPropagation();
alert('hiiiiiiiiii');
});
Try out this example, the onclick is still called from your HTML, and event bubbling is stopped.
<div class="expandable-panel-heading">
<h2>
<a id="ancherComplaint" href="#addComplaint" onclick="markActiveLink(this);event.stopPropagation();">ABC</a>
</h2>
</div>
http://jsfiddle.net/NXML7/1/
put your jquery function inside ready function for call click event:
$(document).ready(function() {
$("#ancherComplaint").click(function () {
alert($(this).attr("id"));
});
});
when click on div alert key
$(document).delegate(".searchbtn", "click", function() {
var key=$.trim($('#txtkey').val());
alert(key);
});

Categories

Resources