37

I've a for-loop I'm looping through.

I want to make a custom modal and wait for a response before continue it.

How can I achieve this? I know I've to wait for a callback.

Like this example:

 for(var x in array){
 alert(x);
 console.log(x);
 }

It does exactly what I want to. But I want to have three buttons. But alert is not part of javascript(? It's in the browser.)

So, do you guys have an idea?

I was thinking about doing something like this:

var run = true;
function foo(){
 if (run){
 setTimeout(foo, 500);
 }
}
function stop(){
 run = false;
}
foo();

and then wait for a stop which calls on a button click before continue. But is this really good practice?

Or use a lambda function as a parameter to the customAlert and a "global" variable that holds the current position of the array I'm going through and do this with functions. Like: Check if array is still holding keys greater than X. Then do the function again and each time increase the global X.

Thank you lostsource for the code: Oh, I got an idea; I'll simply use lostsource's solution inside an anonymous function, so I don't get global variables. Excellent.

(function(){
})();
Brian
16k7 gold badges38 silver badges43 bronze badges
asked Jan 18, 2013 at 22:29
5
  • Are you saying you can't actually use alert because it does not support enough options? Commented Jan 18, 2013 at 22:35
  • It is not possible to simply replace alert() with a custom modal and have it perform the same way alert does Commented Jan 18, 2013 at 22:36
  • alerts are synchronous BTW. You won't be able to halt a function execution like that - unless using JS1.7's coroutines/yield but that isn't very cross-browser and requires defining a version on your script type. You should probably store the i inside an execution context and pass it to a function that does one more iteration with it which calls your styled alert which once again passes an incremented i back to your iterator function. Commented Jan 18, 2013 at 22:41
  • @FabrícioMatté I edited my post. do you think it would be good practice? :/ Commented Jan 18, 2013 at 22:49
  • +1 for improving the question. Yeah, Lostsource's code template is even tidier than what I thought. Commented Jan 18, 2013 at 23:07

2 Answers 2

89

Assuming this is your array

var list = ['one','two','three'];

You can try using this loop / callback approach

var x = 0;
var loopArray = function(arr) {
 customAlert(arr[x],function(){
 // set x to next item
 x++;
 // any more items in array? continue loop
 if(x < arr.length) {
 loopArray(arr); 
 }
 }); 
}
function customAlert(msg,callback) {
 // code to show your custom alert
 // in this case its just a console log
 console.log(msg);
 // do callback when ready
 callback();
}

Usage:

// start 'loop'
loopArray(list);

JSFiddle here: http://jsfiddle.net/D9AXp/

answered Jan 18, 2013 at 22:47
Sign up to request clarification or add additional context in comments.

6 Comments

Yeah, I had an idea like that too (about using a lambda function) :) Thank you very much.
Extremely helpful thank you, awesome i just used arr.lenght-1
Actually, I do not think this works if the callback has to do anything asynchronously. Please check this out, I'd love to know if there is a solution for this, as I'm trying to load image files one by one that take a bit of time each: jsfiddle.net/evejweinberg/tydfbop6/1
You just need to put callback in success return or error return.
The idea is: "Write a loop that does not advance to the next index/item in the array (like regular for-loop) until each request calls/completes its callback". The example above is very simple yet effective. Thank you @lostsource
|
4

MaggiQall, I know you have an answer but I have a flexible solution that may be of interest to you or maybe to someone else.

The solution depends on jQuery (1.7+) and jQuery UI's dialog, but is implemented as a custom method of the Array prototype, not as a jQuery plugin.

Here's the Array method :

Array.prototype.runDialogSequence = function(dialogCallback, startIndex, endIndex){
 startIndex = Math.max(0, startIndex || 0);
 endIndex = Math.min(this.length - 1, endIndex || this.length - 1);
 var sequenceIndex = 0,
 arr = this,
 dfrd = $.Deferred().resolve(startIndex);
 function makeCloseFn(seqData){
 return function(event, ui){
 if(seqData.continue_) { seqData.dfrd.resolve(seqData.arrayIndex+1, seqData); } //continue dialog sequence
 else { seqData.dfrd.reject(seqData.arrayIndex, seqData); } //break dialog sequence
 }
 }
 $.each(this, function(i){
 if(i < startIndex || i > endIndex) { return true; }//continue
 dfrd = dfrd.then(function(arrayIndex){
 var seqData = {
 dfrd: $.Deferred(),
 arrayIndex: arrayIndex,
 sequenceIndex: ++sequenceIndex,
 length: 1 + endIndex - startIndex,
 item: arr[arrayIndex],
 continue_: false
 };
 dialogCallback(seqData).on("dialogclose", makeCloseFn(seqData));
 return seqData.dfrd.promise();
 });
 });
 return dfrd.promise();
}

Array.runDialogSequence() relies on :

  • A dialog template in the document's body, fit to be populated with text/values.
  • an array of similar items (typically javascript objects) containing the data required to populate the dialog, in sequence.
  • passing, as the first argument, a correctly constructed "openDialog" function.

Here's a sample "openDialog" function with explanatory comments :

function openDialog(seqData){
 /*
 seqData is an object with the following properties:
 dfrd: A Deferred object associated with the current dialog. Normally resolved by Array.runDialogSequence() to run through the sequence or rejected to break it, but can be resolved/rejected here to force the dialog sequence to continue/break. If resolved, then pass (seqData.arrayIndex+1, seqData), or custom values. If rejected, typically pass (seqData.arrayIndex, seqData).
 arrayIndex: The current array index.
 sequenceIndex: The current index within the sequence. Normally the same as arrayIndex but Differs when a non-zero startIndex is specified.
 length: The full length of the dialog sequence.
 item: The current item from the array.
 continue_: (false) Set to true to allow the sequence to continue.
 */
 var item = seqData.item;
 var $d = $("#d");
 $d.dialog({
 title: 'Dialog (' + seqData.sequenceIndex + ' of ' + seqData.length + ')',
 modal: true,
 buttons: {
 "Continue": function(){
 seqData.continue_ = true;//set to true before closing to go to next dialog.
 $(this).dialog("close");
 },
 "Cancel": function(){
 $(this).dialog('close');//closing with seqData.continue_ set to its default value false will break the dialog sequence.
 }
 }
 }).find("#message").text(item);//Typically you will do a lot more here to populate the dialog with item data.
 return $d;//openDialog() must return a dialog container, jQuery-wrapped.
}

Array.runDialogSequence() returns a jQuery promise, allowing custom actions to be performed when the dialog sequence completes (done function) or is broken in mid-sequence (fail function).

Here are a couple of sample calls :

//Simplest possible
$("#myButton1").click(function(){
 myArray.runDialogSequence(openDialog);
});
//Call with custom startIndex and endIndex, and chanined `.then()` to provide custom actions on break/completion.
$("#myButton2").click(function(){
 myArray.runDialogSequence(openDialog, 1, 3).then(function(i, seqData){
 alert('All dialogs complete - last item = "' + seqData.item + '"');
 }, function(i, seqData){
 alert('Dialog sequence was broken at item ' + i + ' "' + seqData.item + '"');
 });
});

DEMO

This is as close to a generalized solution as my imagination allows.

answered Jan 21, 2013 at 2:10

1 Comment

Thanks MaggiQall, it will be interesting to see if anyone finds this useful in the future.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.