JQuery detecting click interactions on nested elements causing huge loop - javascript

Attempting to add a click event to a button that is nested between multiple elements (ul, div.col, li, div.panel, div.panel-body). I am able to access it with the below code, however as if I click on the page more than a few times the console.logs begin to loop and execute thousands of times.
I am nearly positive it is the nesting of the functions causing this. But I do not have a thorough enough background in JQuery to be able to tell exactly what the browser is doing.
Here is my JQuery code:
$('#displayList #col1').click(function(){
console.log('clicked col');
$('li').click(function(){
var id = $(this).attr('id');
console.log(id);
$('div.panel').click(function(){
console.log('clicked panel');
$('div.panel-body').click(function(){
console.log('clicked panel body');
$('button').click(function(){
console.log('clicked button');
return;
});
return;
});
return;
});
return;
});
return;
});
Could one of you wonderful JQuery gurus explain what is causing this bug and point me in a better path for checking if the button has been clicked or if it hasnt(just the panel was clicked).
Also, does anyone know a good and preferably free debugging program that I can use to step through JQuery code execution by execution?
Thank you so much for your knowledge

You are binding an event handler inside another event handler. So whenever the later event occurs, new handlers are added, Eventually causing multiple handlers for same event on same element.
Your code should look something like
$('#displayList #col1').click(function(){
console.log('clicked col');
});
$('li').click(function(){
var id = $(this).attr('id');
console.log(id);
});
$('div.panel').click(function(){
console.log('clicked panel');
});
$('div.panel-body').click(function(){
console.log('clicked panel body');
});
$('button').click(function(){
// The following will be executed when the button is clicked
console.log('clicked button');
});
Assume you have the following markup:
<div id="container>
<div id="parent">
<button>Click Me!</button>
</div>
</div>
And the event handlers:
$("#container").click(function(){
console.log("container got a click!");
});
$("#parent").click(function(){
console.log("parent got a click!");
});
$("#parent button").click(function(){
console.log("button got a click!");
});
Now if you click the button output will be
//button got a click!
//parent got a click!
//container got a click!
When you click an element, all of it's ancestors will also receive a click event by default, and the corresponding event handler will be called, if any - This is called event bubbling.
If you don't need any specific functionality then you don't need a handler so bubling won't hurt.
However, You can prevent this behaviour by calling the stopPropagation() method of event object inside the event handler:
$("#parent button").click(function(e){
e.stopPropagation();
console.log("button got a click!");
});
output will be
//button got a click!
There is one more event propagation method in some browsers (Only event bubbling model is supported by all the major browsers), you can read about it in detail #quirksmore: Event order
As DFTR mentioned in comments,
Visual studio allows break points in javascript code, when launching an application within internet explorer. Another debugging tool is firebug, which is a pretty neat firefox extension

Might help you.
if you need to bind event to button, you should use descendant selector
$('#displayList #col1 li div.panel div.panel-body button').click(function() {
console.log('clicked button');
});

Related

Not calling click event

I have kind of strange problem.
I'm trying to add a couple of events to som DOM elements (all existing, some initially hidden:
$self.on("focus", function () {
$self.next().css("display", "inline-block");
});
$self.on("blur", function () {
$(this).next().hide();
});
$self.parent().find(".icon-ok").on("click", function() {
console.log("icon.ok")
});
You can see the relevant part of the DOM here (self is the span user-name):
Later on, the element eventually because visible and I can click on it. However, the event handler is never called. If I remove the blur event, than the click event works. However, I need both.
What's going on here?
How can I fix it?
Looks like the blur cancels out the click (due to event order) but using mousedown instead of blur may help you get both.
UPDATE: Added code based on comment
$self.parent().find(".icon-ok").on("mousedown", function() {
console.log("icon.ok")
});
Your problem might be the classic delegation problem, where in the element is not available in the DOM when the event is bound.
Delegate the event and see if that solves your problem.
$self.on("click", ".icon-ok", function() {
console.log("icon.ok")
});
User $self if that element is visible or any closest ancestor that you can find which is always present in the DOM.

How to get which element was clicked?

I have a simple div in my html as follows:
<div id="myDiv">
....
</div>
Also I have set the onlick event on the window.click as follows:
window.onclick = function()
{
// do something
}
So if I click, anywhere in the div, how can I find that this click was made inside "myDiv"
Note : I cannot add the click event on my div, it is generated randomly from jqgrid
$(document).on("click","#myDiv", function (event) {
alert(event.target.id);
});
The aim of the question is simply this. "I wish to know when a dynamically added div is clicked". The moment you see dynamically added think delegated events! :)
As this question allows for jQuery, the answer by #erkaner is close to ideal for this situation. I just wish to explain why it is the appropriate solution.
$(document).on("click","#myDiv", function (event) {
// Do something
});
Explanation:
This uses a jQuery delegated event handler. The event handling is "delegated" to a non-changing ancestor of the intended target, hence the name.
The chosen ancestor is document in this case.
You should use the closest non-changing ancestor to the target, but document is the best default if nothing else is closer/convenient.
Warning: Do not use body for delegated events as it has a bug (styling can cause it to not get bubbled mouse events).
The event (click in this case) bubbles up to the handler element (i.e. document).
The jQuery selector (in this case #myDiv) is then applied to just the elements in the bubble-chain. This is very efficient.
The supplied handler function is then just applied to any matching elements that caused the event.
The upshot of all this is that the element need not exist until event time (it does not need to exist when the event was registered).
Further, because delegation is typically used on mouse events (and not 50,000 times a second) any speed difference between this and a "raw" event handler is negligible. The benefits far outweigh any trivial speed difference.
Regarding the other onclick= answers
Firstly, using the single window.onclick property is a bad idea as you stop anything else using it (not that anyone using jQuery should use it).
Secondly the event parameter passed to onclick is not provided by all browsers (this should be enough to stop anyone using it). There are workarounds, but jQuery was created to avoid browser workarounds :)
Notes:
If the naming is not under your control, you just need something to match on. Worst case, it could be as simple as "match any div under any table", but that will depend on your specific HTML & code:
$(document).on("click","table div", function (event) {
// Do something
});
Here is a practical example:
http://jsfiddle.net/TrueBlueAussie/eyo5Lnsy/
Get the event from the function: window.onclick = function(event) then inside the function you can use it as event.target:
window.onclick = function(event)
{
alert(event.target);
}
Here's an example using addEventListener - without using jQuery
document.getElementById('myDiv').addEventListener('click',function(e){
// this div has been clicked
console.log('this div has been clicked');
});
UPDATE
Here's the non-jQuery solution for dynamically created elements
document.addEventListener('click',function(e){
if( e.target.id == 'myDiv' )
{
// this div has been clicked
console.log('this div has been clicked');
}
});
var myDiv = document.getElementById('myDiv');
myDiv.style.cursor = 'pointer';
myDiv.onclick = function() {
//DO SOMETHING
};
Here we go.
$('body').click(function(e) {
if (e.target.id == 'myDiv') {
alert('My Div!!!!');
}
});​
$( "body" ).click(function( event ) {
$( "#log" ).html( "clicked: " + event.target.nodeName );
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="myDiv">
....
</div>
<div id="log"></div>
check below code. check DEMO
use event.target to get clicked element .
window.onclick = function(event)
{
if(event.target.id == "myDiv"){
alert("here")
}else{
alert('Body')
}
console.log(event.target.id)
}

Button Fetched using ajax does not respond

I have a small issue. So i have a php page whose content return a button using ajax such as shown below:
HTML part:
<a href="#" class="unit_register_button">
Register
</a>
jQuery part:
$('a.unit_register_button').click(function(e){
e.preventDefault();
alert('yaay');
});
Problem:
The button does not respond to the jQuery.
I have tried copying the exact line of html with the button to the page directly and works perfect when I click.
What is the solution and why does the button not work when it is displayed using ajax?
you should use event delegation for that
$(document).on("click","a.unit_register_button",function(e){
e.preventDefault();
alert('yaay');
});
Event delegation allows you to attach a single event listener, to a parent element, that will fire for all children matching a selector, whether those children exist now or are added in the future.
There is couple ways, one of them is by using on function:
$(document).on('click', 'a.unit_register_button', function(e){
e.preventDefault();
alert('Alert message');
});
And another is with delegate function:
$(document).delegate('a.unit_register_button', 'click', function(e){
e.preventDefault();
alert('Alert message');
});
Here jsfiddle with working examples.
Hope this helps.

can someone explain how this stopPropagation works?

I was trying to setup this "when you click outside of the element, close it" type of thing using some code I found on Stackoverflow:
$(document).click(function() {
$('.list-to-hide').hide();
});
$('.show-list-button').click(function(event) {
event.stopPropagation();
});
Could someone explain the later part with stopPropagation? I don't understand why it's needed.
Thanks!
Matt
Imagine this:
<div>
DIV
<span>
Span
</span>
<div>
and:
$('div').click(function() { alert('div clicked'); });
$('span').click(function() { alert('span clicked'); });
Check out what happens when you click each one
When you click the span, it happens to also trigger the div because your also clicking the div.
Now if we wanted to alert the span only we need to stop the div click from triggering when we click on the span so we do this:
$('div').click(function() { alert('div clicked'); });
$('span').click(function(e) { alert('span clicked'); e.stopPropagation(); });
See what happens now
Your example code is missing a vital part:
$(document).click(function() {
$('.list-to-hide').hide();
});
$('.show-list-button').click(function(event) {
event.stopPropagation();
$('.list-to-hide').show();
});
Without the event.stopPropagation(), it would show the list, and then hide it because the .show-list-button is inside the $(document) so both click handlers would fire. event.stopPropagation() basically says only apply this click event to THIS CHILD NODE and don't tell the parent containers anything because I don't want them to react.
Think about it this way - you rent a taxi for $100. The driver gives his company $80. event.stopPropagation() is like telling him to keep all $100 because the company doesn't need to know anything about the ride.
event.stopPropagation(); prevents the event from bubbling up the DOM. Without this line, clicking on .show-list-button the click handler for document will fire also. With it, the document click will not fire.
Have you read this ?
http://api.jquery.com/event.stopPropagation/
It prevents the event from bubbling up the DOM tree, preventing any parent handlers from being notified of the event.
Example
Kill the bubbling on the click event.
$("p").click(function(event){
event.stopPropagation();
// do something
});

jQuery - Append an event handler to preexisting click event

I have a click event that is defined already. I was wondering what the best way would be to append another event handler to this event without touching this code (if possible).
Is there a way to just append another event handler to the already defined click event?
This is the current click event definition:
$(".HwYesButton", "#HwQuizQuestions")
.append("<a href='javascript:void(0);' rel='.HwYesButton'>Yes</a>")
.click(function(event) {
var button = $(this).parent();
var questionId = button.attr('id');
$iadp.decisionPoint["HwQuizYourself"].Input.inputProvided(questionId);
button.find(".HwNoButton").removeClass("HwButtonSelected");
$(this).addClass("HwButtonSelected");
button.removeClass("HwQuestionSkipped");
$iadp.flat.setBoolean(questionId, true);
return false;
});
The only thing you can do is to attach another (additional) handler:
$(".HwYesButton", "#HwQuizQuestions").click(function() {
// something else
});
jQuery will call the handlers in the order in which they have been attached to the element.
You cannot "extend" the already defined handler.
Btw. your formulation is a bit imprecise. You are not defining a click event. You are only attaching click event handlers. The click event is generated by the browser when the user clicks on some element.
You can have as many click handlers as you want for one element. Maybe you are used to this in plain JavaScript:
element.onclick = function() {}
With this method you can indeed only attach one handler. But JavaScript provides some advanced event handling methods which I assume jQuery makes use of.
I know this is an old post, but perhaps this can still help someone since I still managed to stumble across this question during my search...
I am trying to do same kind of thing except I want my action to trigger BEFORE the existing inline onClick events. This is what I've done so far and it seems to be working ok after my initial tests. It probably won't handle events that aren't inline, such as those bound by other javascipt.
$(function() {
$("[onClick]").each(function(){
var x = $(this).attr("onClick");
$(this).removeAttr("onClick").unbind("click");
$(this).click(function(e){
// do what you want here...
eval(x); // do original action here...
});
});
});
You can just write another click event to the same and the both will get triggered. See it here
<a id="clickme">clickme</a>
<script>
$('#clickme').click(function () {
alert('first click handler triggered');
});
$('#clickme').click(function () {
alert('second click handler triggered');
});
</script>
Ajax can complicate the issue here. If you call two events the first being an ajax call, then the second event will probably fire well before the response comes back.
So even though the events are being fired in the correct order, what you really need is to get in to the callback function in the ajax call.
It's difficult to do without editing the original code.
Yes you can. Let's say that I have a tag cloud, and clicking a tag will remove the tag by default. Something like:
<div class="tags">
<div class="tag">Tag 1</div>
<div class="tag">Tag 2</div>
</div>
<script>
$(document).ready(function(){
$('.tag').click(function(e){
e.preventDefault();
$(this).fadeOut('slow',function(){
$(this).remove();
});
return false;
});
});
</script>
Now let's say that's bundled into an included library for a project, but you want to provide a developer-friendly way to intercept those click events and prompt the user an AYS (Are You Sure?) dialog. In your mind you might be thinking something like:
<script>
$(document).ready(function(){
$('.tag').beforeRemove(function(){
if (AYS() === false) {
return false; // don't allow the removal
}
return true; // allow the removal
});
});
</script>
The solution, therefore, would be:
<div class="tags">
<div class="tag">Tag 1</div>
<div class="tag">Tag 2</div>
</div>
<script><!-- included library script -->
$(document).ready(function(){
$.fn.beforeRemove = $.fn.click;
$('BODY').on('click','.tag',function(e){
e.preventDefault();
console.log('debug: called standard click event');
$(this).fadeOut('slow',function(){
$(this).remove();
});
return false;
});
});
</script>
<script><!-- included in your page, after the library is loaded -->
$(document).ready(function(){
$('.tag').beforeRemove(function(){
var sWhich = $(this).text();
console.log('debug: before remove - ' + sWhich);
return false; // simulate AYS response was no
// change return false to true to simulate AYS response was yes
});
});
</script>
In this example, the trick is that we extended jQuery with a beforeRemove trigger that is a duplicate of the click trigger. Next, by making the library utilize the $('BODY').on('click','.tag',function(e){...}); handler, we made it delay and call after our page's beforeRemove trigger fired. Therefore, we could return false in beforeRemove if we got a negative AYS condition and therefore not allow the click event to continue.
So, run the above and you'll see that clicks on the tags won't remove the item. Now, switch the beforeRemove function to return true (as if you had a positive response to an AYS prompt), and the click event is allowed to continue. You have appended an event handler to a preexisting click event.

Categories