JQuery : Add a confirm handler with bind/on and trigger functions - javascript

I'm trying to add a confirm box to many links, button, or inputs, on the click event.
I can't use location.href, submit(), or other specific features because :
location.href, for example, won't work on a submit button,
location.href, for example, won't trigger other bound handlers.
So what I need to use is the trigger() function, that theorically execute all the handlers AND the native action. The "difficult" part is to execute all handlers, EXCEPT the handler which pop the confirm box.
Here is my code :
$('a, button, input[type="submit"]').each(function() {
var oButton = $(this);
oButton.on('click.pending', function(oEvent) {
console.log('click !');
oEvent.preventDefault();
var oDialog = $('<div class="dialog-example">[Question] ?</div>').dialog({
buttons: {
Cancel: function() {
console.log('cancelled !');
// Nothing to do
oDialog.dialog('close');
},
Ok: function() {
console.log('confirmed !');
// Trigger the rest of the handlers AND the native action, BUT not this one, so this dialog is not used
// Problem : nothing happens here
oButton.trigger('click.confirmed');
oDialog.dialog('close');
}
}
});
});
});
Thanks in advance ! ;)

You should try:
oButton.off('click.pending').trigger('click').get(0).click();
DEMO

Related

jQuery form getting submitted multiple times

I've made a simple lightbox implementation in my code. By Clicking on #makePayment link a lightbox is opened, and within that lightbox there is a form. When user clicks on #paymentDetailsConfrimLB to submit the form, I've just displayed an alert.
Before I explain the problem, please have a look at the code I've written:
$("#makePayment").click(function() {
$("body").addClass("modalPrint");
var lb = new LightBox("paymentDetailsLB", "675", "500");
lb.htmlObjRef.style.height = "auto";
lb.show();
$("#paymentDetailsCloseLB, .hideBox").click(function() {
$("body").removeClass("modalPrint");
lb.hide();
});//paymentDetailsCloseLB
$("#paymentDetailsConfrimLB").click(function( event ) {
alert('form submission happened.');
event.preventDefault();
return false;
});//paymentDetailsConfrimLB
return false;
});//makePayment
Problem is, when I first load the page, and open the lightbox, and click the submit button, the alert is shown once (as it should), but if I close the lightbox, re-open it, and then submit the form, it is submitted twice and alert is shown twice. Similarly if I again close and re-open the lightbox, upon form submission the alert shows up 3 times, and it goes on like this.
Any idea on how I can resolve this?
Other than Kavin's approach, another solution also worked for me. I just added this line immediately after the event.preventDefault() method:
event.stopImmediatePropagation()
And it resolved the issue.
You're setting click callback every time you open lightbox. Try to move click callbacks out of #makePayment:
$("#makePayment").click(function() {
$("body").addClass("modalPrint");
var lb = new LightBox("paymentDetailsLB", "675", "500");
lb.htmlObjRef.style.height = "auto";
lb.show();
return false;
});//makePayment
$("#paymentDetailsCloseLB, .hideBox").click(function() {
$("body").removeClass("modalPrint");
lb.hide();
});//paymentDetailsCloseLB
$("#paymentDetailsConfrimLB").click(function( event ) {
alert('form submission happened.');
event.preventDefault();
return false;
});//paymentDetailsConfrimLB
You're binding a new handlers every time the submit button is clicked. You only need to define a handler once, and it will be executed whenever that action occurs. Otherwise, each handler you bind will execute.
If you absolutely needed to bind the handlers the way you are, then you could also use .one, which will only bind the handler the first time for each element.
jQuery .one() documentation
Attach a handler to an event for the elements. The handler is executed
at most once per element per event type.
Try something like this.
$(document).on('click', '#makePayment', function() {
$("body").addClass("modalPrint");
var lb = new LightBox("paymentDetailsLB", "675", "500");
lb.htmlObjRef.style.height = "auto";
lb.show();
return false;
}).on('click', '#paymentDetailsCloseLB, .hideBox', function() {
$("body").removeClass("modalPrint");
lb.hide()
}).on('click', '#paymentDetailsConfrimLB', function() {
alert('form submission happened.');
event.preventDefault();
return false;
});
The problem is:
$("#paymentDetailsConfrimLB").click(function( event ) {
alert('form submission happened.');
event.preventDefault();
return false;
});//paymentDetailsConfrimLB
it adds to the click queue, so to speak. So if you add multiple click events to something, all of them get added and all of them run by default. You don't notice it because all your other functions don't matter about being run multiple times.
A way to solve this is to put a check within the function.
$("#paymentDetailsCloseLB, .hideBox").click(function() {
$("body").removeClass("modalPrint");
lb.hide();
});//paymentDetailsCloseLB
pDCLB=$("#paymentDetailsConfrimLB");
if(!pDCLB.attr('alertc')); //check if the attribute exists, if not, we've never been here before.
pDCLB.attr('alertc',1); //adds the attribute so we can check it later.
$("#paymentDetailsConfrimLB").click(function( event ) {
alert('form submission happened.');
event.preventDefault();
return false;
});//paymentDetailsConfrimLB
}

How does the event chaining works for multiple objects?

I'm performing a deleting action on a page when the user click on the confirmation button on a twitter bootstrap modal window button.
I have two buttons: one allow the user to cancel the action, and another one to confirm.
When the user clicks on the confirm delete button, when the modal is hidden, I perform my actions, so for example I can show an animation and actually delete the item.
If the user click on few items but his/her choice is the cancel button, when he/she clicks on the item he/she want to delete, the deletion is performed also on the elements where the choice has been to cancel.
Should not the 'hidden' event be detached from the element once it is performed?
I know I can detach the event chaining changing $('#confirmDeleteModal').on('hidden', function() { to $('#confirmDeleteModal').off('hidden').on('hidden', function() { but I really would understand why this happen. Am I missing something?
The code is:
$(document).ready(function(){
$('.delete').on('click', function() {
var itemID = $(this).data('product-id')
$('#confirmDeleteModal').modal('show');
$('#confirmDelete').on('click', function() {
$('#confirmDeleteModal').on('hidden', function() {
// Here I do my stuff to perform deletion
$('#result').append('This method has been called for ' + itemID + ' <br />' )
});
});
});
});
I hope I have exposed clearly my question. I prepared a JS Bin as well: http://jsbin.com/inulaw/5/edit
The problem here is that you are attaching additional listeners to the click and hidden events each time. To fix this, chain the jQuery .off('eventName') method before calling the .on('eventname') again.
Here's your code updated and working great in the JS Bin:
$(document).ready(function(){
$('.delete').on('click', function() {
var itemID = $(this).data('product-id')
$('#confirmDeleteModal').modal('show');
$('#confirmDeleteModal').off('hidden'); // must reset from previous
$('#confirmDelete').off('click').on('click', function() {
$('#confirmDeleteModal').on('hidden', function() {
// Here I do my stuff to perform deletion
$('#result').append('This method has been called for ' + itemID + ' <br />' )
});
});
});
});
EDIT: I moved the $('#confirmDeleteModal').off('hidden'); to above the click event so it resets whether or not the confirm is clicked.

Close colorbox popup with a button and trigger the confirm box?

I have a confirm box on my colorbox("are you sure you want to leave?").
This triggers when i close the popup. This works when i click on the "cboxClose" div on the popup.
I am trying to show this confirm box on a button click. But the popup just closes right away without showing the confirm box.
My question is how do i trigger the the confirm box when i click on a cancel button. i tried several ways
//This just closes the pop up without showing the confirm box
$('#btnCancel').click(function () {
parent.$.colorbox.close(); });
//doesn't work
$('#btnCancel').click(function () {
$('#cboxClose').click()
});
COLORBOX
onComplete: function () {
$("#cboxClose").click(function (e) {
// stop any other script from firing
e.stopPropagation();
if (confirm('are you sure you want to leave?')) {
$.colorbox.close();
// ensure that the binding is removed when closed
$("#cboxClose").unbind();
}
});
} // close oncomplete
The issue here is that colorbox registers a click handler on the cboxClose element. As a result, neither stopping bubbling nor preventing the click (by returning false in a click handler) will have any effect because the colorbox handler is already registered. The only way of stopping that handler from being run is to unbind it. However, to do that you need a reference to the handler, which you won't get without modifying the colorbox code.
In any case, that's what's going on and why the code you have above doesn't work. Another option for you would be to override the colorbox close function (which is the public colorbox method that is called by colorbox's close button handler). All you need is this:
$.colorbox._close = $.colorbox.close;
$.colorbox.close = function() {
if(confirm("Close?")) {
$.colorbox._close();
}
}
The down side (which may not be an issue in your situation) is that this will affect all colorboxes on the page.
I solved this issue by making this method and binding it to the cancel button
var originalClose = $.colorbox.close;
$.colorbox.close = function (e) {
var response;
var formChanged = localStorage.getItem("isFormChanged");
var saveClicked = localStorage.getItem("saveClicked");
if (formChanged == "true" && saveClicked == "false") {
response = confirm('Do you want to close this window? All your changes will not be saved');
if (!response) {
return
}
}
originalClose();
};
<input type="button" value="Cancel" id="btncancel" onclick="parent.$.colorbox.close()"/>

How enable a click event of a button?

I disable a jQuery click event with:
$("#button").click( function () { return false;}
How can I enable it? The intention is avoid double clicks and run multiple times the function that triggers.
I want to restore the event when other button was pushed.
There's a couple options, but I like the following best:
//disable
$("#button").bind('click', function() { return false; });
//enable
$("#button").unbind('click');
You could also bind click again on the button to some other callback function as well. Lastly, I might suggest calling preventDefault on the event from a click event, depending on what #button really is like so:
$("#button").bind('click', function(e) {
e.preventDefault();
return false;
});
As j08691 pointed out, as of jQuery 1.7 on, it should look like:
$("#button").on('click', function(e) {
e.preventDefault();
return false;
});
You could also use one:
$("#button").one('click', function () { /* code here */ });
The event will unbind itself after being called once.
If you do:
$("#button").unbind("click");
the button will be working again, the unbind function erases registered events handlers from the selected element, if you dont pass it an argument it will erase all events registered.
EDIT: as noted in the comments, you can use now the on and off methods:
$("#button").off("click")
to disable clicks and:
$("#button").on("click")
to enable them again

Is there a way to stop all other events in a dojo onBlur event handler?

Maybe I'm on the wrong track...
The setup:
I have a rather complex full dojofied web application. The important part for this question is a longish form in the central region of a dijit.layout.BorderContainer with a navigation tree and some action buttons in the other regions.
What I want to do:
If the user did enter data and did not save, they should get a warning message if he is going to leave the form (if he navigates away, klicks the "new Element" button,...). For a better user experience, I wanted to give a modal dialog with the options "save", "leave anyway", "cancel".
May idea was to use the onBlur event of the form, stop all other events (most likely an onClick on some other widget), check for changes, if there are changes, display the dialog, otherwise let the other events continue.
I do not want to add a checkChanges method to all non-form active elements!
For a first test I just tried to stop the events...
This works
<div id="formArea" dojoType="dijit.form.Form" encoding="multipart/form-data" action="" class="ContentPane" region="center">
<script type="dojo/connect" event="onBlur" >
alert("I don't think so");
</script>
</div>
...but it's ugly and I can't easily continue
This doesn't
<div id="formArea" dojoType="dijit.form.Form" encoding="multipart/form-data" action="" class="ContentPane" region="center">
<script type="dojo/connect" event="onBlur" args="e">
console.log("blur"); // ok
e.preventDefault();//event.stopt(e)//return false //<--neither of these
</script>
</div>
the problem is that if I click on a button outside of the form, the onBlur triggers, but I can't stop the onClick on the button.
I know that onBlur doesn't deliver an event object - so the e.something can't really work...
Is there any way to catch the onClick on the other element?
Pause button event listener(s) in form's onBlur if data are not saved.
See it in action: http://jsfiddle.net/phusick/A5DHf/
You have some button event listeners, register them via on.pausable(node, event, callback) instead of on():
var b1Handler = on.pausable(button1Node, "click", function() {
console.log("b1.onClick");
});
var b2Handler = on.pausable(button2Node, "click", function() {
console.log("b2.onClick");
});
Gather handlers into an array:
var handlersToPause = [b1Handler, b2Handler];
Add onBlur event listener:
on(form, "blur", function(e) {
if (this.isDirty()) {
// if data are not saved, pause button event listeners
handlersToPause.forEach(function(handler) {
handler.pause();
});
// display modal dialog here
}
});
Add e.g. onFocus event listener to resume button event listeners:
on(form, "focus", function(e) {
handlersToPause.forEach(function(handler) {
handler.resume();
});
});
Please note, that handler.pause() is pausing an onclick listener, not an event. The onclick event is waiting in the Event queue and therefore is not accessible in the execution time of onblur.
I would work out some more robust solution, but this is quick and answers your question. Anyway, have a look at dojo/aspect and its around advice to call your checkChanges without the need to change all non-form active elements.
there is afaik only confirm('question?') that will 'deadlock' the events of your page like that.
I have made a similar setup though, the way I came around this (except if user enters url in addressbar and hits enter) was a popup dialog whenever the navigation tree is clicked, sending user to a new view. Consider:
----------------------------------------
| Nav 1 | Asset1 ( view controller ) |
| Nav 2 | Asset2 ( hidden ) |
----------------------------------------
Nav 1 is the default onload view, Asset 1 is loaded, contains a 'setup page' form or similar and can be changed. The trick is, Asset1 and Asset2 is derivative from AbstractAsset which in turn is a simple ContentPane extension.
In AbstractAsset is a 'must-override-for-functionality' function called isDirty
var Viewcontroller = declare("AbstractAsset", [dijit.ContentPane], {
isDirty: function() { return false; }
});
declare("Asset1", [Viewcontroller], {
startup: function() {
// sets up form
...
// and references inputfields to 'this'
this.inputfields = this.form.getChildren();
// and saves (clones) the state of each field
var self = this;
this.inputfields.forEach(function(inputWidget) {
self.states[inputWidget.id] = inputWidget.get("value");
});
},
isDirty: function() {
var self = this;
var dirty = false;
this.form.getChildren().some(input) {
if(self.states[input.id] != input.get("value")) {
dirty = true;
return false; // breaks .some loop
}
return true;
});
return dirty;
}
})
Then in turn, every navigation click must call the currently visible view controller's isDirty function in order to procede. Lets say user clicks the nav-tree (dijit.Tree) row node Nav 2.
var navigation = dojo.declare("NavigationController", [dijit.Tree], {
currentView : null,
onLoad: function() {
// start Asset1 in viewNode by default
this.currentView = new Asset1({ }, this.viewNode);
},
onClick : function() {
if(this.currentView.isDirty()) alert("I Dont Think So");
else {
this.loadFunction(this.model.selection.getSelected());
}
}
});
This is the general idea of implementing the on-unload-check, you Will need to hook any onClick events through your 'master application controller' to determine what should happen. Check this application which serves as cms navigation controller and its page.js:587 for isDirty example

Categories

Resources