stop and later persue javascript event in jquery ui modal dialog - javascript

I want to stop an event show a modal dialog and if the user presses yes persue this event. event.run() brings an error in firefox.
jQuery(element).click(function(event) {
event.preventDefault();
dialog.dialog({
buttons: {
'Ja': function() {
event.run();
},
'Nein': function() {
jQuery(this).dialog('close');
}
}
}).dialog('open');
});
Thanks to a friend and hashbrown I managed to solve this problem. An event cannot be paused and persued. If it is paused it will block the whole DOM. Try:
jQuery(link).click(function(){while(true)});
When using jQuery its possible to set additional event parameters what I did:
jQuery(element).click(function(event, show_dialog) {
var that = jQuery(this);
if(!show_dialog) {
dialog.dialog({
buttons: {
'Yes': function() {
that.trigger(event.type, [true]);
},
'No': function() {
jQuery(this).dialog('close');
}
}
}).dialog('open');
return false;
} else {
dialog.dialog('close');
return true;
}
});
First click show_dialog is undefined and modal dialog is shown. Clicking on Yes in modal dialog triggers the event.type (click) with the additional parameter true for show_dialog. http://api.jquery.com/trigger/#trigger-eventType-extraParameters
It was not possible to that.trigger(event, [true]);. I think cause events default action was prevented before.

That is because immediately after creating the popup the function returns and the event expires.
What you'll have to do is .trigger() a new event.
Note: set some sort of global variable to ignore this second event firing in your anonymous function (infinite loop problem).
Can I ask why you want to do this; what would click go off and do if you let it?
If it just fires off a different function, why not just call that function instead of attempting event.run()?

Related

Calling jQuery Dialog twice

I have 2 functions: myFunc1 and myFunc2. When myFunc1 is called, a jQuery confirmation Dialog appears. When the user clicks yes, myFunc2 is called, which should show another dialog.
But, despite successfully calling myFunc2, the second dialog never appears.
Here is a fiddle.
function myFunc1() {
dialog().then(function(data) {
if (data == "yes") {
console.log("clicked yes: show another dialog")
myFunc2();
} else {
console.log("clicked no")
}
});
}
function myFunc2() {
dialog();
console.log("myFunc2 is called")
}
function dialog(title) {
var def = $.Deferred();
$("#dialog").dialog({
modal: true,
buttons: {
"Yes": function() {
def.resolve("yes");
$(this).dialog("close");
},
"No": function() {
def.resolve("no");
$(this).dialog("close");
}
}
});
return def.promise();
}
$("button").on("click", myFunc1);
You are resolving the Deffered-Object before you are actually closing the first dialog. So when the then()-callback is hit, the dialog is still open, therefore no new one is created.
Just swap the functions and it should be working.
"Yes": function() {
$(this).dialog("close");
def.resolve("yes");
},
Example
When the Deferred is resolved, any doneCallbacks added by deferred.then() or deferred.done() are called. Callbacks are executed in the order they were added
.resolve()
You're using the same div to create all your dialogs, if you need to have more than one dialog open at once that will be an issue.
// Get a random unique number to use as the dialog id
var guid = Math.floor(Math.random() * 9999999999999) + 1;
// Clone the dialog div and give it a new name
$("#dialog").clone()[0].id = guid;
// Create the dialog with the new unique div
$("#"+guid).dialog({...});
Also,
// Make sure you remove the div after you close it
$(this).dialog("close").remove();

Prevent duplcate ajaxLoad event calls added with a click event

I am using MVC Razor - The overall goal is to create a "print view" pop-up page.
The print view button is on the parent page, when clicked, an ajax event is fired which will populate an empty div with the contents that are to be included in the print preview:
//from the view
#Ajax.ActionLink("prntPreview", "Display", new { ID = Model.Detail.ID }, new AjaxOptions { UpdateTargetId = "modal" }, new { #class = "btnPreview" })
then, using JavasScript/jQuery I clone the contents of that newly populated div and create a new window with the contents:
//in the scripts file
$('.btnPreview').on('click', function () {
$(document).ajaxStop(function () {
var pageData = $('#modal').html();
setTimeout( //add a slight delay
function () {
PopupPrint(pageData);
}, 300);
});
});
function PopupPrint(data) {
var mywindow = window.open('', '', 'height=500,width=800,resizable,scrollbars');
mywindow.document.write(data);
mywindow.focus();
//do some other stuff here
}
This is where I run into difficulty. The first time I click, everything is working as expected - however, if you do not navigate away from the parent page and try to use the print preview button a second time, the popup will be created twice, then three times etc. with each additional click.
I think that the problem is because each time the .btnPreview is clicked, a new $(document).ajaxStop event is being created, causing the event to fire multiple times.
I have tried to create the ajaxStop as a named function which is declared outside the scope of the click event and then clear it but this produces the same result:
var evnt = "";
$('.btnPreview').on('click', function () {
evnt =
$(document).ajaxStop(function () {
var pageData = $('#modal').html();
setTimeout( //add a slight delay
function () {
PopupPrint(pageData);
evnt = "";
}, 300);
});
});
I also have other ajaxStop events initialised so don't want to completely unbind the ajaxStop event. Is it possible to get the name or something from each ajax event so that I can clear just that event or similar?
You can prevent adding additional triggers by checking with a variable outside of scope like this:
(function() {
var alreadyAdded = false;
$('.btnPreview').on('click', function() {
if (!alreadyAdded) {
$('.eventTrigger').click(function() {
console.log('printing!');
});
alreadyAdded = true;
}
});
})();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button class="btnPreview">Add Event</button>
<button class="eventTrigger">Trigger</button>
Please note that the variable and function are encapsulated in a self-executing anonymous function and do not pollute global space.
The output of the sample can be seen in the developer console. If you remove the if-check then every click on the "Add Event" button produces an additional print statement on the "Trigger" button each time it is clicked (which is your problem). With the if, there will ever only be one event on the trigger button.
There were 2 issues which I needed to address.
The answer is to unbind the ajax event after it has checked that the request had completed and to unbind and reattach the button click trigger.
This is how I did it:
//in the scripts file
$('.btnPreview').off('click').on('click', function () {
$(document).ajaxComplete(function (e) {
var pageData = $('#modal').html();
setTimeout( //add a slight delay
function () {
PopupPrint(pageData);
}, 300);
$(this).off(e);
});
});
I unbound the click event by adding .off('click') before the .on. this is what stopped it popping up multiple times.
The other issue was that anytime any ajax event completed (triggered by something else) that would also create the popup - to get around that, I added $(this).unbind(e); to the end of the code block which removed the ajaxComplete binding which was being triggered each time any ajax event completed.

Callbacks running twice after AJAX content loaded

I have a page loading content with the waypoints infinite scroller plugin.
On the success of the AJAX call and after DOM elements are added, a callback runs to re-initilize javascript functionality, like carousels, buttons and other animation.
On the first AJAX call, buttons tasked with toggling work properly. On the next AJAX call, the new DOM items work, but the previous buttons now execute toggles twice when clicked. On the third call, original items now run three times, the second items twice and the new ones once, so on and so fourth, continuing to compound as AJAX content is called.
How can I isolate the callback to not affect the previously loaded content, or, is there a way to set a global state for the JS, so that I don't need the callback each time?
Some pseudo code:
$('.infinite-container').waypoint('infinite', {
onAfterPageLoad: function() {
//Carousel options
$('.carousel-container').carousel({
options: here,
....
});
//Button Toggles
$('.button').click(function(){
var self = $(this);
$(this).siblings('.caption').animate({
height: 'toggle'
}, 200, function() {
// Callback after animate() completes.
if(self.text() == 'Hide Details'){
self.text('Show Details');
} else {
self.text('Hide Details');
}
});
});
}
});
Edit: Thanks everybody. All the answers lead me to differing but appropriate solutions. The selected was picked as it's a great collection of all the suggested issues and worth the read.
Check out this answer. I think it is the same situation you are having and has a solution:
Best way to remove an event handler in jQuery?
You are attaching a new click handler each time that block of code gets executed. The result is multiple click handlers being bound to your button. Use jQuery's unbind: http://api.jquery.com/unbind/ to remove any click handler(s) before adding a new one:
$('.infinite-container').waypoint('infinite', {
onAfterPageLoad: function() {
//Carousel options
$('.carousel-container').carousel({
options: here,
....
});
// Un-bind click handler(s)
$('.button').unbind('click');
//Button Toggles
$('.button').click(function(){
var self = $(this);
$(this).siblings('.caption').animate({
height: 'toggle'
}, 200, function() {
// Callback after animate() completes.
if(self.text() == 'Hide Details'){
self.text('Show Details');
} else {
self.text('Hide Details');
}
});
});
}
});
Try bind only once click event to button. Of course you can use on instead of live.
$('.button').live('click', function(){
var self = $(this);
$(this).siblings('.caption').animate({
height: 'toggle'
}, 200, function() {
// Callback after animate() completes.
if(self.text() == 'Hide Details'){
self.text('Show Details');
} else {
self.text('Hide Details');
}
});
});
$('.infinite-container').waypoint('infinite', {
onAfterPageLoad: function() {
//Carousel options
$('.carousel-container').carousel({
options: here,
....
});
//Button Toggles
}
});
$('.button').click(function(){
You add an event handler to every button that has the class button. When the second button is added then you add it to every ... which means button 1 and button 2. And so on.
Try
$('.button').last().click(function(){

Call 2 jQuery functions from 1 event

I'm launching a jquery dialog, which displays some links. on click of the links, i want to display something on an element via ajax, and close the dialog.
here are the two snippets, which i cannot figure out how to combine.
//this is the handler and the ajax function
$(document).delegate("a[rel=link]", "click", function() {
$("#target").load($(this).attr("href"));
return false;
});
//this function i want to add to the above handler
function() {
$(this).closest('.ui-dialog-content').dialog('close');
return false
};
assuming that 'this' refers to the same thing in both snippets:
//this is the combined function
$(document).delegate("a[rel=link]", "click", function() {
$("#target").load($(this).attr("href"));
$(this).closest('.ui-dialog-content').dialog('close');
return false;
});

Change the asynchronous jQuery Dialog to be synchronous?

Currently, I'm working to replace "alert'/"confirm" with the jquery dialog.
But most of legacy codes is written in some asynchronous way, which make it difficult to change. Is there any way to make jquery dialog work in a synchronous way? ( don't use loop or callback function )
For example:
function run()
{
var result = confirm("yes or no");
alert( result );
\\more codes here
}
In this example the alert and other codes will be executed after user's choice.
If we use jquery dialog
var result = $dialog.open()
It will continue to execute the alert, which is asynchronous.
Currently, my solution is to use call back function in the OK|Cancel function.
For example:
OK: function ()
{
$dialog.close();
alert("yes");
//more codes here
}
This method works but it is difficult to make all the synchronous codes become asynchronous, which requires a lot of change (see the following example). So I'm looking for the synchronous jQuery Dialog, is it possible??
For example: ( The real codes are much more complicated than the following example)
function f1()
{
if ( confirm("hello") ) f2();
alert("no");
}
function f2()
{
if( confirm("world") ) f3();
alert("no");
}
function f3()
{
return confirm("!") ;
}
Another example:
vendorObject.on('some-event', function() {
if(confirm("Do you really want to do that?")) {
return true;
}
else {
return false; // cancel the event
}
});
... here the vendor object fires an event, which has to be cancelled if the user confirms. The event can only be cancelled if the event handler returns false - synchronously.
The short answer is no, you won't be able to keep your code synchronous. Here's why:
In order for this to be synchronous, the currently executing script would have to wait for the user to provide input, and then continue.
While there is a currently executing script, the user is unable to interact with the UI. In fact, the UI doesn't even update until after the script is done executing.
If the script can't continue until the user provides input, and the user can't provide input until the script is finished, the closest you'll ever get is a hung browser.
To illustrate this behavior, debug your code and set a break point on the line following a line that changes the UI:
$("body").css("backgroundColor", "red");
var x = 1; // break on this line
Notice that your page background is not yet red. It won't change to red until you resume execution and the script finishes executing. You are also unable to click any links in your page while you've got script execution paused with your debugger.
There is an exception to this rule for alert() and confirm(). These are browser controls, and are treated differently than actual web page UI elements.
The good news is that it really shouldn't be very hard to convert your code. Presumably, your code currently looks something like this:
if (confirm("continue?")) {
// several lines of code if yes
}
else {
// several lines of code if no
}
// several lines of code finally
Your asynchronous version could create a function ifConfirm(text, yesFn, noFn, finallyFn) and your code would look very much the same:
ifConfirm("continue?", function () {
// several lines of code if yes
},
function () {
// several lines of code if no
},
function () {
// several lines of code finally
});
Edit: In response to the additional example you added to your question, unfortunately that code will need to be refactored. It is simply not possible to have synchronous custom confirmation dialogs. To use a custom confirmation dialog in the scenario where an event needs to either continue or cancel, you'll just have to always cancel the event and mimic the event in the yesFn callback.
For example, a link:
$("a[href]").click(function (e) {
e.preventDefault();
var link = this.href;
ifConfirm("Are you sure you want to leave this awesome page?", function () {
location.href = link;
});
});
Or, a form:
$("form").submit(function (e) {
e.preventDefault();
var form = this;
ifConfirm("Are you sure you're ready to submit this form?", function () {
form.submit();
});
});
I'm not exactly sure what the motivation behind not using callbacks is so it is hard to judge what solution might satisfy your requirements, but another way to delay execution is through jQuery's "deferred" object.
http://api.jquery.com/category/deferred-object/
You could set up a function that opens the jquery dialog and add code that "waits" for dialog to close. This ends up working in a fairly similar way to a callback in the case you've laid out but here is an example:
function confirm () {
var defer = $.Deferred();
$('<div>Do you want to continue?</div>').dialog({
autoOpen: true,
close: function () {
$(this).dialog('destroy');
},
position: ['left', 'top'],
title: 'Continue?',
buttons: {
"Yes": function() {
defer.resolve("yes"); //on Yes click, end deferred state successfully with yes value
$( this ).dialog( "close" );
},
"No": function() {
defer.resolve("no"); //on No click end deferred successfully with no value
$( this ).dialog( "close" );
}
}
});
return defer.promise(); //important to return the deferred promise
}
$(document).ready(function () {
$('#prod_btn').click(function () {
confirm().then(function (answer) {//then will run if Yes or No is clicked
alert('run all my code on ' + answer);
});
});
});
Here it is working in jsFiddle: http://jsfiddle.net/FJMuJ/
No, you can't do anything sync in Javascript (alert is breaking the rules, in fact). Javascript is built with "single threaded, async" in the core.
What you can do, though, is disable functionality of the underlying page (lightbox-like) so no event get triggered from the page until you don't take the dialog action, be it OK or Cancel. Thought this does not help you to get your sync code working. You have to rewrite.
Here's some ideas - what you actually want is to block your async event to make it look like sync. Here's some links:
Queuing async calls
Mobl
Narrative JavaScript
Hope this helps you further!!
To answer David Whiteman's more specific question, here's how I'm implementing a "deferred" postback for a LinkButton Click event. Basically, I'm just preventing the default behaviour and firing the postback manually when user feedback is available.
function MyClientClickHandler(event, confirmationMessage, yesText, noText) {
// My LinkButtons are created dynamically, so I compute the caller's ID
var elementName = event.srcElement.id.replace(/_/g, '$');
// I don't want the event to be fired immediately because I want to add a parameter based on user's input
event.preventDefault();
$('<p>' + confirmationMessage + '</p>').dialog({
buttons: [
{
text: yesText,
click: function () {
$(this).dialog("close");
// Now I'm ready to fire the postback
__doPostBack(elementName, 'Y');
}
},
{
text: noText,
click: function () {
$(this).dialog("close");
// In my case, I need a postback when the user presses "no" as well
__doPostBack(elementName, 'N');
}
}
]
});
}
You can use a real modal dialog.
[dialog] is an element for a popup box in a web page, including a modal option which will make the rest of the page inert during use. This could be useful to block a user's interaction until they give you a response, or to confirm an action.
https://github.com/GoogleChrome/dialog-polyfill
I don't really see why you are opposed to using Jquery callbacks to achieve the behavior in your example. You will definitely have to rewrite some code but something like:
function f1() {
$( "#testdiv" ).dialog({
modal: true,
buttons: {
"OK": function() {
$( this ).dialog( "close" );
f2();
},
Cancel: function() {
$( this ).dialog( "close" );
alert('no');
}
}
});
}
function f2() {
$( "#testdiv2" ).dialog({
modal: true,
buttons: {
"OK": function() {
$( this ).dialog( "close" );
f3();
},
Cancel: function() {
$( this ).dialog( "close" );
alert('no');
}
}
});
}
function f3() {
$( "#testdiv3" ).dialog({
modal: true,
buttons: {
"OK": function() {
$( this ).dialog( "close" );
},
Cancel: function() {
$( this ).dialog( "close" );
}
}
});
}
<div id="testdiv" title="Hello"/>
<div id="testdiv2" title="World"/>
<div id="testdiv3" title="!"/>

Categories