Web Design - ***Enseignant Fiable***
Assignment 7 Info/ajax(1).pdf
Using AJAX for Your FlashCard Game
In the earlier lesson, you learned how PHP can be used to generate complete web pages. That
is the sort of thing you would see in an online store to generate pages for items in their catalog.
But for interactive sites, like the Flash Card exercise, generating a new page for each picture is
not ideal. What we really want it to keep the page and just update the picture and four choices.
That possibility exists. The technology is called AJAX, which is an acronym for Asynchronous
JavaScript and XML. Here we will do the asynchronous part, without the XML.
The page sends a request from JavaScript. The response shows up as an event. Both the
request and the response event use a special object called XMLHttpRequest.
The request in JavaScript has the following four steps:
var ajaxObject = new XMLHttpRequest(); ajaxObject.onreadystatechange = function() { … }; ajaxObject.open("GET", "remotePageURL.php?key1=value1", true); ajaxObject.send(); The first line creates an object of the special XMLHttpRequest type.
The second line registers a listener for the onReadyStateChange event to field the response.
The third line creates the HTTP request to send to the server.
The fourth line sends the request.
The HTTP request may or may not include parameters, encoded as key=value pairs. If there
are no parameters, don’t include the question mark. For two or more parameters, the separator
between parameters is the & character. The first argument is the HTTP type which can be GET
or POST. POST is used when the parameters need more complicate processing. Generally
speaking, POST indicates that you are sending data to enter in the database. The true on the end
indicates that the response will be handled asynchronously.
It is common, though not required, to define the response listener in the same statement
where it is registered. A function that is created inline in an expression is called an “anonymous
function” since it is not named. You can keep the listener short by calling another function to do
the actual work, after fielding the response and its data here. The form of the response is shown
below.
var ajaxObject = new XMLHttpRequest(); ajaxObject.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { var reponseString = this.responseText; // code to read response text and update page goes here } }; ajaxObject.open("GET", "remotePageURL.php?key1=value1", true); ajaxObject.send();
The listener must check both the state and the status. In practice, the listener will be called
several times with different status codes. State 3 indicates that a reply is in progress (and may
have partial data if it is a large reply). State 4, indicates that a reply is complete. The status is a
code sent by the server. 200 is success. Status 404 is for page not found. You may want an else
case to report status failures.
In the reply, the response is a document or string. Either way, it is a sequence of characters
available as this.responseText. In bigger applications, the intention was for the reply to be sent
as structured data encoded as XML. They did this because JavaScript already has a markup
parser, called DOM, that can be used to parse the data. Because XML is so verbose, requiring
element tags for everything, a recent alternative called JSON has evolved. In JSON, the
structure is encoded with nested {} characters, and not everything gets a tag.
For this application we keep it simple, and send either a single value, or a comma separated
list. In Java script, a comma separated list can be turned into an array of strings with the split
method on the original string: var responseArray = responseString.split(‘,’);
One final thing to know is that the responses are asynchronous events. They don’t happen
when the request is sent. More significantly, if you send a series of requests in short succession,
there is no guarantee that the replies will happen in the same order as they were sent, and often
don’t.
On the server side, an AJAX request is the same as any other. The difference is that for the
reply, you aren’t generating an HTML page. You pretty much just send the result of a query. If
there are multiple rows with multiple fields on each row, you may want to encode it in XML or
JSON. But for simple requests, sending a comma separated string can suffice.
The following code snippet just sends a single number, printed as a string. The count(*)
counts the rows, while AS count gives the field in the resulting table a name other than count(*). <?php ... $query = "SELECT count(*) AS count FROM $table;"; $result = $mydb->query($query); $row = $result->fetch_assoc(); $count = $row["count"]; print ($count); $mydb->close(); ?>
In the JavaScript listener function, the response is handled with this line:
var numberOfQuestions = parseInt(this.responseText);
Here is another example that involves a request parameter and a comma separated list reply.
var url = "getQuestion.php?row=" + questionNumber.toString(); xhttp.open("GET", url, true); xhttp.send();
In the JavaScript request, if questionNumber is 4, the HTTP will be “getQuestion.php?row=4”.
In the PHP code below, the parameter with a key name “row” in the HTTP GET request is
retrieved as $_GET['row'], and is used as a value to match against the table’s id field.
<?php ... $target = $_GET['row']; $query = "SELECT name, url FROM $table WHERE id = $target"; if ($result = $mydb->query($query)) { $row = $result->fetch_assoc(); $reply = $row["name"] . "," . $row["url"]; print ($reply); } else { print ("query error,"); } $mydb->close(); ?> Note that in PHP, string concatenation uses a . instead of a +. Here, in case the query failed, it
sends an alternative reply so that the receiver gets something that works and won’t crash. Here is
the corresponding response handler:
xhttp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { theQuestion = this.responseText.split(','); updateGame(); } }; And in the updateGame function:
correctButton.innerHTML = theQuestion[0]; document.getElementById("picture").setAttribute("src", theQuestion[1]);
In the previous example, the comma separated list represented several fields in the same row.
In this next example from PHP, the comma separated list represents one field from several rows.
The query randomly selects the first 4 records in random order excluding any where the id field
matches the row parameter. $target = $_GET['row']; $query = "SELECT name FROM $table WHERE id != $target ORDER BY rand() LIMIT 4;"; if ($result = $mydb->query($query)) { $row = $result->fetch_assoc(); $names = $row["name"]; while ($row = $result->fetch_assoc()) $names .= "," . $row["name"]; print ($names); } $mydb->close();
One final note: my FlashCardAJAX game sends out two AJAX requests for the update. The
first requests 4 answers other than the answer to the next question, chosen at random. The second
requests the answer and url of the next question. It writes the random answers to all four buttons,
and then overwrites one of the buttons, chosen at random, with the correct answer. That makes
the code simpler than having do decide which buttons get the random answers – just do them all.
My solution depends on the random buttons being written first, and the correct one being
written afterwards. But I cannot guarantee the order in which the AJAX responses will happen
(and they do often happen in different orders). So I count the responses that come in, and do the
update when there are two, regardless of the order in which they happened. To make that happen,
both response handlers call the same update function. The decision is made there. function updateGame() { replies++; // count replies // we need both replies (in any order) before we can update if (replies >= 2) { // populate buttons with wrong answers for (i = 0; i < 4; i++) { var btn = document.getElementById("btn" + i); btn.innerHTML = otherAnswers[i]; btn.setAttribute("class", ""); // unset correct or wrong } // choose a button for the correct answer var buttonNum = Math.floor(Math.random() * 4); correctButton = document.getElementById("btn" + buttonNum.toString()); correctButton.innerHTML = theQuestion[0]; document.getElementById("picture").setAttribute("src", theQuestion[1]); replies = 0; // reset reply counter } }
Assignment 7 Info/FlashCard3.css
h1 { text-align: center; } img { height: 300px; } div { text-align: center; padding: 10px; } input { margin: 5px; } .correct { border-color: green; } .wrong { border-color: red; }
Assignment 7 Info/FlashCards.html
Flash Cards
0
1
2
3
Answer
0/0
Next
Assignment 7 Info/FlashCards_files/FlashCard3.css
Open Quick Links
Quick LinksPage Landmarks
Content Outline
Keyboard Shortcuts
Logout
Global Menu
Orquidea QuinonesActivity Updates
Top Frame Tabs
|
Not Found
Content
The specified resource was not found, or you do not have permission to access it.Friday, November 24, 2017 5:14:58 PM EST
Assignment 7 Info/getOtherAnswers.php
<?php $user = "flashuser"; $pword = "MyDogHasFleas"; $dbase = "FlashCards"; $table = "Cars"; $mydb = new mysqli('localhost', $user, $pword, $dbase); if ($mydb->connect_error) { die( "Failed to connect to MySQL: " . $mydb->connect_error); } $target = $_GET['row']; $query = "SELECT name FROM $table WHERE id != $target ORDER BY rand() LIMIT 4;"; if ($result = $mydb->query($query)) { $row = $result->fetch_assoc(); $names = $row["name"]; while ($row = $result->fetch_assoc()) $names .= "," . $row["name"]; print ($names); } else { print ("query error"); } $mydb->close(); ?>
Assignment 7 Info/getQuestion(1).php
<?php $user = "flashuser"; $pword = "MyDogHasFleas"; $dbase = "FlashCards"; $table = "Cars"; $mydb = new mysqli('localhost', $user, $pword, $dbase); if ($mydb->connect_error) { die( "Failed to connect to MySQL: " . $mydb->connect_error); } $target = $_GET['row']; $query = "SELECT name, url FROM $table WHERE id = $target"; if ($result = $mydb->query($query)) { $row = $result->fetch_assoc(); $reply = $row["name"] . "," . $row["url"]; print ($reply); } else { print ("query error,"); } $mydb->close(); ?>
Assignment 7 Info/getQuestionCount(1).php
<?php $user = "flashuser"; $pword = "MyDogHasFleas"; $dbase = "FlashCards"; $table = "Cars"; $mydb = new mysqli('localhost', $user, $pword, $dbase); if ($mydb->connect_error) { die( "Failed to connect to MySQL: " . $mydb->connect_error); } // $query = "SELECT name FROM $table ORDER BY rand() LIMIT 1;"; $query = "SELECT count(*) AS count FROM $table;"; $result = $mydb->query($query); $mydb->close(); $row = $result->fetch_assoc(); $count = $row["count"]; print ($count); ?>