Auto div refresh and easy user changability

Hi Guys,

1st post :).

But im stuck, i need to make a website that will be used as a traffic light system. So i will have a page with loads of divs starting red, that will change to green when clicked once, and back to red if clicked again, so on, so on. These changes will need to be viewable live on another computer, so changes need to save and will also need to automatically refresh without distrubing the rest of the page.

Many thanks,

Stephen

Ok i have it all working finally, ran into 1 lost problem with an invalid label error but fixed that with var students = eval( “(” + res + “)” ), id;

Many many thanks for your help, i hope to learn JS alot in the next few weeks and hopefully help people as much as you have me.

If “students” is undefined, then you’re not outputting the data properly in response to the GET ajax request (or the ajax request is not working at all).

This is what I mean about you being able to debug things yourself. Using firebug you can pinpoint exactly at what stage of the process things are going wrong.

All your PHP code needs to look like is this:

if ($_GET['update'] === '1') {
  $res = mysqli_query($link, 'SELECT * FROM register');
  $buff = '{';
  while ($row = mysqli_fetch_array($res)) {
    $buff .= "'" . $row['id'] . "': '";
    $buff .= $row['present'] === 1 ? "'p'" : "'a'";
    $buff .= ',';
  }
  echo rtrim($buff, ',') . '}';
}

Ok i think the above post was my mistake, however fixing that seems to create another error

students is undefined
[Break on this error] if (students[id]) {

I think its the way im handling the php code, i have the loop on the main index.php, and the php script in a seprate file which i put into the javascript (what you refer to as register.php)

Ok fixed that but now i have

missing ; before statement

[Break on this error] for (var i = 0, j = lis.length; i < j; i++) { 

which is just after


var students = eval(res),id;

which is causing the problem.

Any ideas?

Well, no, because then all you will be doing is reloading the same page every three seconds.

If it isn’t changing colour onclick, you’ll have to debug it yourself. Find out if the click is getting registered, and then if the ajax POST is working. This is best done with Firebug (use console.log for the onclick check and the Net panel to monitor the ajax POST).

Ok thats working perfect, just 1 last small problem, it now only loads every 30 seconds and doesnt automatically change colour when clicked, any way around this or should i just set the timer to 3seconds-ish???

And thank you so much for your help, again. :slight_smile:

What I would do is make a GET request to the script, including a query string. Like ‘register.php?update=1’. That will allow your PHP script to know that it needs to output only the present status of each student. Output it as an object literal:

{
  '12434': 'p',
  '34435': 'a',
  '87631': 'p'
}

Where the ‘XXXX’ code is the student ID and p and a represent present and absent.

The function called by setInterval simply loops through the list items and sets the class name.

So, with a little change to the ajax function, your script becomes:

var lis;
function registerClicks() {
lis = document.getElementsByTagName('li');
  for (var i = 0, j = lis.length; i < j; i++) {
    lis[i].onclick = function() {
      var li = this;
      ajax('register.php', 'id='+this.id, function(res) {
        if (res === '1') { // query was successful, so change the colour
          li.className = li.className === 'present' ? '' : 'present';
        }
      });
    }
  }
}

window.onload = registerClicks;
window.setInterval(function() {
  ajax('register.php?update=1', null, function(res) {
    var students = eval(res), id;
    for (var i = 0, j = lis.length; i < j; i++) {
      id = lis[i].id.split('_')[1];
      if (students[id]) {
        lis[i].className = students[id] === 'p' ?  'present' : '';
      }
    }
  }, 'GET');
}, 30000);

  function ajax(url, data, callback, method) {
    if (!method) method = 'POST';
    if (window.XMLHttpRequest) req = new XMLHttpRequest();
    else if (window.ActiveXObject) {
      var msxml = ['Msxml2.XMLHTTP', 'Msxml3.XMLHTTP', 'Microsoft.XMLHTTP'];
      for (var i = 0; i < 3; i++) {
        try {req = new ActiveXObject(msxml[i])}
        catch (e) {continue;}
        break;
      }
    }
    req.open(method, url, true);
    req.onreadystatechange = function(e) {
      if (callback && req.readyState === 4 && req.status === 200) callback(req.responseText);
    }
    req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    req.send(data);
  }

You might want to include a little check to ensure that the setInterval function is receiving the correct sort of string before doing the innerHTML. Look at the string methods.

Ok now it works perfectly, but it doesn’t refresh automatically every 30 seconds on another computer? How would i do this, so the changes will refresh on a different computer?

Oh right, ok i will test it on my server.

Again many thanks.

Have you got Firebug? Look at the console and you will see that when something is clicked, you get this error:

Access to restricted URI denied"  code: "1012"

This is because you’re testing it locally.

If you test it on an actual webserver then you shouldn’t get this problem. Give it a go and hopefully it’ll work.

Hmmm ok clicking the list items still doesn’t do anything.

I know im proberly missing something really basic, but here is my code.

Like i said im a total noob at AJAX/PHP but do have a few books coming so i won’t have this problem again.

<script>
onload = function(){
var lis = document.getElementsByTagName('li');
for (var i = 0, j = lis.length; i < j; i++) {
  lis[i].onclick = function() {
    var li = this;
    ajax('id='+this.id, function(res) {
      if (res == '1') { // query was successful, so change the colour
        li.className = li.className == 'present' ? '' : 'present';
      }
    });
  }
}
 
function ajax(data, callback) {
    var url = '1.php'; // change this to your PHP script path
    if (window.XMLHttpRequest) req = new XMLHttpRequest();
    else if (window.ActiveXObject) {
      var msxml = ['Msxml2.XMLHTTP', 'Msxml3.XMLHTTP', 'Microsoft.XMLHTTP'];
      for (var i = 0; i < 3; i++) {
        try {req = new ActiveXObject(msxml[i])}
        catch (e) {continue;}
        break;
      }
    }
    req.open('POST', url, true);
    req.onreadystatechange = function(e) {
      if (callback && req.readyState == 4 && req.status == 200) callback(req.responseText);
    }
    req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    req.send(data);
  }
}
</script>
<style type="text/css">
li{
list-style-type: none;
color: #F00;
margin: 10px auto 10px auto;
cursor:pointer;
}
.present {
color:green;	
}


</style>
</head>
<?php
$link = mysqli_connect('localhost','test3','test3','test2');
$res = mysqli_query($link, 'SELECT * FROM name');
echo "<ul>\
";
while ($row = mysqli_fetch_array($res)) {
  echo '<li id="student_' . $row['id'] . '"';
  if ($row['present'] == 1) echo ' class="present"';
  echo '>' . $row['name'] . "</li>\
";
} 
mysqli_close($link);
?>
<?php
$id = array_pop(explode('_', $_POST['id']));
if (is_numeric($id)) { // check it's a number
  $res = mysqli_query($link, "UPDATE register SET present= IF(present=1, 0, 1) WHERE id=$id");
  echo (int)(mysqli_affected_rows($link) > 0); // success with query
} 
?>

I did have the php script at the bottom of the page in a different document as there is a link to it in the JS.

Sorry if im totally off with this.

Ok i have hit another wall.

I can output the database contents fine but how exactly would i allow the user to change the value? Would i need to change the list to buttons instead?

That’s where the AJAX request comes in. First you need to attach event listeners to your list items. Then you need a simple Ajax function to handle the click and communicate with the server:

var lis = document.getElementsByTagName('li');
for (var i = 0, j = lis.length; i < j; i++) {
  lis[i].onclick = function() {
    var li = this;
    ajax('id='+this.id, function(res) {
      if (res === '1') { // query was successful, so change the colour
        li.className = li.className === 'present' ? '' : 'present';
      }
    });
  }
}

function ajax(data, callback) {
    var url = 'registration.php'; // change this to your PHP script path
    if (window.XMLHttpRequest) req = new XMLHttpRequest();
    else if (window.ActiveXObject) {
      var msxml = ['Msxml2.XMLHTTP', 'Msxml3.XMLHTTP', 'Microsoft.XMLHTTP'];
      for (var i = 0; i < 3; i++) {
        try {req = new ActiveXObject(msxml[i])}
        catch (e) {continue;}
        break;
      }
    }
    req.open('POST', url, true);
    req.onreadystatechange = function(e) {
      if (callback && req.readyState === 4 && req.status === 200) callback(req.responseText);
    }
    req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    req.send(data);
  }

In your CSS you might want to do something like this to indicate they can be clicked:

li {
  cursor:hand
}

Let’s say you have a MySQL table with the columns “id” “name” and “present”, where “id” is a unique identifier, “name” is the student’s name and “present” is 1 or 0.

You output this data with a simple PHP loop when the page is first loaded, and every 30 seconds:

$res = mysqli_query($link, 'SELECT * FROM register');
echo "<ul>\
";
while ($row = mysqli_fetch_array($res)) {
  echo '<li id="student_' . $row['id'] . '"';
  if ($row['present'] === 1) echo ' class="present"';
  echo '>' . $row['name'] . "</li>\
";
}

In the CSS you can style the list items red by default, and style .present green.

Now, let’s suppose someone clicks one of your list items. You catch the click and submit your XMLHttpRequest by POST, passing the id. It doesn’t matter whether the page currently shows the student as present or not, you’re just toggling between present and absent, and the database is what controls this. Your PHP script processes it:

$id = array_pop(explode('_', $_POST['id']));
if (is_numeric($id)) { // check it's a number
  $res = mysqli_query($link, "UPDATE register SET present= IF(present=1, 0, 1) WHERE id=$id");
  echo (int)(mysqli_affected_rows($link) > 0); // success with query
}

The echo will give either 0 (that means for some reason the table was not updated) or 1 (the table was updated) in which case you change the colour of the LI in question.

30 seconds later, of course, the entire page will be refreshed with the contents of the database.

Now you just need to write the javascript for it. It should be very easy with jQuery if you want to go via that route, or it’s simple enough without it too.

In my example I used is_numeric to sanitise the POSTed data, but depending on what you have, you might want to do something else. But you must check you’re getting the expected input.

Wow thank you, works beatifully.

I will let you know how i get on with the javascript, but many many thanks.

You need to decide what you want. Are you OK with each click refreshing the page? If so, it’s a simple case of using a normal HTML form, and converting your coloured DIVs to form controls of some kind - BUTTON perhaps. This requires no javascript at all.

If for some reason you don’t want the page to refresh (this is not a bad thing at all), then you need to use JavaScript and XMLHttpRequests.

In either case, you will need a very simple PHP script that accepts the input, processes it and stores the value in the database. This means you’ll need to create a database table for the values. Most web servers offer MySQL, which is what I know a bit about, but you need to find out what your server has.

I want to use XMLHttpRequests to refresh the data every few seconds.

Server has got mysql.

I’m just not sure what php code i need to start.

Ok im getting somewhere, i have some php code which displays red or green depending if the database value is 1 or 0.

Now how can i make it so clicking on these will change there value in the database?

Okay, so any idea where to start?

All i need is a little helping hand to get started then i will be on my way, hopefully, i am still a noob when it comes to AJAX/DBs.

Thanks for your help so far.