03.03.2010 Updated code for the file “RatingForSharePoint.js” due to a bug occurring when the ID column was hidden, but not placed far right in the view.
I got this request after enabling “rating” of the blog comments:
…How can we implement the thumbs up or down functionality you have on your site to a sharepoint list?
Tony
I have made a solution that uses a separate list to hold all the ratings. There will be written one line in this list for each rating of an item (list item or document).
This solution allows for one rating per item, per user, per session (browser session – new window = new session). There are an option to restrict or allow multiple ratings per user in different sessions.
The rating of the items are available in a list view (plain type, no boxed style), and in grouped plain list views. It is also available in DispForm.
To enable rating of items, you add a calculated column to your list with this code:
=""
Yes, only “equals” and two double quotes. You then assures that this new calculated column and the ID column is visible in the list view (there is an option in the script to hide the ID column).
Then you create a custom list for the ratings, with the name: RatingForSharePoint, and add these fields:
- Plus The type of information in this column is: Number
- Minus The type of information in this column is: Number
- ListUrl The type of information in this column is: Single line of text
- ItemID The type of information in this column is: Single line of text
- ListGuid The type of information in this column is: Single line of text
- ListName The type of information in this column is: Single line of text
As always we start like this:
Create a document library to hold your scripts (or a folder on the root created in SharePoint Designer). In this example i have made a document library with a relative URL of “/test/English/Javascript” (a sub site named “test” with a sub site named “English” with a document library named “Javascript”):
The jQuery-library is found here. The pictures and the sourcecode refers to jquery-1.3.2.min. If you download another version, be sure to update the script reference in the sourcecode.
The scripts “interaction.js” and “stringBuffer.js” is created by Erucy and published on CodePlex.
The file “sessvars.js” is found here.
The sourcecode for the file “RatingForSharePoint.js” is found below.
Add a CEWP below the listView and add this code:
<script type="text/javascript" src="/test/English/Javascript/jquery-1.3.2.min.js"></script> <script type="text/javascript" src="/test/English/Javascript/interaction.js"></script> <script type="text/javascript" src="/test/English/Javascript/stringBuffer.js"></script> <script type="text/javascript" src="/test/English/Javascript/sessvars.js"></script> <script type="text/javascript" src="/test/English/Javascript/RatingForSharePoint.js"></script> <script type="text/javascript"> ratingForSharePoint('Rating',true,true); </script>
Parameters explained:
- FieldInternalName: FieldInternalName of the calculated column created above.
- hideIdCol: true to hide the ID column, false to let it be visible.
- rateOnlyOnce: true to allow only one rating per user, false to allow multiple ratings (in different sessions)
Add a CEWP below your DispForm, and add this code:
<script type="text/javascript" src="/test/English/Javascript/jquery-1.3.2.min.js"></script> <script type="text/javascript" src="/test/English/Javascript/interaction.js"></script> <script type="text/javascript" src="/test/English/Javascript/stringBuffer.js"></script> <script type="text/javascript" src="/test/English/Javascript/sessvars.js"></script> <script type="text/javascript" src="/test/English/Javascript/RatingForSharePoint.js"></script> <script type="text/javascript"> // You must edit this GUID to match THIS list's GUID - this is NOT the GUID of the "RatingForSharePoint" thisListsGuid = '{77C35652-1D4D-4B09-B58C-D941068D251E}'; ratingForSharePointDispForm('Rating',true); </script>
Parameters explained:
- thisListsGuid: This list’s GUID – this is NOT the GUID of the “RatingForSharePoint”
- FieldInternalName: FieldInternalName of the calculated column created above
- rateOnlyOnce: true to allow only one rating per user, false to allow multiple ratings (in different sessions)
The sourcecode for the file “RatingForSharePoint.js” looks like this:
/* Rating for SharePoint lists * --------------------------------------------- * Created by Alexander Bautz * alexander.bautz@gmail.com * https://spjsblog.com * v1.1 * LastMod: 03.03.2010 * --------------------------------------------- * Must include reference to: * jQuery - http://jquery.com * interaction.js - http://spjslib.codeplex.com * stringBuffer.js - http://spjslib.codeplex.com * sessvars.js - http://www.thomasfrank.se * RatingForSharePoint.js - This file * --------------------------------------------- */ // If anonymous the SharePoint variable "_spUserId" does not exist if(typeof(_spUserId)=='undefined'){ _spUserId = ''; } /************************************************************ *********************** ListView Code *********************** ************************************************************/ function ratingForSharePoint(FieldInternalName,hideIdCol,rateOnlyOnce){ if(typeof(ctx)=='object'){ var thisListsGuid=ctx.listName; } if(typeof(currentRatingObj)=='undefined'){ currentRatingObj = getRating(); } if(typeof(FieldInternalName)!='undefined'){ intName = FieldInternalName; hideId = hideIdCol; rateOnce = rateOnlyOnce; // Fiend index of "Rating" column $(".ms-viewheadertr th").each(function(){ if($(this).find('table:first').attr('name')==intName){ colIndex = $(this).attr('cellIndex'); displayName = $(this).find('table:first').attr('displayname'); // Remove filtering possibility $(this).removeClass('ms-vh2').addClass('ms-vh2-nograd').html(displayName); } }); // Find index of ID column $(".ms-viewheadertr th").each(function(){ if($(this).find('table:first').attr('name')=='ID'){ IdColIndex = $(this).attr('cellIndex'); // Hide ID column if(hideId){ $(this).remove(); } } }); if(typeof(colIndex)=='undefined' || typeof(IdColIndex)=='undefined'){ alert("Both the column with FieldInternalName ""+FieldInternalName+"" and the ID-column must be in the view."); return false; } } $("table.ms-listviewtable tbody:not([id^='aggr']) >tr[beenthere!=1]:has(td.ms-vb2)").each(function(){ $(this).attr('beenthere',1); var itemAlreadyRate = false; var thisTd = $(this).find("td[cellIndex=" + colIndex + "]"); var thisIdColumn = $(this).find("td[cellIndex=" + IdColIndex + "]"); var thisId = thisIdColumn.text() if(hideId){ thisIdColumn.remove(); } var rPos = 0; var rNeg = 0; if(currentRatingObj[thisId]!=undefined){ // Previously rated by current user if(rateOnce && currentRatingObj[thisId]['ratedByMe']==true){ itemAlreadyRate = true; } rPos = currentRatingObj[thisId]['Plus']; rNeg = currentRatingObj[thisId]['Minus']; } // Rated in this session if(sessvars[thisListsGuid+thisId]==1 && currentRatingObj!=false){ itemAlreadyRate = true; } var str = "<div style='color:gray'>"; str += "<span><img "; if(!itemAlreadyRate){ str += "onclick='javascript:rateMe("up","+thisId+")' title='Rate Up' "; str += "style='vertical-align:middle;cursor:pointer' "; }else{ str += "title='You have already rated this item "+currentRatingObj[thisId]['ratedByMeVal']+"' "; str += "style='vertical-align:middle;cursor:no-drop' "; } str += "src='/_layouts/images/arrupi.gif'></span>"; str += "<span style='font-weight:bold;' id='rateUp_"+thisId+"'>"+rPos+"</span>"; str += "<span><img "; if(!itemAlreadyRate){ str += "onclick='javascript:rateMe("down","+thisId+")' title='Rate Down' "; str += "style='vertical-align:middle;cursor:pointer' "; }else{ str += "title='You have already rated this item "+currentRatingObj[thisId]['ratedByMeVal']+"' "; str += "style='vertical-align:middle;cursor:no-drop' "; } str += "src='/_layouts/images/arrdowni.gif'></span>"; str += "<span style='font-weight:bold;' id='rateDown_"+thisId+"'>"+rNeg+"</span>"; str += "</div>"; // Write HTML thisTd.html(str); }); } function getRating(){ // Path to webservices wsBaseUrl = L_Menu_BaseUrl + '/_vti_bin/'; var query = "<Where><Eq><FieldRef Name='ListGuid' /><Value Type='Text'>"+ctx.listName+"</Value></Eq></Where>"; var rating = queryItems('RatingForSharePoint',query,['Plus','Minus','ItemID','Author']); if(rating.count==-1){ alert("An error occured in the query:n"+query); }else if(rating.count>0){ ratingObj = {}; $.each(rating.items,function(i,item){ var authorIdRaw = item['Author']; var authorId = authorIdRaw.substring(0,authorIdRaw.indexOf(';#')); var plusVal = item['Plus']; var minusVal = item['Minus']; if(plusVal==null)plusVal=0; if(minusVal==null)minusVal=0; if(ratingObj[item['ItemID']]==undefined){ ratingObj[item['ItemID']]={'Plus':parseInt(plusVal),'Minus':parseInt(minusVal)}; }else{ ratingObj[item['ItemID']]['Plus']+=parseInt(plusVal); ratingObj[item['ItemID']]['Minus']+=parseInt(minusVal); } // Rated by current user if(_spUserId==authorId){ ratingObj[item['ItemID']]['ratedByMe']=true; if(item['Plus']>0){ ratingObj[item['ItemID']]['ratedByMeVal']="+1"; }else{ ratingObj[item['ItemID']]['ratedByMeVal']="-1"; } } }); return ratingObj; }else{ return false; } } /************************************************************ *********************** DispForm Code *********************** ************************************************************/ function ratingForSharePointDispForm(FieldInternalName,rateOnlyOnce){ var queryStr = getQueryParameters(); if(typeof(fields)=='undefined')fields = init_fields(); var itemAlreadyRate = false; var thisId = queryStr.ID; var currentRatingObj = getRatingDispForm(thisId); if(currentRatingObj[thisId]==undefined){ rPos = 0; rNeg = 0; }else{ // Previously rated by current user if(rateOnlyOnce && currentRatingObj[thisId]['ratedByMe']==true){ itemAlreadyRate = true; } rPos = currentRatingObj[thisId]['Plus']; rNeg = currentRatingObj[thisId]['Minus']; } // Rated in this session if(sessvars[thisListsGuid+thisId]==1 && currentRatingObj!=false){ itemAlreadyRate = true; } var thisTd = $(fields[FieldInternalName]).find('.ms-formbody'); var str = "<div style='color:gray'>"; str += "<img "; if(!itemAlreadyRate){ str += "onclick='javascript:rateMe("up","+thisId+")' title='Rate Up' "; str += "style='vertical-align:middle;cursor:pointer' "; }else{ str += "title='You have already rated this item "+currentRatingObj[thisId]['ratedByMeVal']+"' "; str += "style='vertical-align:middle;cursor:no-drop' "; } str += "src='/_layouts/images/arrupi.gif'>"; str += "<span style='font-weight:bold;' id='rateUp_"+thisId+"'>"+rPos+"</span>"; str += "<img "; if(!itemAlreadyRate){ str += "onclick='javascript:rateMe("down","+thisId+")' title='Rate Down' "; str += "style='vertical-align:middle;cursor:pointer' "; }else{ str += "title='You have already rated this item "+currentRatingObj[thisId]['ratedByMeVal']+"' "; str += "style='vertical-align:middle;cursor:no-drop' "; } str += "src='/_layouts/images/arrdowni.gif'>"; str += "<span style='font-weight:bold;' id='rateDown_"+thisId+"'>"+rNeg+"</span>"; str += "</div>"; // Write HTML thisTd.html(str); } function getRatingDispForm(itemID){ // Path to webservices wsBaseUrl = L_Menu_BaseUrl + '/_vti_bin/'; var query = "<Where><And><Eq><FieldRef Name='ListGuid' /><Value Type='Text'>"+thisListsGuid+"</Value></Eq>"+ "<Eq><FieldRef Name='ItemID' /><Value Type='Text'>"+itemID+"</Value></Eq></And></Where>"; var rating = queryItems('RatingForSharePoint',query,['Plus','Minus','ItemID','Author']); if(rating.count==-1){ alert("An error occured in the query:n"+query); }else if(rating.count>0){ ratingObj = {}; $.each(rating.items,function(i,item){ var authorIdRaw = item['Author']; var authorId = authorIdRaw.substring(0,authorIdRaw.indexOf(';#')); var plusVal = item['Plus']; var minusVal = item['Minus']; if(plusVal==null)plusVal=0; if(minusVal==null)minusVal=0; if(ratingObj[item['ItemID']]==undefined){ ratingObj[item['ItemID']]={'Plus':parseInt(plusVal),'Minus':parseInt(minusVal)}; }else{ ratingObj[item['ItemID']]['Plus']+=parseInt(plusVal); ratingObj[item['ItemID']]['Minus']+=parseInt(minusVal); } // Rated by current user if(_spUserId==authorId){ ratingObj[item['ItemID']]['ratedByMe']=true; if(item['Plus']>0){ ratingObj[item['ItemID']]['ratedByMeVal']="+1"; }else{ ratingObj[item['ItemID']]['ratedByMeVal']="-1"; } } }); return ratingObj; }else{ return false; } } /************************************************************ *********************** Shared Code *********************** ************************************************************/ function rateMe(upORdown,id){ if(typeof(ctx)=='object'){ var listGuid=ctx.listName; var listTitle = $.trim($("td.ms-pagetitle").text()); }else{ var listGuid=thisListsGuid; var listTitle = $.trim($("td.ms-pagetitle a").text()); } // Only rate item if not already rated in this session if(sessvars[listGuid+id]==undefined){ plus=''; minus=''; if(upORdown=='up'){ sessvars[listGuid+id]=1; currVal = parseInt($("#rateUp_"+id).text()); $("#rateUp_"+id).text(currVal+1) .prev().css({'background-color':'#C5E3BF'}) .parent().find('img').attr({'onclick':'','title':'Rated +1'}); // Prepare for writing rating plus=1; }else{ sessvars[listGuid+id]=1; currVal = parseInt($("#rateDown_"+id).text()); $("#rateDown_"+id).text(currVal+1) //.parent().css({'background-color':'#FFE4E1'}) .prev().css({'background-color':'#FFE4E1'}) .parent().find('img').attr({'onclick':'','title':'Rated -1'}); // Prepare for writing rating minus=1; } // Get url var urlDir = location.pathname; // Path to webservices wsBaseUrl = L_Menu_BaseUrl + '/_vti_bin/'; // Add item var res = addItem('RatingForSharePoint', {'Title':"Rating for: " + listTitle, 'Plus':plus, 'Minus':minus, 'ListUrl':urlDir, 'ListGuid':listGuid, 'ListName':listTitle, 'ItemID':id}); } } // Attaches a call to the function to the "expand grouped elements function" for it to function in grouped listview's function ExpGroupRenderData(htmlToRender, groupName, isLoaded){ var tbody=document.getElementById("tbod"+groupName+"_"); var wrapDiv=document.createElement("DIV"); wrapDiv.innerHTML="<TABLE><TBODY id="tbod"+groupName+"_" isLoaded=""+isLoaded+"">"+htmlToRender+"</TBODY></TABLE>"; tbody.parentNode.replaceChild(wrapDiv.firstChild.firstChild,tbody); ratingForSharePoint(); } // Function to separate each url search string parameters function getQueryParameters(){ qObj = {}; var urlSearch = window.location.search; if(urlSearch.length>0){ var qpart = urlSearch.substring(1).split('&'); $.each(qpart,function(i,item){ var splitAgain = item.split('='); qObj[splitAgain[0]] = splitAgain[1]; }); } return qObj; } function init_fields(){ var res = {}; $("td.ms-formbody").each(function(){ if($(this).html().indexOf('FieldInternalName="')<0) return; var start = $(this).html().indexOf('FieldInternalName="')+19; var stopp = $(this).html().indexOf('FieldType="')-7; var nm = $(this).html().substring(start,stopp); res[nm] = this.parentNode; }); return res; }
Save as “RatingForSharePoint.js”, mind the file extension, and upload to the scriptlibrary as shown above.
Ask is something is unclear, and please report any bugs.
Regards
Alexander
Bloody great!!! Anyway to change images?
Tony
As for now, the images are set in the file “RatingForSharePoint.js”.
I have used “arrupi.gif” and “arrdowni.gif” from the “/_Layouts/images/” directory to assure that all users have them available.
Replace the images as you like, just search and replace in the file.
Alexander
Agree, great job. 2 questions, I know this can be converted to the star rating, instead of up/down, but is this an easy modification? Can this be added to pages in addition to a list item, track by URL in addition to itemID (maybe create an or get page ID)?
I have created a library where new bullitins are added. once they are approved they are visible for users. A user can then read the document. Once read there is a link to click to mark the item read. the link opens a popup win that goes to a newform. they agree they read and click OK, takes them to a thank you page. Recently they asked if user could rate the item. I am thinking i could use this script, add the user_ID and I can get the result with less work.
It is possible, but requires some additional work. I reckon i will be updating this solution in the following weeks, but cannot promise anything right now.
Alexander
I have tried to replicate this and I am failing. I know it is something simple. I installed as described. Works perfectly from the dispform. In the list view the calcultaed fields writes to the title field and nothing in the calc field, no tracking. if i set ID to visible, it lays out correctly, but still not tracking. I have checked and rechecked my src URLs.
Assure that you have used the CEWP-code for the ListView, and not the one for DispForm.
Alexander
I have checked and rechecked. If I hide the ID the ratings font size get 2x larger and it writes to the title field.
ratingForSharePoint(‘Rating’,true,true);
when I show the ID it looks normal, but still not working
ratingForSharePoint(‘Rating’,false,true);
in this image you can see the results I get:
http://home.comcast.net/~larry.h.pfaff/ratingError.png
Very strange – if you look at the code in line 51, i should give en alert if you do not have both the “Rating” and the “ID” column in the view.
Do you have any other code in this page that could interfere with this script?
Alexander
No other code on the page, the ID and Calc field is in view. No alert. I even created a new list and started from new. same issue. The DispForm works perfectly. I even copies the src links from there to make sure i had them correct. it is bizzare.
I cant figure it out. I scraped it all and created new lists in a new site collection. Same issue. the dispform works fine, but in the list view I get no error, and no results. I found if ID hide set to false, the layout is fine.it does not work, but the rate up/down is in the rating column. if UD col set to hide, it moves the rate up/down column to the left one column. let me know if there is any other info I can provide to help trouble shoot
k, I have made some changes. The order of my columns,
from ID, Title, Ratings
TO Attachments, ID, Title, Ratings
The script is functioning, but the column alignment is still off by one. My text in the Title column has been replaced with the rating Up/Down arrows and numbers.
Another modification. If I DO NOT hide the ID column, with the fields in the order above, it work perfectly. So this may just be a bug. That attachment column may be what is causing the bug.
Hi,
I have reproduced the bug and will post a fix soon. Until this is supplied, i think it should work if you put the ID column to the far right in your list view.
Alexander
that nailed it for a work around, thanks!
Code for the file “RatingForSharePoint.js” is updated to fix this bug.
Alexander
Is there away to make sure a person only votes once per item ever? not by session
Tony
By setting the argument “rateOnlyOnce” to true, a person can vote only once.
Alexander
per session or ever?
If they are logged in, it is for ever. Anonymous visitors are restricted only per session.
Alexander
Hi Alexander,
can you tell me how to use the “ExpGroupRenderData(htmlToRender, groupName, isLoaded)” Function?
I don’t know which parameters need to be delivered.
What is “htmlToRender” and “isLoaded”?
Is there a possibility to use it in a Newsletter View?
Thanks a lot!
Martin
This function is a generic SharePoint function to handle expanding of groups and sub groups in a grouped list view. The only reason it is included in the script is to insert the call to “our function – ratingForSharePoint()” at the bottom of the function.
It should work in a Newsletter view.
Alexander
Great Solution Alexander,
How do I apply the Thumbs Up & Down for a Blog Post?
/Lists/Posts/Post.aspx?ID=1
I’ve tried using the DispForm Code, but the Thumbs don’t display.
Can this be modified for Blog Posts?
Thanks, Brett.
Take a look at this one: Hit counter for SharePoint
I have updated it to support counting hits in a blog (or ordinary list).
Alexander
You have a great library of script, with details and descriptions. Have you revisited this one to convert or update to star rating?
I would be interested in this also. Considering that there is no upgrade in the near future.
Have you created the star rating scripts. I would be very much interested in it with same function as this script.
Is there a way to combine this feature with your Comment Box Discussion script?
I’m sorry, but I do not have time to do that at the moment.
Alexander
Every time user is clicking like/dislike it is making separate entry in the rating list. Is there is any way we can increment the same ItemID count if different user clicks on same ItemID? E.g. If user A click on like for ItemID 1 and user B also put like on Item ID 1, both entries are getting recorded as separate entity rather than updating the same ItemID. My requirement is like/dislike entries should be updated against each ItemID rather than as separate entries.
Hi,
This cannot be done due to the possibility for two (or more) users trying to write to the same item simultaneously. SharePoint cannot handle this scenario, and you would either get an error message, or just loose some “hits”.
Alexander
Hi,
I am trying to use this for Wiki page, but unfortunately I am not able to do that. Can you please give me some suggestion to add ratings system or Like/Dislike option for Wiki Page.
Nithin
Hi,
I have not had the time to test it in a Wiki page, and at the moment I’m kind of swamped so I cannot help you on this right now.
Please post back if you find a solution as others might benefit from it.
Alexander
Hi Alex,
Even I didn’t get any solution for this, but for timing I have created general listings and used as a Wiki page and included Like/Dislike option with attachment.
I am attempting to use this on my default.aspx page with a list web part. All the images show up and you can click up or down, but when you refresh the page it looses its value. Any suggestions?
Hi,
Does the values show up in the “RatingForSharePoint” list?
Which browser are you using?
Which version of jQuery are you using?
Alexander
Ok, got it to work. But now we have switched over to SP2010 and the code does not work again. Is this just meant for SP2007?
Alex – Thanks for the wonderful solution.
A question- Can we have the names of the people who liked and dislikes the entry? (something like facebook??)
Thank you much!
Is there a way to get total number of likes and dislikes for an item?
Hi Alex Could you please throw some light on the above question when you get a chance..
SPUser, Alexander is on hiatus for a while and won’t be adding anything new to his Blog for the moment as described in his latest post “Status Update”.
I asked the same question for the Hit/Like Counter: Alexander Bautz Says:
February 17, 2011 at 10:04 am
Hi,
The hit counter stores each “hit” or “like” in a separate line in the “hit counter list”. To retrieve this you would have to write a script to pull them in and count the hits and likes on the individual items. This might take a few seconds, but is no big deal.
The problem is that as the script is setup right now, it does not store the “title” for each record – only the URL. To have the “Most viewed” display in a proper manner, you would have to do another query against the list where the hits are being registered to fetch the title.
Again not hard to do, but adds a few seconds to the page render (depending on the number of line in the counter list).
I realize that support is sporadic, but am hoping someone can answer a question. I was able to get this to work flawlessly in IE. But, when I view the same site/list in Firefox, Chrome or Safari, I get the alert that the Ratings and ID field need to be visible. In IE it works without a problem, but on the other browsers, the alert comes up no matter what I try and the ratings don’t show up. Does anyone have any idea why it is doing this?
Hi,
This post is a bit outdated and the scripts are IE only – due to a limitation in “interaction.js” related to how the script parses xml.
I’ll put it on the “need update list”, but cannot promise anything.
Alexander
No worries, at least I know I’m not crazy haha The script is perfect as is, and in truth, our mandated browser is IE, so technically I can get away with saying “if you aren’t following the rules, don’t expect it to work!” hehe This was a fantastic solution for those of us who can’t do anything server side. So, I am happy nonetheless! Thanks again!
Hi Alex,
I have a customization here. we have to book ‘meeting room’ as well as ‘bridge’ here, can i use this code to check availability for both the resources on the selected time and then save the form?
Guess i posted here wrongly! 😀
I found this blog very interesting. Is there a way to do something similar without the ranking, just track unique visitors to a list item. perhaps when they open the dispform. I am trying to show unread items for each user. it would be nice to see the items unread, but I really just want a count. Maybe in the secondary list we can add listitemid and title. Could create a link back to unread items. let me know your thoughts
Hi,
You are welcome to modify this solution , but I think this is best answered with a custom made solution. I’ll add this as a request, but unfortunately I have a lot in the queue already so it may take some time.
Alexander