im trying to add a star rating system to a website. id like to do something elegant graphically instead of just using a drop down... the javascript framework im using is mootools and i found a great rating system here, but have no idea how to adjust it so that it updates the database with the new rating vote from the user and it calculates the new average rating.

the way i have the database set up for this is with the following fields:
id (the rating's unique id)
listingid (the listing that the rating corresponds to)
userid (the user that entered the rating)
rating (the rating itself)

i use php to communicate with a mysql database.
the file im going to write will check to see if there is already a rating entered in from the logged in user. if there is, it will update it. if not, it will insert it, then it will calculate the average of all votes for that listing (rounded up) and return the new rating to the page and set the stars to reflect that rating... ive tweaked the code a bit to represent a 5 star system instead of a 10 star system and also so it can be used multiple times on a page.

heres what ive been playing with:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Mootools Simple Star Rating</title>
<script type="text/javascript" src="mootools.js"></script>
<link rel="stylesheet" type="text/css" href="star-rating.css" />
</head>
<body>
<ul id="rate1" class="normal rating fourstar">
  <li title="1" class="rate one"><a href="javascript://" title="1 Star">1</a></li>
  <li title="2" class="rate two"><a href="javascript://" title="2 Stars">2</a></li>
  <li title="3" class="rate three"><a href="javascript://" title="3 Stars">3</a></li>
  <li title="4" class="rate four"><a href="javascript://" title="4 Stars">4</a></li>
  <li title="5" class="rate five"><a href="javascript://" title="5 Stars">5</a></li>
</ul>
<ul class="featured rating fivestar">
  <li title="1" class="rate one"><a href="javascript://" title="1 Star">1</a></li>
  <li title="2" class="rate two"><a href="javascript://" title="2 Stars">2</a></li>
  <li title="3" class="rate three"><a href="javascript://" title="3 Stars">3</a></li>
  <li title="4" class="rate four"><a href="javascript://" title="4 Stars">4</a></li>
  <li title="5" class="rate five"><a href="javascript://" title="5 Stars">5</a></li>
</ul>
<script>
$$('.rate').each(function(element,i){
	element.addEvent('mouseup', function(){
		var myStyles = ['nostar', 'onestar', 'twostar', 'threestar', 'fourstar', 'fivestar'];
		myStyles.each(function(myStyle){
			if(element.getParent().hasClass(myStyle)){
				element.getParent().removeClass(myStyle)
			}
		});		
		myStyles.each(function(myStyle, index){
			if(index == element.title){
				element.getParent().toggleClass(myStyle);
				alert(index);
			}
		});		
		
	});
});

</script>
</body>
</html>

and the css:

/* star rating code - use lists because its more semantic */
/* No javascript required */
/* all the stars are contained in one matrix to solve rollover problems with delay */
/* the background position is just shifted to reveal the correct image. */
/* the images are 16px by 16px and the background position will be shifted in negative 16px increments */
/*  key:  B=Blank : O=Orange : G = Green * /
/*..... The Matrix ....... */
/* colours ....Background position */
/* B B B B B - (0 0)*/
/* G B B B B - (0 -16px)*/
/* G G B B B - (0 -32px)*/
/* G G G B B - (0 -48px)*/
/* G G G G B - (0 -64px)*/
/* G G G G G - (0 -80px)*/
/* O B B B B - (0 -96px)*/
/* O O B B B - (0 -112px)*/
/* O O O B B - (0 -128px)*/
/* O O O O B - (0 -144px)*/
/* O O O O O - (0 -160px)*/


/* the default rating is placed as a background image in the ul */
/* use the background position according to the table above to display the required images*/
.rating{
	width:80px;
	height:16px;
	margin:0 0 20px 0;
	padding:0;
	list-style:none;
	clear:both;
	position:relative;
}
.normal{background: url(star-bluebk.gif) no-repeat 0 0;}
.featured{background: url(star-goldbk.gif) no-repeat 0 0;}
/* add these classes to the ul to effect the change to the correct number of stars */
.nostar {background-position:0 0}
.onestar {background-position:0 -16px}
.twostar {background-position:0 -32px}
.threestar {background-position:0 -48px}
.fourstar {background-position:0 -64px}
.fivestar {background-position:0 -80px}
ul.rating li {
	cursor: pointer;
 /*ie5 mac doesn't like it if the list is floated\*/
	float:left;
	/* end hide*/
	text-indent:-999em;
}
ul.rating li a {
	position:absolute;
	left:0;
	top:0;
	width:16px;
	height:16px;
	text-decoration:none;
	z-index: 200;
}
ul.rating li.one a {left:0}
ul.rating li.two a {left:16px;}
ul.rating li.three a {left:32px;}
ul.rating li.four a {left:48px;}
ul.rating li.five a {left:64px;}

ul.rating li a:hover {
	z-index:2;
	width:80px;
	height:16px;
	overflow:hidden;
	left:0;	
}
ul.normal li a:hover {	background: url(star-bluebk.gif) no-repeat 0 0}
ul.featured li a:hover {	background: url(star-goldbk.gif) no-repeat 0 0}
ul.rating li.one a:hover {background-position:0 -176px;}
ul.rating li.two a:hover {background-position:0 -192px;}
ul.rating li.three a:hover {background-position:0 -208px}
ul.rating li.four a:hover {background-position:0 -224px}
ul.rating li.five a:hover {background-position:0 -240px}

i havent even been able to get the system to average the current rating and the new rating... HELP!!!

a lil more info... i know how to structure an ajax server request, both traditionally and via mootools... its making this code dynamic that im having a problem with

ok, ive made some serious headway... it updates the database and i know it has the ability to update the rating correctly... for some reason its not and i cant quite figure out what im missing... here is the javascript:

// JavaScript Document
		var ratequest
		
	function rateIt(id, rating){
		ratequest=GetXmlHttpObject();
		addId = "add" + id;
		if (ratequest==null){
		  alert ("Your browser does not support AJAX!");
		  return;
		  }
		var url="ajax/rating.php";
		url=url+"?id=" + id;
		url=url+"&rating=" + rating;
		ratequest.onreadystatechange=stateChanged;
		ratequest.open("GET",url,true);
		ratequest.send(null);
		return newRate;
	}
		
	function stateChanged(){
		if (ratequest.readyState==4){
		  newRate = ratequest.responseText;
		  }
	}
		
	function GetXmlHttpObject(){
		if (window.XMLHttpRequest){
		  // code for IE7+, Firefox, Chrome, Opera, Safari
		  return new XMLHttpRequest();
		  }
		if (window.ActiveXObject){
		  // code for IE6, IE5
		  return new ActiveXObject("Microsoft.XMLHTTP");
		  }
		return null;
	}
$$('.rate').each(function(element,i){
	element.addEvent('mouseup', function(){
		var newPosition = rateIt((element.getParent().get('id').replace('rate','')), parseInt(element.title));
		var myStyles = ['nostar', 'onestar', 'twostar', 'threestar', 'fourstar', 'fivestar'];
		myStyles.each(function(myStyle){
			if(element.getParent().hasClass(myStyle)){
				element.getParent().removeClass(myStyle);
			}
		});	
		myStyles.each(function(myStyle, index){
			if(index == newPosition){
				element.getParent().toggleClass(myStyle);
			}
		});		
	});
});

and the php file is connects to:

<?php
	session_start();
	include("../lib/login.php");
	login();
	$username = $_SESSION['user'];
	$listingid = $_GET['id'];
	$rating = (int)$_GET['rating'];
	
	$query = "SELECT COUNT(rating) FROM ratings WHERE listingid = '$listingid' AND userid = '$username'";
	$result = mysql_query($query)  or die(mysql_error());
	$row = mysql_fetch_array($result)  or die(mysql_error());
	
	$totrecords = $row[0];

	if($totrecords == 0){
		$query = "INSERT INTO ratings (listingid, userid, rating) VALUES ('$listingid', '$username', '$rating')";
		$result = mysql_query($query)  or die(mysql_error());
	}
	else{
		$query = "UPDATE ratings SET listingid = '$listingid', userid = '$username', rating = '$rating' WHERE listingid = '$listingid' AND userid = '$username'";
		$result = mysql_query($query)  or die(mysql_error());
	}
	
	$query = "SELECT rating FROM ratings WHERE listingid ='$listingid'";
	$result = mysql_query($query);
	//$row = mysql_fetch_array($result, MYSQL_ASSOC);
	$allratings = array();
	$i=0;
	while($row = mysql_fetch_array($result, MYSQL_ASSOC)){
		$allratings[$i] = $row['rating'];
		$i++;
	}
	$num_of_ratings = count($allratings);
	$rating = ceil(array_sum($allratings) / $num_of_ratings);
	
	echo $rating;
	
?>

what happens is the first time its clicked, it will display the image for no starts... then the second time its clicked, it will show what it should have shown the first time... and so on and so forth... anyone see something im not?

First of all, you're using MooTools, right? Then why on earth are you using GetXmlHttpRequest? MooTools has this builtin as var req = new Request(options); .

Second, the reason your code isn't working is probably because when your code gets to return newRate; (line 17) there is no newRate variable defined. In order for you to understand this a little better I'll explain to you exactly (at least I'll try :-P) what happens when you call the rateIt-function.

  1. A request object is made
  2. You define a variable named addId and set it to hold the value of "add" + the id submitted.
  3. If request is null you alert the user of too old browser.
  4. You set the url, add the id and the rating.
  5. You set the onreadystatechange-function
  6. You define the url and the method for the request (and some more things happen behind the curtain)
  7. You send the request.
  8. You return a value that doesn't exist because the request isn't finished yet.

And there's the important part to know about AJAX, just because you run send() doesn't mean that the request is finnished. That's why it's called async.

In order to get the effect you want here's what I'dd do (note, I don't have the ability to test this code because the machine I'm on doesn't have php installed, but I'm pretty sure it should work. If it doesn't, just respond and I'll take a look at it tomorrow).

$$('.rate').addEvent('mouseup', function() {
    var id = element.getParent().get('id').replace(/rate/, '')
    var rating = element.get('title').toInt();
    var req = new Request({
        url: 'ajax/rating.php',
        data: {
            id: id,
            rating: rating
        }
    });
    req.addEvent('success', ratingSuccess.bind(this));
});

Two things is worth noting here (actually more than two, but I'll mention two, but first, note this isn't the entire code, the function ratingSucces I've yet to write).
The first one is that I use addEvent directly after $$ (instead of inside a each-loop). This is taken care of by mootools by default, so don't go doing what you don't have to, let mootools handle that. The second thing is that you wrote ...get('id').replace('rate', ''); . The replace-function in JS uses regular-expressions so you should actually use ...get('id').replace([b]/[/b]rate[b]/[/b], '') instead.

Then the function ratingSuccess:

function ratingSuccess(responseText) {
    doRating(this, responseText.trim().toInt());
}
function doRating(element, newPosition)
{
    var myStyles = ['nostar', 'onestar', 'twostar', 'threestar', 'fourstar', 'fivestar'];
    var parent = element.getParent();
    myStyles.each(function(myStyle) {
        parent.removeClass(myStyle); //This isn't dangerous, if parent doesn't have class, nothing happens.
    });
    parent.addClass(myStyles[newPosition]);
}

As you see you end up with a lot less code all thanks to mootools. But using this code in it self is rather useless, so I strongly recommend that you learn to understand what it actually does. If you are planing on doing any web-development, understanding javascript and knowing how to use one of the big frameworks is ALWAYS a huge advantage.

thanks for your help, but it didnt seem to work... on click a new record was entered into the database, but the listing id and rating were entered as 0.
im unfortunately using GetXMLHttpRequest because im unsure how to onSuccess to retireve the text thats been generated by the php file. ive scoured the net and havent been able to find a real comprehensive tutorial on the matter. thanks for all your help... if you have any other ideas or guidance im all ears :)

thanks for your help, but it didnt seem to work... on click a new record was entered into the database, but the listing id and rating were entered as 0.
im unfortunately using GetXMLHttpRequest because im unsure how to onSuccess to retireve the text thats been generated by the php file. ive scoured the net and havent been able to find a real comprehensive tutorial on the matter. thanks for all your help... if you have any other ideas or guidance im all ears :)

SOLVED!

$$('.rate').each(function(element,i){
	element.addEvent('mouseup', function(){
		var myStyles = ['nostar', 'onestar', 'twostar', 'threestar', 'fourstar', 'fivestar'];
		var newPosition;
		myStyles.each(function(myStyle){
			if(element.getParent().hasClass(myStyle)){
				element.getParent().removeClass(myStyle);
			}
		});	
		var rateIt = new Request({
			url: 'ajax/rating.php',
			link: 'chain',
			method: 'get',
			data: {
				'id': element.getParent().get('id').replace('rate',''),
				'rating': parseInt(element.title),
				ajax: 1
			},
			onSuccess: function(response){
				myStyles.each(function(myStyle, index){
					if(index == response){
						element.getParent().toggleClass(myStyle);
					}
				});		
			}
		}).send();
	});
});

Sorry delayd response. Try adding method: "get" to my code too...

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.