I have a PHP website that allows users to search a library catalog and then select/add books to a shopping cart. This all works well but we would like to implement AJAX into the search results table so that instead of clicking a link which runs another php script to add the selected record to their cart, it does this inline within the same page. This will remove the search results page refreshing when they "select" a record and it pops back to the top of the page (annoying if you were at the bottom of the page).
I've found a similar example of implementing AJAX with a link - this is my first time with AJAX - but I'm stuck as nothing happens when the user clicks the link.
Here's my script:
function selectRecord() {
// Allocate an XMLHttpRequest object
if (window.XMLHttpRequest) {
// IE7+, Firefox, Chrome, Opera, Safari
var xmlhttp=new XMLHttpRequest();
} else {
// IE6, IE5
var xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
// Set up the readyState change event handler
xmlhttp.onreadystatechange = function() {
if ((this.readyState == 4) && (this.status == 200)) {
document.getElementById("selectRecord").innerHTML=xmlhttp.responseText;
}
}
// Open an asynchronous POST connection and send request
xmlhttp.open("POST", "selectRecord.php", true);
return false; // Do not follow hyperlink
}
and here's the table cell with the link:
<td class="hidden-narrow" id="selectRecord">
<?php
if (in_array($bookID, $_SESSION['selectedBooks'])) {
echo "Selected";
} else {
echo 'Select';
}
?>
</td>
In case it's not clear the result I'm after is a link ("Select") in the table cell - when the user clicks this link it then performs the selectRecord.php script which will echo "Selected" or an error message if there was an error. At present nothing happens when the user clicks the Select link.
I also need to work out how to pass the $bookID PHP variable to the AJAX script so the selectRecord.php knows which Book ID to add to the cart.
You can add a parameter to your selectRecord() call like this:
echo 'Select';
Then your selectRecord function should look like this:
function selectRecord(id) {
// Allocate an XMLHttpRequest object
if (window.XMLHttpRequest) {
// IE7+, Firefox, Chrome, Opera, Safari
var xmlhttp=new XMLHttpRequest();
} else {
// IE6, IE5
var xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
// Set up the readyState change event handler
xmlhttp.onreadystatechange = function() {
if ((this.readyState == 4) && (this.status == 200)) {
document.getElementById("selectRecord").innerHTML="Selected";
}
}
// Open an asynchronous POST connection and send request
xmlhttp.open("POST", "selectRecord.php", true);
xmlhttp.send("id="+id);
return false; // Do not follow hyperlink
}
This works well as long there is only one element in your document with id selectRecord. You can always modify the id of any link by simply adding the BookId number as a postfix.
Please note your code wasn't working because it was missing the .open call on the xmlHttpRequest object which actually perform the request.
Related
Code below is run in the onLoad event of the page. I first would like to populate a drop down menu with getCompany() and then fill in data from the server into text boxes and choose the selected option.
Both functions work, in fact when I reload the page with debugger running and step into everything both do what they are supposed to.
When I just open the page or reload with out debugger the text boxes are filled but the options disappear from the dropdown, why is that?
<script>
var result;
function init(){
var name = window.name;
name = name.split(",");
getCompany();
setTimeout(200);
if (name[0] = "update"){
id = name[1];
getTenant(id);
//get the info for the line that called the edit function
//fill fields with information from server
}
}
function getCompany() {
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 x() {
if (xmlhttp.readyState==4 && xmlhttp.status==200) {
document.getElementById("company").innerHTML = xmlhttp.responseText;
}
}
xmlhttp.open("GET","getCompany.php",true);
xmlhttp.send();
}
function getTenant(id){
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 y() {
if (xmlhttp.readyState==4 && xmlhttp.status==200) {
result = xmlhttp.responseText;
result = result.split(",");
//fill the form with old information
document.getElementById("fname").value = result[0];
document.getElementById("lname").value = result[1];
document.getElementById("company").selectedIndex = result[2];
document.getElementById("phone").value = result[3];
document.getElementById("email").value = result[4];
document.getElementById("crm").value = result[5];
}
}
xmlhttp.open("GET","getTenant.php?p=" + id,true);
xmlhttp.send();
}
</script>
I assume the input fields you are filling in data in the second request belong to the data fetched from the first request. I also assume you are using the setTimeout() to delay the 2nd request...
Javascripts are single threaded. To provide asynchronous behavior js uses callback mechanism. After sending a request to the server, js doesn't wait until the response comes. JS keeps executing the rest of code until the results from the server comes. When the response comes from the server the code in the callback function xmlhttp.onreadystatechange is executed. Because of that, both requests may happen at almost the same time and consequently the response for the 2nd request may come before the first response which leads the behavior you see as an error.
When you debug, you execute line by line. Therefore there is enough time to get the response for the first request before getting the response for the second request.
As a solution you can move the code for the second request inside the xmlhttp.onreadystatechange callback in the code for the first request. Then as the callback is always executed after the results are fetched, the second request is sent after the response for the first one comes.
You may google about asynchronous javascript and learn in details...
<script>
var result;
function init(){
var name = window.name;
name = name.split(",");
getCompany(name);
}
function getCompany(name) {
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 x() {
if (xmlhttp.readyState==4 && xmlhttp.status==200) {
document.getElementById("company").innerHTML = xmlhttp.responseText;
if (name[0] == "update"){
id = name[1];
getTenant(id);
//get the info for the line that called the edit function
//fill fields with information from server
}
}
}
xmlhttp.open("GET","getCompany.php",true);
xmlhttp.send();
}
function getTenant(id){
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 y() {
if (xmlhttp.readyState==4 && xmlhttp.status==200) {
result = xmlhttp.responseText;
result = result.split(",");
//fill the form with old information
document.getElementById("fname").value = result[0];
document.getElementById("lname").value = result[1];
document.getElementById("company").selectedIndex = result[2];
document.getElementById("phone").value = result[3];
document.getElementById("email").value = result[4];
document.getElementById("crm").value = result[5];
}
}
xmlhttp.open("GET","getTenant.php?p=" + id,true);
xmlhttp.send();
}
</script>
It is happening because of a race condition between the two XHR calls made from getCompany and getTenant methods. Even though you are making the getTenant call 200ms after making the first XHR call there is no guarantee that the getComapny XHR call will finish first. When that happens the follwoing line of code
document.getElementById("company").innerHTML = xmlhttp.responseText;
removes all the options from the menu and also resets the selected index. To circumvent this issue do not make getTenant(id); call from init method. Instead make it from the success handler of the getCompany XHR call.
xmlhttp.onreadystatechange=function x() {
if (xmlhttp.readyState==4 && xmlhttp.status==200) {
document.getElementById("company").innerHTML = xmlhttp.responseText;
**getTenant(id);**
}
}
Make two different object name instead of one (xmlhttp). Like
in 'getCompany()' function object name is 'xmlhttp'
in 'getTenant()' function changed object name to 'xmlTalenthttp' (or any other name which you wish)
I have menu that is included and many other modules (leftside, rightside, content ect.).
I want to load some PHP page to specific place (content) on link click.
Use Ajax on button click and then fill the div in with the php from the ajax response.
function dispRecords()
{
if (window.XMLHttpRequest)
{
// Create the object for browsers
xmlhttp=new XMLHttpRequest();
}
else
{
// Create the object for browser versions prior to IE 7
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange=function()
{
// if server is ready with the response
if (xmlhttp.readyState==4)
{
// if everything is Ok on browser
if(xmlhttp.status==200)
{
//Update the div with the response
document.getElementById("YOURDIV").innerHTML=xmlhttp.responseText;
}
}
}
//send the selected option id to the php page
xmlhttp.open("GET","YOURPHP.php",true);
xmlhttp.send();
}
I'm working on my first AJAX script with some PHP pages - it's my first time with AJAX and I've finally got the script to work. I'm also a bit of a Javascript newbie too.
I have a PHP website that allows users to search a library catalog and then select/add books to a shopping cart. We've now changed the "Select" link to load via AJAX so the search results page doesn't refresh.
To finish the AJAX changes I now need to pass a unique ID for each of the table cells that has the AJAX link to the script. I'm using PHP to generate a unique ID for each table cell by using the $bookID variable as follows:
<td class="hidden-narrow" id="<?php echo 'selectRecord'.$bookID; ?>">
<?php
if (in_array($bookID, $_SESSION['selectedBooks'])) {
echo "Selected";
} else {
echo 'Select';
}
?>
</td>
I now need to update my script to work with the unique ID's - to get it working I hardcoded it to an ID named "selectRecord" using an example script that I found. Here's my script:
function selectRecord(id) {
// Allocate an XMLHttpRequest object
if (window.XMLHttpRequest) {
// IE7+, Firefox, Chrome, Opera, Safari
var xmlhttp=new XMLHttpRequest();
} else {
// IE6, IE5
var xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
// Set up the readyState change event handler
xmlhttp.onreadystatechange = function() {
if ((this.readyState == 4) && (this.status == 200)) {
document.getElementById("selectRecord").innerHTML="Selected";
}
}
// Open an asynchronous POST connection and send request
xmlhttp.open("POST", "selectRecord.php", true);
xmlhttp.send("id="+id);
return false; // Do not follow hyperlink
I gather I need to change this line:
document.getElementById("selectRecord").innerHTML="Selected";
but not sure of the syntax to handle unique ID's for each table row cell.
You can pass the id to the getElementById function:
document.getElementById("selectRecord" + id).innerHTML="Selected";
Have you tried
document.getElementById(id).innerHTML="Selected";
? id should get enclosed by the readystate function and keep its value.
I can't seem to figure out how to get this function working. The crucial thing to see is that the tStat = xmlhttp2.responseText seems to be delayed. I have it test this out with the .innerHTML +=" withintest "+Stat. It prints out "withintest withintest withintest 0". So it does a few iterations until it has the value?? 0 is the value of Stat that I want, and the checktStatus.php gets it from a database.
But since this function returns a value, and I have it being called from another function into a variable for that value, it can't be delayed DB read before it returns. But I can't figure out how to accomplish this! Help?
EDIT: took out some commented code, but the problem still remains. It returns an "undefined" value before it can get a real one.
function gettStatus()
{
var tStat;
if (window.XMLHttpRequest)
{ // code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp2=new XMLHttpRequest();
}
else
{ // code for IE6, IE5
xmlhttp2=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp2.onreadystatechange=function()
{
if (xmlhttp2.readyState==4 && xmlhttp2.status==200)
{
tStat=xmlhttp2.responseText;
document.getElementById("txtHint").innerHTML+=" withintest "+tStat;
return tStat;
}
}
xmlhttp2.open("GET","checktStatus.php?tID=1",true);
xmlhttp2.send();
}
How onreadystatechange works
onreadystatechange event handler - as the name of the event suggests - is called when the readyState is changed. So you must check for the state and status (as you did in the part you commented).
See more details here: Mozilla Developer Network: AJAX - Getting Started
List of readyState values
From the page referenced above (link to the section):
The full list of the readyState values is as follows:
0 (uninitialized)
1 (loading)
2 (loaded)
3 (interactive)
4 (complete)
Asynchronous nature of AJAX
You should also be aware of the fact, that usually AJAX works asynchronously, so it would be easier for you to just pass callbacks that will be executed once the response is received, like that:
function gettStatus(callback){
// do something here...
callback(result); // ...and execute callback passing the result
}
Solution
Thus you should edit your code to look similarly to this (with readyState / status conditions uncommented):
function gettStatus(callback)
{
var tStat;
if (window.XMLHttpRequest)
{ // code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp2=new XMLHttpRequest();
}
else
{ // code for IE6, IE5
xmlhttp2=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp2.onreadystatechange=function()
{
if (xmlhttp2.readyState==4 && xmlhttp2.status==200)
{
tStat=xmlhttp2.responseText;
document.getElementById("txtHint").innerHTML+=" withintest "+tStat;
callback(tStat);
}
}
xmlhttp2.open("GET","checktStatus.php?tID=1",true);
xmlhttp2.send();
}
and then just use it like that:
gettStatus(function(tStat){
// tStat here is accessible, use it for further actions
});
instead of using it in the following manner:
var tStat = gettStatus();
// tStat would be available here, if gettStatus() would not involve
// asynchronous requests
The lines you have commented out serves the purpose of filtering readystates.
/*if (xmlhttp2.readyState==4 && xmlhttp2.status==200)
{*/
tStat=xmlhttp2.responseText;
document.getElementById("txtHint").innerHTML+=" withintest "+tStat;
return tStat;
//}
Should be
if (xmlhttp2.readyState==4 && xmlhttp2.status==200)
{
tStat=xmlhttp2.responseText;
document.getElementById("txtHint").innerHTML+=" withintest "+tStat;
return tStat;
}
I made this little script with tutorials on internet. php function calls this javascript as many times as there are buttons (foreach), right now i have three. $value is the div name of specific button (buttons are stored in php array).
Everything works fine... except when i click through all the buttons fast, the loading gif remains without javascript changing the button status. The response, witch it gets from another php is the new button state and session variable change. Session variable gets changed but the div dosent.
So, heres where i need help, how can i make it so, when i click buttons fast, the div gets changed too?
my code
function load_javascripts() {
foreach ($GLOBALS["VARIABLES"]["button_list"] as $key => $value) {
$scripts .= "
function run_alias_button_".$value."(str)
{
document.getElementById('aliasbutton_".$value."').innerHTML='<img src=ajax_loader.gif>';
if (str=='')
{
document.getElementById('aliasbutton_".$value."').innerHTML='';
return;
}
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)
{
document.getElementById('aliasbutton_".$value."').innerHTML=xmlhttp.responseText;
}
}
xmlhttp.open('GET','?leht=alias_logimine&alias=".$value."',true);
xmlhttp.send();
}
";
}
return $scripts;
}
Put var xmlhttp; at the very beginning of the function, making the variable explicitly local. Sometimes without that statement browsers may try to find a global variable with this name and readystate monitoring is shifted from one request to another.
Just a tip. Use the open function always before the onreadystatechange event. The way you using may works on firefox but IE doens't understand. LIke this:
xmlhttp.open('GET','?leht=alias_logimine&alias=".$value."',true);
xmlhttp.onreadystatechange=function()
{
if (xmlhttp.readyState==4 && xmlhttp.status==200)
{
document.getElementById('aliasbutton_".$value."').innerHTML=xmlhttp.responseText;
}
}
xmlhttp.send();