PDA

View Full Version : Loading PHP page using AJAX



corbo950
06-28-2010, 02:31 AM
Ok so I'm putting this under JavaScript as the problem is in the AJAX but it does contain PHP... So I'm trying to create a sign up form for a swim meet. The form has a drop down selector where you select the number of swimmers you want to sign up and then a javascript function loops over an AJAX request to add a PHP page which is the form inputs for each swimmer.

So I have tested and when I have the AJAX function show an alert with the return from the AJAX and it returns the PHP page just fine... but when I try to have that function return the data to the original function that is looping over it the data comes back as undefined. I have included the pages below. You will see an events page as well.. this will be looped over in a similar way but have not coded that part yet as I need to first get this part working

Thanks for any help... I would also gladly accept any suggestions for improvement as well

signup_form.php


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Event Signup</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<link href="signup.css" rel="stylesheet" type="text/css">
<?php

if($_GET['swimmer_number'])
{
$swimmer_number = $_GET['swimmer_number'];
}
else
{
$swimmer_number = 1;
}
if($_GET['event_number'])
{
$event_number = $_GET['event_number'];
}
else
{
$event_number = 1;
}
?>
<script type="text/javascript">
function changeSwimmerNumber(number_swimmers)
{
var swimmersHTML = "";

for(i=1;i<=number_swimmers;i++)
{
swimmersHTML += getSwimmer(i);
}

document.getElementById("swimmers_holder").innerHTML = swimmersHTML;
}
function getSwimmer(swimmer)
{
if (window.XMLHttpRequest)
{// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
}
else
{// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange=function()
{
if(xmlhttp.readyState==4 && xmlhttp.status==200)
{
var htmlReturn = xmlhttp.responseText;
return(htmlReturn);
}
}
xmlhttp.open("GET", "swimmer.php?swimmer="+swimmer, true);
xmlhttp.send();
}
</script>
</head>
<body>
<form name="events" action="signup_result.php" method="get">
<fieldset>
<legend>Event Signup</legend>
<p class="selecter_holder">
Number of Swimmers:
<select id="swimmer_number" onChange="changeSwimmerNumber(this.value)">
<option value="1">1</option>
<option value="2" selected="selected">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
</select>
</p>
<br />
<div id="swimmers_holder">
</div>
</fieldset>
</form>
</body>
</html>


swimmer.php


<?php

if($_GET['swimmer'])
{
$swimmer = $_GET['swimmer'];
}
else
{
echo("Foobar!");
}

?>
<fieldset id="swimmer_holder">
<legend>Swimmer <?php echo($swimmer); ?></legend>
<p class="inputs_holder">
First name: <input type="text" name="firstName_<?php echo($swimmer); ?>" />
<br />
Last name: <input type="text" name="fastName_<?php echo($swimmer); ?>" />
</p>
<br />
<p class="selecter_holder">
Number of Events:
<select name="swimmer_number_<?php echo($swimmer); ?>">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4" selected="selected">4</option>
<option value="5">5</option>
<option value="6">6</option>
<option value="7">7</option>
<option value="8">8</option>
<option value="9">9</option>
<option value="10">10</option>
<option value="11">11</option>
<option value="12">12</option>
<option value="13">13</option>
<option value="14">14</option>
<option value="15">15</option>
</select>
</p>
<br />
<fieldset>
<legend>Events</legend>
<div id="events_holder_<?php echo($swimmer); ?>">
<p class="inputs_holder">
Event Number: <input type="text" name="eventNumber" />
<br />
Event Name: <input type="text" name="eventName" />
</p>
</div>
</fieldset>
</fieldset>
<?php
$swimmer = '';
?>

jscheuer1
06-28-2010, 05:20 PM
Two major problems. In no particular order, here:


if (window.XMLHttpRequest)
{// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
}
else
{// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}

Regardless of the method employed, xmlhttp is in the global scope. AJAX requests are not instantaneous. So before one is finished (if more than one swimmer), another overwrites it. But that doesn't even come into play until you realize that the return value of the onreadystatechange function is not the return value of the getSwimmer function.

Once that's worked out, I found that the order in which the requests were completed wasn't necessarily the order in which they were made. So you could get swimmer 3 followed by 1, by 4, then 2, for example. All accounted for, but not in the order expected. So I worked out a way to store the return values in an array in the proper order until all were in, and then writing them to the output division:


<script type="text/javascript">
function changeSwimmerNumber(number_swimmers)
{
getSwimmer.ar = []; //create empty array to hold responses
getSwimmer.swimmers = 0; //create counter

for(i=1;i<=number_swimmers;i++)
{
getSwimmer(i, number_swimmers); // pass the total number as the second parameter
}

}
function getSwimmer(swimmer, number_swimmers)
{
var xmlhttp = {}; //create a local instance of xmlhttp that will not be overwritten by others (if any) in this run
if (window.XMLHttpRequest)
{// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
}
else
{// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange=function()
{
if(xmlhttp.readyState==4 && xmlhttp.status==200)
{
var htmlReturn = xmlhttp.responseText;
++getSwimmer.swimmers; //increment the counter
getSwimmer.ar[swimmer] = htmlReturn; //put this response in its proper order in the array
if(getSwimmer.swimmers == number_swimmers) //if the counter equals the total
document.getElementById("swimmers_holder").innerHTML = getSwimmer.ar.join('');
}
}
xmlhttp.open("GET", "swimmer.php?swimmer="+swimmer, true);
xmlhttp.send();
}
</script>

clueful
06-28-2010, 06:11 PM
Ok so I'm putting this under JavaScript as the problem is in the AJAX but it does contain PHP... So I'm trying to create a sign up form for a swim meet. The form has a drop down selector where you select the number of swimmers you want to sign up and then a javascript function loops over an AJAX request to add a PHP page which is the form inputs for each swimmer.You don't need AJAX to create a form. Just generate the elements using DOM methods. You can Google an explanation of doing it the proper way and the I.E. way.

jscheuer1
06-28-2010, 06:55 PM
Here's a slightly more robust version:


<?php

if(isset($_GET['swimmer_number']))
{
$swimmer_number = $_GET['swimmer_number'];
}
else
{
$swimmer_number = 1;
}
if(isset($_GET['event_number']))
{
$event_number = $_GET['event_number'];
}
else
{
$event_number = 1;
}
?>
<script type="text/javascript">
function changeSwimmerNumber(number_swimmers)
{
document.getElementById("swimmers_holder").innerHTML = "Loading . . ."; // display loading message while fetching contents
getSwimmer.ar = []; // create/reset empty array to hold responses
getSwimmer.swimmers = 0; // create/reset counter

for(i = 1; i <= number_swimmers; ++i)
{
getSwimmer(i, number_swimmers); // pass the total number as the second parameter
}

}
function getSwimmer(swimmer, number_swimmers)
{
var xmlhttp = {}; // create a local instance of xmlhttp that will not be overwritten by others (if any) in this run
if (window.XMLHttpRequest)
{// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp = new XMLHttpRequest();
}
else
{// code for IE6, IE5
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange = function()
{
if(xmlhttp.readyState==4 && xmlhttp.status==200)
{
var htmlReturn = xmlhttp.responseText;
++getSwimmer.swimmers; // increment the counter
getSwimmer.ar[swimmer] = htmlReturn; // put this response in its proper order in the array
if(getSwimmer.swimmers == number_swimmers) // if the counter equals the total
{
document.getElementById("swimmers_holder").innerHTML = getSwimmer.ar.join('');
}
}
}
xmlhttp.open("GET", "swimmer.php?swimmer=" + swimmer, true);
xmlhttp.send();
}
changeSwimmerNumber.onload = function(){ // run onload to display some swimmer(s)
changeSwimmerNumber(document.getElementById('swimmer_number').value);
}
if (window.addEventListener){
window.addEventListener('load', changeSwimmerNumber.onload, false);
}
else if (window.attachEvent){
window.attachEvent('onload', changeSwimmerNumber.onload);
}
</script>

corbo950
06-28-2010, 07:34 PM
Thank you so much it works great! I do have one question though.... in attempting to fix the problem I started rewriting the functions using jQuery.... just wondering if you think jQuery is a good thing to use.... again thanks

jscheuer1
06-28-2010, 07:47 PM
I love jQuery. Just make sure you understand the parts of it you are using. It's not worth the effort or byte load though unless your site has other scripts that use it. In fact, the maximum benefit to jQuery is to use it for everything (all javascript on a site).

corbo950
06-28-2010, 11:38 PM
Ok so i tired reading through your script and then applying it to the events part of the form as well and i got it to add them when selected but for some reason no matter what events number drop down I select it only effects the first swimmer ... what am i doing wrong? ... here is the code now:

signup_form.php


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Event Signup</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<link href="signup.css" rel="stylesheet" type="text/css">
<script type="text/javascript" src="signup.js"></script>
</head>
<body>
<form name="events" action="signup_result.php" method="get">
<fieldset>
<legend>Event Signup</legend>
<p class="selecter_holder">
Number of Swimmers:
<select id="swimmer_number" onChange="changeSwimmerNumber(this.value)">
<option value="1">1</option>
<option value="2" selected="selected">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
</select>
</p>
<br />
<div id="swimmers_holder">
</div>
</fieldset>
</form>
</body>
</html>


swimmer.php


<?php
if(isset($_GET['swimmer']))
{
$swimmer_number = $_GET['swimmer'];
}
else
{
$swimmer_number = "Error could not load swimmer";
}
?>
<fieldset>
<legend>Swimmer <?php echo($swimmer); ?></legend>
<p class="inputs_holder">
First name: <input type="text" name="firstName_<?php echo($swimmer); ?>" />
<br />
Last name: <input type="text" name="fastName_<?php echo($swimmer); ?>" />
</p>
<br />
<p class="selecter_holder">
Number of Events:
<select id="<?php echo($swimmer); ?>" name="event_number_<?php echo($swimmer); ?>" onChange="changeEventNumber(this.value, this.id)">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4" selected="selected">4</option>
<option value="5">5</option>
<option value="6">6</option>
<option value="7">7</option>
<option value="8">8</option>
<option value="9">9</option>
<option value="10">10</option>
<option value="11">11</option>
<option value="12">12</option>
<option value="13">13</option>
<option value="14">14</option>
<option value="15">15</option>
</select>
</p>
<br />
<fieldset>
<legend>Events</legend>
<div id="events_holder_<?php echo($swimmer); ?>">
</div>
</fieldset>
</fieldset>
<?php
$swimmer = '';
?>


event.php

<?php

if(isset($_GET['event']))
{
$swimmer_number = $_GET['event'];
}
else
{
$swimmer_number = "Error could not load event";
}

?>
<fieldset>
<legend>Event <?php echo($event); ?></legend>
<p class="inputs_holder">
Event Number: <input type="text" name="eventNumber" />
<br />
Event Name: <input type="text" name="eventName" />
</p>
</fieldset>


signup.js


// JavaScript Document
function changeSwimmerNumber(number_swimmers)
{
document.getElementById("swimmers_holder").innerHTML = "Loading . . ."; // display loading message while fetching contents
getSwimmer.ar = []; // create/reset empty array to hold responses
getSwimmer.swimmers = 0; // create/reset counter

for(i = 1; i <= number_swimmers; ++i)
{
getSwimmer(i, number_swimmers); // pass the total number as the second parameter
}

}
function getSwimmer(swimmer, number_swimmers)
{
var xmlhttp = {}; // create a local instance of xmlhttp that will not be overwritten by others (if any) in this run
if (window.XMLHttpRequest)
{// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp = new XMLHttpRequest();
}
else
{// code for IE6, IE5
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange = function()
{
if(xmlhttp.readyState==4 && xmlhttp.status==200)
{
var htmlReturn = xmlhttp.responseText;
++getSwimmer.swimmers; // increment the counter
getSwimmer.ar[swimmer] = htmlReturn; // put this response in its proper order in the array
if(getSwimmer.swimmers == number_swimmers) // if the counter equals the total
{
document.getElementById("swimmers_holder").innerHTML = getSwimmer.ar.join('');
}
}
}
xmlhttp.open("GET", "swimmer.php?swimmer=" + swimmer, true);
xmlhttp.send();
}
function changeEventNumber(number_events, swimmer_number)
{
eventsHolder = "events_holder_" + swimmer_number;
document.getElementById(eventsHolder).innerHTML = "Loading . . ."; // display loading message while fetching contents
getEvents.ar = []; // create/reset empty array to hold responses
getEvents.events = 0; // create/reset counter

for(i = 1; i <= number_events; ++i)
{
getEvents(i, number_events, eventsHolder); // pass the total number as the second parameter
}

}
function getEvents(swim_event, number_events, eventsHolder)
{
var xmlhttp = {}; // create a local instance of xmlhttp that will not be overwritten by others (if any) in this run
if (window.XMLHttpRequest)
{// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp = new XMLHttpRequest();
}
else
{// code for IE6, IE5
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange = function()
{
if(xmlhttp.readyState==4 && xmlhttp.status==200)
{
var htmlReturn = xmlhttp.responseText;
++getEvents.events; // increment the counter
getEvents.ar[swim_event] = htmlReturn; // put this response in its proper order in the array
if(getEvents.events == number_events) // if the counter equals the total
{
document.getElementById(eventsHolder).innerHTML = getEvents.ar.join('');
}
}
}
xmlhttp.open("GET", "forms/event.php?event="+ swim_event, true);
xmlhttp.send();
}
changeSwimmerNumber.onload = function()
{ // run onload to display some swimmer(s)
changeSwimmerNumber(document.getElementById('swimmer_number').value);
}
if (window.addEventListener)
{
window.addEventListener('load', changeSwimmerNumber.onload, false);
}
else if (window.attachEvent)
{
window.attachEvent('onload', changeSwimmerNumber.onload);
}

jscheuer1
06-29-2010, 09:21 AM
I see some logical inconsistencies in some of the code (not the signup.js).

On swimmer.php you define $swimmer_number, but in the HTML portion of the page you reference $swimmer, which is undefined, though some PHP setups would understand it to mean the $_GET['swimmer'] value and it might work anyway. Mine is a little more strict, so I was getting an error. This (on swimmer.php):


<?php
if(isset($_GET['swimmer']))
{
$swimmer_number = $_GET['swimmer'];
}
else
{
$swimmer_number = "Error could not load swimmer";
}
?>

should be:


<?php
if(isset($_GET['swimmer']))
{
$swimmer = $_GET['swimmer'];
}
else
{
$swimmer = "Error could not load swimmer";
}
?>

Pretty much the same deal on event.php, where:


<?php

if(isset($_GET['event']))
{
$swimmer_number = $_GET['event'];
}
else
{
$swimmer_number = "Error could not load event";
}

?>

should be:


<?php

if(isset($_GET['event']))
{
$event = $_GET['event'];
}
else
{
$event = "Error could not load event";
}

?>

Once I fixed up that stuff, it worked fine. There would probably be some problems when the form is submitted. All resulting:


<p class="inputs_holder">
Event Number: <input name="eventNumber" type="text">
<br>
Event Name: <input name="eventName" type="text">
</p>


are the same. But other than that, it was fine. One potential problem could be the fact that you list the location of event.php (in signup.js) as:


xmlhttp.open("GET", "forms/event.php?event="+ swim_event, true);

That makes the script look for it in the forms folder. Your other forms (signup_form.php and swimmer.php) are in the current folder.

If the script can't find the event.php page it will get stuck on:


Loading . . .

corbo950
06-29-2010, 05:33 PM
Thanks I'm so new to JS that i kept assuming the problem was there... should have just checked my PHP lol

corbo950
06-29-2010, 05:43 PM
So now I added a for loop to the getSwimmer function to add the events when each swimmer loads but i can only get one swimmer to load the events... like if i set it to load into number 2 then number 2 loads... and if I replace the changeEventNumber function with an alert it pops up 1 & 2 like it should for i but if I add an alert infront of the changeEventNumber function it will only show 1 ... why is this?



function getSwimmer(swimmer, number_swimmers)
{
var xmlhttp = {}; // create a local instance of xmlhttp that will not be overwritten by others (if any) in this run
if (window.XMLHttpRequest)
{// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp = new XMLHttpRequest();
}
else
{// code for IE6, IE5
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange = function()
{
if(xmlhttp.readyState==4 && xmlhttp.status==200)
{
var htmlReturn = xmlhttp.responseText;
++getSwimmer.swimmers; // increment the counter
getSwimmer.ar[swimmer] = htmlReturn; // put this response in its proper order in the array
if(getSwimmer.swimmers == number_swimmers) // if the counter equals the total
{
document.getElementById("swimmers_holder").innerHTML = getSwimmer.ar.join('');
for(i = 1; i <= number_swimmers; ++i)
{
changeEventNumber(4, i);
}
}
}
}
xmlhttp.open("GET", "swimmer.php?swimmer=" + swimmer, true);
xmlhttp.send();
}

jscheuer1
06-29-2010, 08:19 PM
I'm not sure exactly what happens. But when we do:


xmlhttp.open("GET", "forms/event.php?event="+ swim_event, true);

we are telling javascript to perform an asynchronous request.

This means that it can do other things while it waits for the results to come through. Like, for example, continue to increment i. As I say, I'm not certain how this affects the outcome (probably varies by browser). But if we make it a synchronous request, that takes care of the problem (to do so we must set false, get rid of the onreadystatechange function which no longer applies, and move things around a bit):


function getEvents(swim_event, number_events, eventsHolder)
{
var xmlhttp = {}; // create a local instance of xmlhttp that will not be overwritten by others (if any) in this run
if (window.XMLHttpRequest)
{// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp = new XMLHttpRequest();
}
else
{// code for IE6, IE5
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.open("GET", "forms/event.php?event="+ swim_event, false);
xmlhttp.send();
var htmlReturn = xmlhttp.responseText;
++getEvents.events; // increment the counter
getEvents.ar[swim_event] = htmlReturn; // put this response in its proper order in the array
if(getEvents.events == number_events) // if the counter equals the total
{
document.getElementById(eventsHolder).innerHTML = getEvents.ar.join('');
}
}

When we make our request synchronous, javascript must wait until each one is finished before performing the next. This may or may not work so well live. But it works great here in my PHP sandbox. It may be fine live. Regardless of asynchronous or synchronous, if the page to be fetched is unavailable for any reason, there will be problems. With asynchronous, this will not halt all further script processing on the page. So if you have error catching routines (which you don't), you can recover in some way or give the user a message to try again, or try again later, report the error, etc.). With a synchronous request, an error will stop processing. So there is no way to recover gracefully from that. Also, with an async request, other work can be done while the async request is going on. With a sync request it's a little like an alert. Nothing happens (the page doesn't continue to load, other javascript waits, etc.) until the user clicks OK. Except here, nothing happens until the request is successfully returned.

corbo950
06-29-2010, 09:42 PM
Thanks... I'm not really sure why that makes my loop not work though? can javascript only handle a couple asynchronous tasks at a time? Because the loop is after the swimmers have been loaded? so why would that effect the loop that i have another set of requests inside of it? Sorry I'm used to using Actionscript and I keep running into stuff like this with JS where I don't really understand the issue because there just wouldn't be one in actionscript.

If so that this is incorrect way to do something like this in JS then what is the correct way.... I am very confused by the seemly lack of standard in JS coding... even simply across browsers.

jscheuer1
06-30-2010, 01:22 AM
As I say, I don't fully understand it myself. However, working with javascript as much as I do, I'm completely at peace with variations among browsers. You will encounter them with actionscript as well if you work with it enough. Not so much because of actionscript itself (though there are probably some examples of that), rather more due to the various ways the Flash plugin is implemented. And attributable to the fact that not everyone has the latest Flash plugin installed.

This is somewhat like PHP, which is even less vulnerable because it's server based. But, via PHP, if you serve code (javascript and/or plain HTML) that isn't serviceable cross browser, you will still have problems.

Getting back to the issue in AJAX requests of async vs. sync - It's like I say. Once the request is invoked, if it's async, others things can proceed. So, even though your swimmers request(s) may be finished (are all of them? - let's say they are), the for loop around the events isn't. This means that the browser is free to continue processing. There are almost bound to be collisions/omissions/skipping.

In javascript there are two basic principles:


Do things in the most standard way possible.


Do what works.

These are frequently at odds with each other.

The very nature of AJAX requests (at the moment) exemplifies this. We are requesting content. The most efficient way to deal with this would be as an xml document (though this would require all requested content be valid xml documents, often they are not). But IE doesn't implement that. So we are stuck with a string. The most efficient way of dealing with that is by importing it as innerHTML to an element. But innerHTML is non-standard.

I'm offering the benefit of my knowledge and experience here, as well as my willingness to learn and to try different things. Please don't hold me or the technology responsible for variations among browsers or for the intricacies of the AJAX request method. It's one of the more complex aspects of javascript, and involves (by its very nature) more than javascript alone.

There are other ways to go about this without AJAX, even without javascript. Each method has advantages and disadvantages. No method will work if you ignore browser differences or try to get it to do something it cannot. Here we are combining PHP, AJAX, and javascript. It can probably (almost certainly) be worked out. But if it cannot, there are always other ways.

corbo950
06-30-2010, 01:58 AM
Ok so I should just keep messing with the code until i find what works? Is there real no reference i can use? cause according to W3schools I'm doing everything right. I guess what i really need is the equivalent of Adobe's AS3 Language reference. Or like Python's documentation... and after much googling. I can't seem to find a single real consistent reference or am I wrong about that?

jscheuer1
06-30-2010, 06:28 AM
See:

http://www.w3schools.com/ajax/ajax_xmlhttprequest_send.asp

Scroll down to where it explains the difference between asynchronous and synchronous requests.

It's pretty much like my explanation:


. . . with an async request, other work can be done while the async request is going on. With a sync request it's a little like an alert. Nothing happens (the page doesn't continue to load, other javascript waits, etc.) until the user clicks OK. Except here, nothing happens until the request is successfully returned.

Now, as explained on that page, async is preferred and (something I had forgotten) part of what makes it AJAX. However, you can clearly see that one way or another the asynchronous nature of the request is causing issues here.

As also explained there, if the request is simple (here it is), and the server isn't slow (no way to really control that, a good server will be fine though), things will work out well.

If you aren't comfortable with that, and I could understand why, we can do it without a request to the server.