jQuery - Append an event handler to preexisting click event - javascript

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.

Related

JQuery detecting click interactions on nested elements causing huge loop

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');
});

Page refresh required before jQuery events update [duplicate]

This question already has answers here:
Event binding on dynamically created elements?
(23 answers)
AddClass/RemoveClass Issues
(3 answers)
Closed 8 years ago.
I made a like system but when I click on the like button it will run but I have to refresh the page for me to then be able to click on the unlike button even though it is showing up ? please help me I need it to be able to be clickable and work right after each other....
<script type="text/javascript">
$(document).ready(function(){
$('.like').click(function(){
if($(this).attr('title')=='like'){
$.post('/like.php',{imid:$(this).attr('id'),action:'like'});
$(this).removeClass('like').addClass('unlike');
$(this).attr('title','unlike');
}
});
});
</script>
<script type="text/javascript">
$(document).ready(function(){
$('.unlike').click(function(){
if($(this).attr('title')=='unlike'){
$.post('/like.php',{imid:$(this).attr('id'),action:'unlike'});
$(this).removeClass('unlike').addClass('like');
$(this).attr('title','like');
}
});
});
</script>
I think #Steven Britton's answer should work - except he's missing the event for the likes.
The following works for me:
<script type="text/javascript">
$(function(){
$('body')
.on('click', '.like', function(){
// Do whatever to register action
$(this).removeClass('like').addClass('unlike').attr('title', 'unlike');
})
.on('click', '.unlike', function(){
// Do whatever to register action
$(this).removeClass('unlike').addClass('like').attr('title', 'like');
});
});
</script>
I don't see the need for the test (if title = unlike) since you have different classes for like/unlike anyway. If you are going to do a test I would recommend doing it by testing for classes - testing for texts in the title will break as soon as you change to texts (e.g. translate the page). The texts are part of the presentation, the classes part of the data structure.
You could give all the links a common class (e.g. "like-unlike") in addition to the like or unlike class and use something like the following:
<script type="text/javascript">
$(function(){
$('body').on('click', '.like-unlike', function(){
if( $(this).hasClass('like') ){
// Do whatever to register action
$(this).removeClass('like').addClass('unlike').attr('title', 'unlike');
} else if( $(this).hasClass('unlike') ){
// Do whatever to register action
$(this).removeClass('unlike').addClass('like').attr('title', 'like');
} else {
// SHould never happen - log error?
}
});
});
</script>
To answer the comment about how to use a delegated event: .on is (when used like this) using a delegated event. I am binding the event to the document body, not to the like buttons. When the user clicks the like buttons the event bubbles up to the body at which point my delegated event is run (assuming the element clicked matches the selector I give as second argument to .on (.e.g ".like-unlike" in my final example).
The difference is that the original code was saying "when the page loads find all elements with class 'like' and attach this click event to them and all elements with class 'unlike' and attach that click event to them". When the click event is run it changes the class from like to unlike. But because the element did not have class "unlike" when the document loaded it was not found then and did not get that event. In fact it still has the "like" event handler even after the class is changed to "unlike" since that event handler was attached to the element when the page loaded. The fact that the list of elements to attach the event to was found using a specific class doesn't matter - those on the list get that event handler and keep it.
By attaching to the body using .on in it's three-argument form we are then running events based on what class the element has when it is clicked, instead of what class it had when the document loaded.
My second example (using the like-unlike class) would actually work as a non-delegated event as well, but I still think delegated events are better here - you might end up loading content with ajax requests or something.
Give this a shot (by using the ON, you're setting an event handler on classes that haven't been applied yet):
<script type="text/javascript">
$(document).ready(function(){
$(document).on('click','.unlike', function(){
if($(this).attr('title')=='unlike'){
$.post('/like.php',{imid:$(this).attr('id'),action:'unlike'});
$(this).removeClass('unlike').addClass('like');
$(this).attr('title','like');
}
});
});
</script>

Prevent click jump on jQuery one() function

I'm using the one() function in jQuery to prevent multiple clicks. However, when they click the element a second time, it does the annoying click jump and sends you back to the top of the page.
Is there a way to prevent this from happening? Unbinding the click and re-binding it when the function is done has the same result (and I'm assuming that one() just unbinds the event anyways).
A quick example of this happening: http://jsfiddle.net/iwasrobbed/FtbZa/
I'm not sure if this is better or not, but you could bind a simple function that maintains the return false.
jQuery provides a shortcut for this with .bind('click',false).
$('.someLink').one('click', function() {
$(this).bind('click',false);
return false;
});
or if you have several of these links, a very efficient alternative would be to use the delegate()[docs] method to bind a handler to a common ancestor that takes care of the return false; for you.
Here I just used the body, but you could use a nearer ancestor.
$('.someLink').one('click', function() {
});
$('body').delegate('.someLink','click',function(){return false;});
Try changing the href so the '#' isn't being used: http://jsfiddle.net/FtbZa/1/
$('.someLink').one('click', function() {
alert('test');
return false;
}).attr('href', 'javascript:void(0);');
You could use the standard .click() function and a little logic:
1. $('.someLink').click(function(event) {
2. event.preventDefault();
3. if (!$(this).hasClass("clicked"))
4. alert('This will be displayed only once.');
5. $(this).addClass("clicked");
});
Listen to anything with the class someLink for a .click()
Stop the browser doing what it would normally do.
Check if the object has the class clicked (Note this could be any name you wanted)
It hasn't so do something.
Add the class clicked so next time its clicked, it will ignore your code.
See the demo here
The problem is that after the listener has been unbound there is nothing stopping the browser from honoring the link (Which it is treating as an anchor tag) and trying to go to it. (Which in this case will simply lead to the top of the page.

cancel onClick javascript functions with another javascript

HI there,
this is a little sticky situation and I need some advice.
I have a couple of href's like this, across my project.
<a class="not-allowed" href="javascript:void(0)" onclick="showSettingDiv();">Change</a>
<a class="" href="javascript:void(0)" onclick="DeleteSettingDiv();">Delete</a>
Depending on the type of user logged in, I have to have some links do nothing and trigger something else.
I have this function to make that happen:
$('.not-allowed').each(function (e) {
$(this).click(function (e) {
e.preventDefault();
alert("you dont have permission");
})
});
This does fire up the alert, however, it also executes my onclick function too. is there a way I Can stop all javascript functions and execute just this above one?
I realized I could just use .removeAttr() but I am not sure if thats the best way. I mean I might have buttons to restrict or checkbox and radio button to disable.
e.preventDefault will not take care of all that I guess. Anyway, How do I prevent all javascript functions ?
Thanks.
Yes. It is called Capture mode. It works on DOM-compatible browsers. Check out if your JS framework makes this function available for you.
If the capturing EventListener wishes to prevent further processing of the event from occurring it may call the stopProgagation method of the Event interface.
A quick example:
<html>
<body>
<button class="not-allowed" id="btn1" onclick="alert('onclick executed');">BTN1</button>
<button id="btn2" onclick="alert('onclick executed');">BTN2</button>
<script type="text/javascript">
document.addEventListener("click", function (event) {
if (event.target.className.indexOf("not-allowed") > -1) {
event.stopPropagation(); // prevent normal event handler form running
event.preventDefault(); // prevent browser action (for links)
}
}, true);
</script>
</body>
<html>
.preventDefault() does not have an affect on inline event handlers. You would need to remove the functionality completely. Give this a shot:
$('.not-allowed').each(function(i, elem) {
elem.onclick = null;
$(this).click(function (e) {
e.preventDefault();
alert("you dont have permission");
})
});
Simplified example on jsfiddle: http://jsfiddle.net/cJcCJ/
You could bind the click event on a parent element and then bind another click event on the "inactive" elements which will call e.stopPropagation(); to prevent the event from bubbling up to the parent element.
Another solution would be giving the disabled elements a class and doing if($(this).hasClass('disabled')) return; in the handler.
You will need to have to modify the function(s) in onclick (showSettingDiv, DeleteSettingDiv) to check for the permissions, too.
BTW, make sure that you don't only check permissions in JavaScript, you must also do it server-side, or it's very easy to manipulate.

Jquery remove and add back click event

Is it possible to remove than add back click event to specific element? i.e
I have a $("#elem").click(function{//some behaviour});, $(".elem").click(function{//some behaviour});(there are more than 1 element) while my other function getJson is executing I'd like to remove the click event from the #elem, and add it again onsuccess from getJson function, but preserve both mouseenter and mouseleave events the whole time?
Or maybe create overlay to prevent clicking like in modal windows? is that better idea?
edit :
I've seen some really good answers, but there is one detail that I omitted not on purpose. There are more than one element, and I call the click function on the className not on elementId as I stated in the original question
Rather than using unbind(), which means you'll have to rebind the same event handler later, you can use jQuery's data() facility with the ajaxStart and ajaxStop events to have your elements ignore click events during all AJAX requests:
$(".elem").click(function() {
if (!$(this).data("ajaxRequestPending")) {
// some behaviour
}
}).ajaxStart(function() {
$(this).data("ajaxRequestPending", true);
}).ajaxStop(function() {
$(this).removeData("ajaxRequestPending");
});
EDIT: This answer is also id-to-class-proof (see questioner's edit), since everything matching the selector will handle the AJAX events the right way. That's the main selling point of jQuery, and it shows.
You are looking for .unbind(). Pass it 'click' and it will destroy the click event.
I would put it just before your getJSON and re-bind the click event inside the success handler of your ajax call.
You have to do some additional scripting. There is no callback for that. Take a look over here: jQuery - How can I temporarily disable the onclick event listener after the event has been fired?
Rather than unbinding/binding the click event, you could check the state of another variable to see if it should do the action.
var MyObject = {
requestActive = false;
};
function MyJsonFunction() {
// when requesting
MyObject.requestActive = true;
//...
// when request over
MyObject.requestActive = false;
}
$("#elem").click(function{
if (MyObject.requestActive == true) {
//do something
}
});

Categories