Javascript ajax success function fires off multiple times - javascript

In my project, I'm working on a function which deletes rows from a table and also from the database using .ajax(). I've created my function for when the user clicks on the delete button. As you can understand, there a several rows and every row has its own delete button.
Now, when the user clicks on one of them for the first time, a popup modal appears with the question if he/she is sure about deleting the item. When he/she clicks yes, the popup disappears and the row fades-out. This all works perfectly, but...
When you click on a second delete button without refreshing the page, JavaScript fires off the current requested delete (and id) + de previous one. If you do it for a third time, it will fire of the current one and the two previous ones.
I tried to empty the current var $(this).attr('data-page-id');, but it still does the same thing when the .ajax() success function gets fired.
$('a.btn-page-delete').click(function() {
var curPageId = $(this).attr('data-page-id');
$('#delete-page').modal('show');
$('a#action-confirm').click(function() {
$.ajax({
type : 'POST',
url : '/pages/async/delete',
dataType : 'json',
data : { page : curPageId },
success : function(data) {
$('#delete-page').modal('hide');
console.log(curPageId);
console.log(data);
},
error : function() {}
});
});
});

When your outer click event handler gets called
$('a.btn-page-delete').click(function() {
it'll .bind() another click event handler to $('a#action-confirm') everytime. So, everytime the outer event is executed you add one more event handler.
I'm sure you can and should re-build and construct this in a better way, but in the present state your only choice is to .unbind() / .off() the inner click handlers, like
$('a#action-confirm').off('click').click(function() {
that will remove any click event handler bound via jQuery previously.
Ref.: .off()

Related

Possible bug in fullcalendar's eventClick?

Here is the code snippet in question:
eventClick: function(calEvent) {
if(user != calEvent.modified_by && calEvent.modified_by != 0){
$('.antoconfirm').css("display", "inline-block");
}
$('#fc_edit').click();
$('#title2').val(calEvent.title);
//-----------Submit button click-------------------
$(".antosubmit2").on("click", function(e) {
e.preventDefault();
e.stopImmediatePropagation();
calEvent.title = $("#title2").val();
calEvent.confirm = 0;
calEvent.backgroundColor = '#ddbd39';
dbTitle = calEvent.title;
//ajax goes here, works fine
calendar.fullCalendar('updateEvent', calEvent);
$(".antosubmit2").off("click");
$('.antoclose2').click();
});
//---------------------------------------------------
//-----------Close button click-------------------
$(".antoclose2").on("click", function() {
console.log(calEvent.title);
$(".antoclose2").off("click");
});
//---------------------------------------------------
return false;
},
$('#fc_edit').click(); calls the modal in which the editing is done. There are two buttons with the classes "antosubmit2" and "antoclose2". You click on an event, the modal comes up, you change the title, click submit, the modal goes away and voila, the title is changed(from "new1" to "new3" in this example):Test events, title change
When ONLY the submit button is used, everything works fine, you can change one event after the other without incident. On the other hand, when you use the close button on one event and try to change the title on another, the first event will be changed:Test events, title change after close
Now at the "ajax goes here, works fine" part is an ajax POST, that sends the correct data despite what the calendar shows and after a page reload everything is edited the way it should be.
Is this a bug with fullcalendar's event rendering or does my code fail somewhere?
I think you need to run .off against all your button click handlers whenever any of your buttons is used. At the moment you only remove the handler for the button that was actually clicked. If you don't remove them, those handlers will remain and get used again if the other button is clicked in future. This is exactly the scenario you have run into.
In the case you described, I suspect because when you closed the first event, you didn't remove the "click" handler related to the "submit" button that went with that event. Then, when you changed the title of the second event, it ran the "click" handler for both events, because you never removed the first handler. Hence why the title for the first event gets changed when it shouldn't.

Backbone.js ListenToOnce gets called twice

I have a view (I'll call parent) that has another view inside of it ( a child view). The parent view has elements that use a "context menu" event (when right clicked, a set of menu options show up). When a user selects an item from the menu, it fires a callback. From there, I trigger an event in the child view. Here is my trigger:
that.trigger("editFileFolder", {fileType: fileType});
In my child view's initialize function, I have this:
this.listenToOnce(this.options.parent,'editFileFolder', this.editFileObjectEvent);
The editFileObjectEvent function calls another view to be created (a dialog with some fields, a cancel button and a save button). If the user clicks cancel, everything works fine. If the use clicks the save button, the view does an ajax call, saves to the server, and closes the dialog. But, the next time the user right clicks and selects the same menu item, the editFileObjectEvent gets called twice (resulting in the dialog being added twice to the parent view).
Can anyone explain why it's getting called twice and how to resolve this? If you wish to see specific code, let me know and I can add it. There's a lot, so I don't want to overwhelm the question.
thanks
I think the function that calls this.listenToOnce is called twice. You should try to avoid this. However if this is not possible you can make sure the listeners is only bound once by unbinding it before binding:
this.stopListening(this.options.parent, 'editFileFolder', this.editFileObjectEvent);
this.listenToOnce(this.options.parent, 'editFileFolder', this.editFileObjectEvent);
Or you could have a property on your instance to prevent from binding twice:
if (!this.__boundListener) {
this.listenToOnce(this.options.parent, 'editFileFolder', this.editFileObjectEvent);
this._boundListener = true;
}
All listeners are registered in this._listeningTo and you might be able to search though that to check if the event is already bound.
You can also refactor your code:
MyModel.extend({
initialize: function() {
this.bindListenToOnce = _.once(this.bindListenToOnce);
},
abc: function() {
// do stuff
this.bindListenToOnce();
// do stuff
},
bindListenToOnce: function() {
this.listenToOnce(this.options.parent, 'editFileFolder', this.editFileObjectEvent);
}
});

jQuery trigger click triggers twice

I have a checkout form that uses php to load some javascript & html into #Div-A when the page loads. The javascript binds a click event to #Button-A in the same div. Something like this:
<div id="#Div-A"><input type="button" id="Button-A"/>
<script type="text/javascript">
$('#Button-A').bind('click', function() {
$.ajax({
type: 'get',
url: 'some/url/gets/called',
success: function() { this happens on success }
});
});</script>
</div>
Afterward, #Global-Button is created and a javascript function binds a different click event to this second button which then triggers #Button-A to be clicked like this:
$('#Global-Button').live('click', function(event) {
$("#Button-A").trigger("click");
})
The reason being that the contents of #Div-A can change (via ajax), so the second button acts as a global trigger regardless of which button or function happens to reside in #Div-A.
The problem I'm encountering is that for some reason if #Global-Button is clicked after page load #Button-A gets triggered twice. If an Ajax event reloads the contents of #Div-A then all is well and the the trigger happens only once as it should.
I've examined the html within #Div-A before and after reloading via Ajax and everything appears to be identical. There are definitely no duplicate buttons or functions anywhere as far as I can see that would cause two events to be triggered.
I know only a very little about the DOM and can only guess this has something to do with the order in which things are loaded and events are bound.
This is always recommended to use 'unbind' before bind to make sure the event is not bound multiple times. In your case, there may be two possibilities -
'#Global-Button' click function is bound twice.
'#Button-A' click function is bound twice and '#Global-Button' is actually triggering the click once.
Change your code like -
$('#Global-Button').unbind('click').bind('click', function(event) {
$("#Button-A").trigger("click");
})
and also -
$('#Button-A').unbind('click').bind('click', function() {
$.ajax({
type: 'get',
url: 'some/url/gets/called',
success: function() { this happens on success }
});
});
Don't use .live. Live is deprecated use .on instead
Every time you click on #Blobal-Button you are biding an click event.So you need to off before use it
Use .off to remove event handler
$('#Global-Button').off('click');
$('#Global-Button').on('click', function(event) {
$("#Button-A").trigger("click");
})
You can also use
$(".selector").on("click", function (**e**) {
**e**.stopImmediatePropagation();//use to stop click twice or more
})

Add data from form field to table on page without refreshing whole page

Most of the stuff I see on this site refers to adding information to a div from php. I just want to take a user's input, add it to a table dynamically and have it displayed in the div as the click the submit button.
I'm not doing anything where the data gets posted to a database, no php, etc, at this point.
I have the JQuery to run the dynamic add to the database as well. It works, but I have to use a breakpoint in the javascript to step through and see the entered data added to the row....issue is when that function is done, the page refreshes, and all data is gone.
I need a way to add the data to the table in the div without the page refreshing and clearing all of the other data each time.
Imagine parent information in one div, and child information being added in another div. As I add each child piece, I don't want the parent information to be removed, and in fact want it to remain as "editable" information.
------------------------------------------ Edit ----------------------------------------------
Below is the code I'm now using based on suggestions, but I obviously don't understand completely, because it hits the event handler for the click, but then doesn't get inside the function, just jumps back out, and never hits my other breakpoints inside there.
---------------------------------------Final Edit ----------------------------------------------
I finally pulled my head out of my backside and paid attention to what Charlie was telling me, and got this...and it works when I call the function from the onClick event. I couldn't get the event to bind to the button click, but that may be because I was using the tag instead of the setup.
function(addNoteRow){
event.prevent.Default();
var noteTxt = $('#noteEntry').val();
var noteEntBy = 'BGM'
noteEntDate = (Date());
if (!document.getElementsByTagName) return;
tabBody=document.getElementsByTagName("TBODY").item(0);
row=document.createElement("TR");
cell1=document.createElement("TD");
cell2=document.createElement("TD");
cell3=document.createElement("TD");
textnode1=document.createTextNode(noteEntDate);
textnode2=document.createTextNode(noteEntBy);
textnode3=document.createTextNode(noteTxt);
cell1.appendChild(textnode1);
cell2.appendChild(textnode2);
cell3.appendChild(textnode3);
row.appendChild(cell1);
row.appendChild(cell2);
row.appendChild(cell3);
tabBody.appendChild(row);
}
You mentioned you're using jQuery, so I will in my example to make this easier.
$('#submit_button').click( // Bind an event handler to the submit button
function(event){
event.preventDefualt(); //This is necessary if you are using a an submit button input element as normal behavior is to submit the form without ajax causing the page location to change. Event is automatically passed in, and we can call preventDefault() on this object to stop the browser from navigating away from our page.
$.ajax({
url:'http://youserver.com/script_that_will_respond.php', //Give the ajax call a target url
data: $('#user_input').val(), //Supply some data to send with the request,
success: function(data){ //Supply an anonymous function to execute on success. The parameter will be the response text.
$('#your_result_div').html(data); //On success of the ajax call, put the response text into an html element on your page
}
});
}
);
jQuery's ajax method has a lot of settings. This is a pretty bare-bones example.
You can read more about the various options here: http://api.jquery.com/jQuery.ajax/
Edit: I may have misunderstood. If you do not need to reach a server, and want to do this all locally, it is simpler.
$('#submit_button').click( // Bind an event handler to the submit button
function(event){
event.preventDefault();
/*
We have to call this method on the event object bacause deafult behavior
of a subnmit button is to submit the form, causing the browser to go to a new page.
*/
var text = $('#some_input_element').val(); //Store the value of a text input in the variable text
$('#your_div').html(text); //put the contents of the variable text into the "your_div" elemnt.
}
);
try AJAX for partially refreshing portions of page like div etc.
http://api.jquery.com/jQuery.ajax/

jQuery different binds on containing div and button inside

So I have a button inside a list row that is used to delete the row from the page (calls ajax stuff to delete the object represented by the row, but that's not important for my question). The whole row is bound to a click event which would redirect to another page.
In other words, the containing row is click bound and the inner button is click bound, which is causing me problems since clicking the inner button also triggers the containing row click event (as it should).
I've tried binding a hover event for all delete buttons that unbinds the row click on mouseover, and rebinds it on mouseout, like this pseudocode below:
$('.delete-button').hover(
function() {
$('.list-row').unbind();
$('.delete-button').bind('click', function() { /* delete action */ });
},
function() {
$('.delete-button').unbind();
$('.list-row').bind('click', function() { /* list row action */ });
}
);
This isn't working very well, and I'm convinced there is a better way to approach it. Should I take the button out of the containing list-row? It's way easier to have it in there since my list row contains custom attributes that have data I need for the ajax calls and I can just var rid = $('.delete-button).parent().attr('row-id'); to get the data, but I'm not opposed to change :)
Thanks!
In your click event handler for the button, you need to call e.stopPropagation(). This will prevent the event from bubbling up the DOM tree. More info here: http://api.jquery.com/event.stopPropagation/
edit: you already accepted (thanks!), but maybe this code snippet would help explain some of the concepts better:
$('.list-row').click(function() {
/* list row action */
});
$('.delete-button').click(function(e) {
// die, bubbles, die
e.stopPropagation();
// if you also need to prevent the default behavior for the button itself,
// uncomment the following line:
// e.preventDefault();
// note that if you are doing both e.stopPropagation() AND e.preventDefault()
// you should just `return false;` at the end of the handler (which is jQuery-
// sugar for doing both of these at once)
/* delete action */
})
There's a few ways of approaching this. As #jmar777 has already said you may attach an altered event to the click handler on the button, stopping propagation.
If you want to do this with the same function as you're applying to the div then you can approach it as such:
if($(event.target).is("input")) {
event.stopPropagation();
}
Another approach is to actually not bind the click event to the button, for any time the browser supports clicks on the containing element. As you will always trigger that, then you don't actually need the button to handle it too! This does require you to handle IE6 etc a little differently from everything else though...
Let your handler function return false

Categories