The other day a good friend of mine asked me to create a simple three step wizard in AJAX so that it does not reload the page and the whole process feels seamless and smooth. At first i thought, yeah thats easy enough; just change innerHTML property of a div as the user clicks nextwhere the content can be taken from a backend PHP page that keeps track of and records the process. But then he said he wants it in a popup and an unobtrusive one at that. What this means, is that the popup wizard should not be in a separate new window. So in essence its notactually a popup but instead an illusionary effect where the page goes dark while some section is being highlighted making the user thinks that something just popuped up in front of him. Again, easy enough to do withjQuery and there is a wonderfull tutorial on how to do just that.
BUT
If you look at the tutorial you will notice that the effect is achieved by hiding and then emphasizing a certain <div> in a way i just described. So how do you change the content as the user presses the next button? Again, innerHTML sounds like a deal but then you have a humangus ugly JavaScript variable with load of HTML code between single quotes. Its very difficult to edit and manage. Yes, i could use AJAX and let the server pull the approriate content for each step, so at least the setup is modular but it just seems like an overkill. And then i reflected upon one of the most fundamental practices in regular programming languages where the code is put in a function and is called as needed. But HTML alone cannot do that, right?……… OR CAN IT????
First of all lets clear up what AJAX has to do with any of this and thatsnothing. AJAX became such a buzword that its being used in many places it shouldn’t. Just because something changed on the page doesn’t mean there is AJAX on it. Please remember that AJAX is strictly a technique involving calling a server asynchroneously over xmlHTTP request. Actual pretty changes are done by good old JavaScript techniques. So in this case we will be using only old fasion JS with some elementary DOM.
If you want to follow this, i suggest you complete tutorial i mentioned earlier to get some basic concepts down. This entry is a heavy modification of that one. So heavy the end code looks like a completely separate project but the same consept is there. The tutorial is great in every way so instead of telling how to do this technique from scratch I will show only modifications i made to the original one. Ok, sorry for the ramble but i just had to get that clear and out of the way. Now lets begin modifying the original tutorial.
01In the origianal tutorial you will notice that the popup is actually a previously hidden <div id=”popupContact”> that gets highlighted while the rest of the page dims out. This is done with JS functions loadPopup and centerPopup. What i am going to do instead is to take out all the content from <div id=”popupContact”> and use it purely as a placeholder with no content so the whole HTML line is just <div id=”popupContact”></div>
02Next i will create a separate <div> for every step in the wizard and i will make them hidden, using style=”display: none”. It will look something like
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<div id="step1" style="display: none"> <h1>Step one</h1> Name: <input type="text" name="name" /> <br> Email: <input type="text" name="email" /> <a id="popupNext" onClick="loadPopup();">Next<a> </div> <div id="step2" style="display: none"> <h1>Step two</h1> URL: <input type="text" name="url" /> <br> Twitter: <input type="text" name="twitter" /> <a id="popupNext" onClick="loadPopup();">Next<a> </div> |
03Now we will modify his loadPopup function in his JS file to look like
1 2 3 4 5 6 7 8 9 10 |
function loadPopup(){ $("#backgroundPopup").css({ "opacity": "0.7" }); $("#backgroundPopup").fadeIn("slow"); $("#popupContent").fadeIn("slow"); document.getElementById("popupContent").innerHTML = document.getElementById("step"+step).innerHTML; step++; } |
and add var step = 1; at the top of the file. You may also want to add
1 2 3 4 5 6 7 8 9 10 |
#popupNext{ font-size:14px; line-height:14px; right:6px; bottom:4px; position:absolute; color:#6fa5fd; font-weight:700; display:block; } |
to the css file to make next button look appropriate.
Now try to run the modified script and see if the wizard works. Hopefully it does. If it does you are probably surprised (and if it does not please contact me so i can recheck my instructions). So what happened? Notice how i have each <div> IDed as step1, step2 etc. Now look at line 8 in the loadPopup function. What I am doing is tracking the position of the wizard with variable step and then I am grabbing HTML content of the <div> with the appropriate step# and pushing it inside of the placeholder popupContact. So its as if i am “calling” for content of a div: by grabbing innerHTML of a template <div> and pasing it to the innerHTML of a placeholder i am immitating the feel of a function call from other programming languages. Neat huh?
04But what about the data? if you try to submit it through any JS form submission technique you know you will get empty dataset because every time user clicks next innerHTML of the placeholder <div>is rewritten and all data is lost. To keep data in memory until final submit we could use AJAX to pass data to $_SESSION on PHP side but instead I will do something more lightweight and completly server independent. I will create a hidden form placeholder
which will be populated by hidden input fields with names and values taken from the previous wizard step just before it is overwritten with the current. The function to do so is
1 2 3 4 5 6 7 8 9 |
function getValues(elem) { for(var i = 0; i < elem.length; i++) { var inputElement = document.createElement('input'); inputElement.type = "text"; inputElement.name = elem[i].name; inputElement.value = elem[i].value; wizard.appendChild(inputElement); } } |
(to understand how it works look up JavaScript DOM). Variable wizard is initialized with
1 |
wizard = document.getElementById('formWizard');
|
right after $(document).ready(function(){ in the original tutorial. Call of getValues will be achieved with
1 2 3 4 |
if(step > 1){ var elem = document.getElementById('popupContent').getElementsByTagName('input'); getValues(elem); } |
which is right before that magic line in step 3 so the whole function looks like
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
function loadPopup(){ $("#backgroundPopup").css({ "opacity": "0.7" }); $("#backgroundPopup").fadeIn("slow"); $("#popupContent").fadeIn("slow"); if(step > 1){ var elem = document.getElementById('popupContent').getElementsByTagName('input'); getValues(elem); } document.getElementById("popupContent").innerHTML = document.getElementById("step"+step).innerHTML; step++; } |
05To submit the form make the onClick event in the button of the last step onClick=”document.formWizard.submit();” and you are done! You got an unobtrusive popup with seamless wizard.
If you found this usefull but had difficulty following modifications of the original tutorial and would instead like to see full step by step instructions, please tell me and I will make one.Here are full code listings to make this work.
| form.htm | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
<html> <head> <link rel="stylesheet" href="popup.css" type="text/css" media="screen" /> <script src="jquery-1.3.2.js" type="text/javascript"></script> <script src="popup.js" type="text/javascript"></script> </head> <body> <!-- button to begin the process --> <div id="formButton"> <input type="button" value="open form" /></div> <!-- place holders --> <div id="popupContent"></div> <div id="backgroundPopup"></div> <!-- content of the form steps these will never "show up". Instead HTML will be "copied" from them into the placeholder. This is done purely for editor's convenience to avoid long difficult to read variables--> <div id="step1" style="display: none"> <h1>Step one</h1> Name: <input type="text" name="name" /> Email: <input type="text" name="email" /> <a id="popupNext" onClick="loadPopup();">Next<a> </div> <div id="step2" style="display: none"> <h1>Step two</h1> URL: <input type="text" name="url" /> Twitter: <input type="text" name="twitter" /> <a id="popupNext" onClick="loadPopup();">Next<a> </div> <div id="step3" style="display: none"> <h1>Thank you!</h1> <a id="popupNext" onClick="document.formWizard.submit();">Submit</a> </div> <div style="display: none"> <form name="formWizard" id="formWizard" action="backend.php" method="post"></form> </div> </body> </html> |
| popup.js | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
var step = 1; var wizard; // load it function loadPopup(){ $("#backgroundPopup").css({ "opacity": "0.7" }); $("#backgroundPopup").fadeIn("slow"); $("#popupContent").fadeIn("slow"); if(step > 1){ var elem = document.getElementById('popupContent').getElementsByTagName('input'); getValues(elem); } document.getElementById("popupContent").innerHTML = document.getElementById("step"+step).innerHTML; step++; } // disable function disablePopup(){ // disable only if enabled if(popupStatus==1){ $("#backgroundPopup").fadeOut("slow"); $("#popupContent").fadeOut("slow"); popupStatus = 0; } } //center it function centerPopup(){ //request data for centering var windowWidth = document.documentElement.clientWidth; var windowHeight = document.documentElement.clientHeight; var popupHeight = $("#popupContent").height(); var popupWidth = $("#popupContent").width(); //centering $("#popupContent").css({ "position": "absolute", "top": windowHeight/2-popupHeight/2, "left": windowWidth/2-popupWidth/2 }); //only need force for IE6 $("#backgroundPopup").css({ "height": windowHeight }); } function getValues(elem) { for(var i = 0; i < elem.length; i++) { var inputElement = document.createElement('input'); inputElement.type = "text"; inputElement.name = elem[i].name; inputElement.value = elem[i].value; wizard.appendChild(inputElement); } } $(document).ready(function(){ wizard = document.getElementById('formWizard'); // ===========loading popup begin========= // button click event $("#formButton").click(function(){ //centering with css centerPopup(); //load popup loadPopup(); }); // -----------loading popup end----------- }); |
| popup.css | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td { border:0pt none; font-family:inherit; font-size:100%; font-style:inherit; font-weight:inherit; margin:0pt; padding:0pt; vertical-align:baseline; } body{ background:#fff none repeat scroll 0%; line-height:1; font-size: 12px; font-family:arial,sans-serif; margin:0pt; height:100%; } a{ cursor: pointer; text-decoration:none; } br.both{ clear:both; } #backgroundPopup{ display:none; position:fixed; _position:absolute; /* hack for internet explorer 6*/ height:100%; width:100%; top:0; left:0; background:#000000; border:1px solid #cecece; z-index:1; } #popupContent{ display:none; position:fixed; _position:absolute; /* hack for internet explorer 6*/ height:384px; width:408px; background:#FFFFFF; border:2px solid #cecece; z-index:2; padding:12px; font-size:13px; } #popupContent h1{ text-align:left; color:#6FA5FD; font-size:22px; font-weight:700; border-bottom:1px dotted #D3D3D3; padding-bottom:2px; margin-bottom:20px; } #popupNext{ font-size:14px; line-height:14px; right:6px; bottom:4px; position:absolute; color:#6fa5fd; font-weight:700; display:block; } |
| < Prev |
|---|
