I have a game, and at certain points I need to prompt the user.
For one question, it's simple. I place an overlay over everything else, and have a yes and no button. I change the actions of the buttons depending on the question, but in some cases I need to ask 2 or 3 questions. By using confirm() I could just put this in a for loop, but at the moment it will just change the questions and answers.
I was thinking to set a variable like numberOfQs = 3 and then currentQ = 1 and after the yes/no button is clicked I increment and call a question function until currentQ === numberOfQs
Is that the best/only option? I don't want to use confirm() or prompt() because I can't style it.
No JS library answers please.
2 Answers 2
Problem
How can I pause JavaScript until input, like a confirm/alert box?
Simple answer is: you can't (if styling is required).
JS is asynchronous and single-threaded so there is no way to prevent running the code as all "inputs" (DOM buttons, input fields etc.) are asynchronous in nature (ie. by using an event queue). Doing a busy-loop (for/while) for polling would simply block the thread so you couldn't check events and polling using for example setTimeout would make it asynchronous again.
You can use prompt() (not style-able but blocks the browser as you intend) or use an overlay as you already do and handle events the way JS was designed for. Flags are useful here to block UX (ghosting/disabling-wise) while waiting for a condition to be fulfilled. It's all about program design.
If you already have an overlay simply replace the question text with the next until there are no more and then remove the overlay. You can embed this into an object that handles the buttons and text for you.
Solution
Creating objects that are self-contained makes this a walk in the park.
Lets create two objects:
- Poll master
- A Question object.
The Poll object will setup the overlay and remove it when done as well as call our callback.
The Question object will setup the question text, buttons and handle the button clicks and answer.
Online demo here
The Poll object looks like this:
function Poll(callback) {
var overlay = document.createElement('div'), /// overlay DIV
current, /// question counter
me = this; /// self-reference
/// question array
this.questions = [];
/// setup overlay div with classname
overlay.className = 'overlay';
/// method to add questions
this.add = function(q) {
me.questions.push(q);
}
/// start the poll
this.start = function() {
if (me.questions.length === 0) return;
/// inject the overlay to DOM
document.body.appendChild(overlay);
/// start the displaying of questions
current = 0;
next();
}
/// called from question itself as well as a callback
function next() {
if (current < me.questions.length) {
/// provide overlay (parent) and callback
me.questions[current].show(overlay, next);
current++;
} else {
/// when done, remove overlay and callback
document.body.removeChild(overlay);
callback();
}
}
}
Not so complicated: it keeps a mechanism to add question objects, start the poll and go to next question.
The Question object looks like this:
function Question(txt) {
/// create the various elements we need
var box = document.createElement('div'),
btnYes = document.createElement('button'),
btnNo = document.createElement('button'),
me = this;
/// setup question box with text and class name
box.className = 'question';
box.innerHTML = txt;
/// setup buttons with text, classes and event handlers
btnYes.className = 'buttonYes';
btnNo.className = 'buttonNo';
btnYes.innerHTML = 'YES';
btnNo.innerHTML = 'NO';
/// add the buttons to question box
box.appendChild(btnYes);
box.appendChild(btnNo);
btnYes.onclick = handleYes;
btnNo.onclick = handleNo;
/// expose question and answer
this.question = txt;
this.answer = null;
/// called from Poll object to show question
this.show = function(parent, callback) {
me.parent = parent;
me.callback = callback;
/// add question box to overlay
parent.appendChild(box);
}
/// if we clicked "yes", set answer and remove box
function handleYes() {
me.answer = true;
done();
}
/// if we clicked "no", set answer and remove box
function handleNo() {
me.answer = false;
done();
}
function done() {
/// remove box and call "next()" in Poll object
me.parent.removeChild(box);
me.callback(me.answer);
}
}
Usage
Now when these objects are in place we simply setup a poll like this:
var poll = new Poll(done);
poll.add(new Question('This will work fine?'));
poll.add(new Question('This is second question?'));
poll.add(new Question('This is third question?'));
poll.start();
And that's it. All we need to do now is to define the CSS rules for the classes we added.
We can also iterate the question objects as well to see what we answered:
function done() {
for(var i = 0, q; q = poll.questions[i]; i++)
console.log(q.question, q.answer);
}
Hope this helps.
Comments
window.prompt("sometext","defaultText");
var answer = prompt("some question")...?