Category Archives: List view modification

vLookup type rollup for SharePoint

02.03.2011 Updated the code for the file “vLookupForSharePoint.js” to v1.7.0

  • Add an “orderBy” option to sort the returned items by a FieldInternalName – See parameter descriptions below.
  • The dependencies upon interaction.js and stringBuffer.js has been eliminated.

10.06.2010 Updated the code for the file “vLookupForSharePoint.js” to fix some bugs.
This update includes:

  • Support for multiple webparts in one page
  • Bugfix: get listName in EditForm mode
  • Added option “DispFormRelURL” under “inQueryString” to provide current items relative DispForm URL
  • Handling multichoice values by replacing “;#” with “<br />”
  • Fixed bug with “newItemLink” if “linkBack” is not true
  • Added id tag for the “newItemLinkContainer” – to make hiding it possible.
  • Fixed bug when item has no “children” (newItemLink and inQueryString)
  • Fixed bug when matching on a calculated column

06.05.2010 Updated the code for the file “vLookupForSharePoint.js”.

This update includes:

  • Some small bugfixes
  • Added support for getting items connected by a multiLookup
  • Added createNewItem option by setting the parameter “newItemLink:true”
  • Added inQueryString option to include values from DispForm when creating new items using the “createNewItem” option
  • Added option to display vLookup connected items in EditForm
  • Localized for Norwegian, Swedish and English

01.04.2010 Small update to the file “vLookupForSharePoint.js”. I have modified the argument “hoverToView” to have separate settings for DispForm and for ListView. You must change the argument “hoverToView” to “hoverToViewDispForm” and “hoverToViewListView” in your calculated columns.


I have long thought of making a generic solution for pulling information from another list, kind of like you would do in Excel with the function vLookup. Here is my attempt to create just this. I’m fairly confident this solution will fill an empty space in many SharePointers hearts…

This solution features

  • Easy, generic interface to pull information from any list in current site or cross site
  • Concatenation of values from multiple items
  • Sum, average or count values from multiple items
  • Link back to filtered view of all matched items in “sum”, “avg” or “count” -mode, or to individual items in “concat” -mode
  • Direct link to documents
  • “Reverse lookup” on lookup columns – the “mother” item now has link back to the “children”.
  • And more…

The way this solution works is by using a calculated column to build a string with all the parameters like this:

This code matches the ID of the current item against a lookup column (the “child-list” has a lookup column named “ParentBug” targeting the “Mother-list” – what field the lookup is connected to is irrelevant as it is the ID we are looking for). This approach is a bit different than it will be for other columns as the ID is not accessible to a normal calculated column.

="listName:vLookupBugTrackerTasks|find:ID|findInCol:ParentBug|action:concat|linkBack:true|viewFields:Title#Title;TaskDescription#Task description;Deadline#Deadline"

This example matches the text in the “Title” column in one list against the “Title” column in another list.

="listName:vLookupBugTrackerTasks|find:"&Title&"|findInCol:Title|action:concat|linkBack:true|viewFields:Title#Title"

These are the available arguments:

  • listName: Name or GUID of the target list.
  • listBaseUrl: The base URL of the site the target list is located in. Defaults to current site is the argument is omitted.
  • find: The string to search for in the column specified as “findInCol” below. To use the current items ID (in case of a lookup connection), specify like this: “find:ID”.
  • findInCol: The FieldInternalName of the column to query against.
  • viewFields: “Array” of the fields of which to return a value for. Format: FieldInternalName1#DisplayName1. Separate multiple fields with semicolon.
  • action: “sum”, “avg”, “count” or “concat”.
  • sumOrAvgPrefix: A prefix in “sum” or “avg” -mode, like “$” for dollar.
  • sumOrAvgPostfix: As above, but postfix.
  • linkBack: true=Link back to item(s), false=no linkback. Default value: false
  • linkBackView: If in “sum”, “avg” or “count” -mode, set the target view name for the filtered result on “linkBack”. Defaults to “AllItems.aspx” if omitted.
  • hoverToViewListView: (In “concat” -mode) true=Displays a placeholder text that the user must hover over with the mouse to view the items, false=View items directly in list view. Default value: false
  • hoverToViewDispForm: (In “concat” -mode) true=Displays a placeholder text that the user must hover over with the mouse to view the items, false=View items directly in DispForm. Default value: false
  • hoverToViewEditForm: (In “concat” -mode) true=Displays a placeholder text that the user must hover over with the mouse to view the items, false=View items directly in DispForm. Default value: false
  • newItemLink: (In “concat” -mode) true=Displays a “Create new item” link. Default value: false
  • inQueryString: (In “concat” -mode) semicolon separated array of FieldInternalNames to include the value from in the queryString that is passed to the NewForm. This only applies if the above parameter is set to true. Default value: “”. You need another script in the target list to pull the parameters from the query string and write them to a field in NewForm
  • orderBy: New! A FieldInternalName to sort the returned items by. Set the parameter “orderBy” in the calculated column like this: orderBy:Title. To have the result ordered descending, append a hash behind the FieldInternalName like this: orderBy:Title#

These are the base arguments that are required:
‘listName’,’find’,’findInCol’,’action’,’viewFields’

Note: If your formula errors out, it most likely has to do with the string being to long. To overcome this problem, just concatenate the string like this:
…first part of the string her”&”and the rest here…


Example images:

List view with tasks pulled from tasklist
IMG

Hover over each item for “link back menu”. Click on a “link back” will take you to the item
IMG

hoverToViewListView:true – before hover:
IMG

hoverToViewListView:true – after hover:
IMG

“sum”, “avg” or “count” looks like this:
IMG

A click on a “link back” on items in “sum”,”avg” or “count” -mode will take you to a filtered list of all matched items.
IMG
The target list view is set in the parameter “linkBackView”.

DispForm with “newItemLink:true”:
IMG

The same item in EditForm:
IMG

When setting the parameter inQueryString like this “inQueryString:Title;ID”, the value from these fields are included in the querystring of the URL (click to enlarge the image):
IMG
You need another script in the target list to pull the parameters from the query string and write them to a field in NewForm. Look here for an example.

When the code is added to the listView and to DispForm/EditForm, you create a new “enhanced lookup column” by creating a new field of type “Calculated (calculation based on other columns)” with a FieldInternalName starting with “vLookup” (you can rename the column afterwards, it’s only the FieldInternalName that is important). All fields with a FieldInternalName starting with this text will be included. No need to specify the columns to include! (this is not true for EditForm though, see separate instructions). Note: the ID column must be in the view (but can be hidden in the script).

Here is the code

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 referred from Google, but if you prefer a local copy, it is found here. The pictures and the sourcecode refers to jquery-1.5.1.min. If you download another version, be sure to update the script reference in the sourcecode.

The sourcecode for the file “vLookupForSharePoint.js” is found below.

Read here how to add a CEWP to the DispForm or EditForm.

Add this code in a CEWP below the list form in DispForm:

<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js"></script>
<script type="text/javascript" src="/test/English/Javascript/vLookupForSharePoint.js"></script>
<script type="text/javascript">
  init_vLookupForSharePointDispForm();
</script>

Add this code in a CEWP below the list form in EditForm:

<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js"></script>
<script type="text/javascript" src="/test/English/Javascript/vLookupForSharePoint.js"></script>
<script type="text/javascript">
  // This is an array of objects. Add more fields by adding a new object to the array
  // The "insertHere" value can be "top", "bottom" or a FieldInternalName to append the returned data to
  init_vLookupForSharePointEditForm([{'FieldInternalName':'vLookupTasks','FieldDisplayName':'Tasks','insertHere':'BugDescription'}]);
</script>

The reason the EditForm code is different is that the calculated column is not available unless we query for it using a CAML query. The “insertHere” parameter is used to place the “new field”.

Add this code in a CEWP below the list view:

<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js"></script>
<script type="text/javascript" src="/test/English/Javascript/vLookupForSharePoint.js"></script>
<script type="text/javascript">
// The argument "true" sets the ID column to hidden
  init_vLookupForSharePointListView(true);
</script>

The code for the file “vLookupForSharePoint.js”:
download code

Upload the file to the scriptlibrary as described above.

If you find any bugs, please let me know!

Regards
Alexander

Ratings for SharePoint lists

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.

ListView:
IMG

DispForm:
IMG

DispForm if already rated:
IMG

To enable rating of items, you add a calculated column to your list with this code:

=&quot;&quot;

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”):
IMG

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:

&lt;script type=&quot;text/javascript&quot; src=&quot;/test/English/Javascript/jquery-1.3.2.min.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;/test/English/Javascript/interaction.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;/test/English/Javascript/stringBuffer.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;/test/English/Javascript/sessvars.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;/test/English/Javascript/RatingForSharePoint.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
ratingForSharePoint('Rating',true,true);
&lt;/script&gt;

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:

&lt;script type=&quot;text/javascript&quot; src=&quot;/test/English/Javascript/jquery-1.3.2.min.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;/test/English/Javascript/interaction.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;/test/English/Javascript/stringBuffer.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;/test/English/Javascript/sessvars.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;/test/English/Javascript/RatingForSharePoint.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
// You must edit this GUID to match THIS list's GUID - this is NOT the GUID of the &quot;RatingForSharePoint&quot;
thisListsGuid = '{77C35652-1D4D-4B09-B58C-D941068D251E}';
ratingForSharePointDispForm('Rating',true);
&lt;/script&gt;

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)

Read here how to add a CEWP to the NewForm, DispForm or EditForm, how to find the list Guid of your list, and how to find the FieldInternalName of your columns.

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 &quot;_spUserId&quot; 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 &quot;Rating&quot; column
		$(&quot;.ms-viewheadertr th&quot;).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
		$(&quot;.ms-viewheadertr th&quot;).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(&quot;Both the column with FieldInternalName &quot;&quot;+FieldInternalName+&quot;&quot; and the ID-column must be in the view.&quot;);
			return false;
		}
	}

	$(&quot;table.ms-listviewtable tbody:not([id^='aggr']) &gt;tr[beenthere!=1]:has(td.ms-vb2)&quot;).each(function(){
		$(this).attr('beenthere',1);
		var itemAlreadyRate = false;
		var thisTd = $(this).find(&quot;td[cellIndex=&quot; + colIndex + &quot;]&quot;);
		var thisIdColumn = $(this).find(&quot;td[cellIndex=&quot; + IdColIndex + &quot;]&quot;);
		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 &amp;&amp; currentRatingObj[thisId]['ratedByMe']==true){
				itemAlreadyRate = true;
			}
			rPos = currentRatingObj[thisId]['Plus'];
			rNeg = currentRatingObj[thisId]['Minus'];
		}
		// Rated in this session
		if(sessvars[thisListsGuid+thisId]==1 &amp;&amp; currentRatingObj!=false){
			itemAlreadyRate = true;
		}

		var str = &quot;&lt;div style='color:gray'&gt;&quot;;
		str += &quot;&lt;span&gt;&lt;img &quot;;
		if(!itemAlreadyRate){
			str += &quot;onclick='javascript:rateMe(&quot;up&quot;,&quot;+thisId+&quot;)' title='Rate Up' &quot;;
			str += &quot;style='vertical-align:middle;cursor:pointer' &quot;;
		}else{
			str += &quot;title='You have already rated this item &quot;+currentRatingObj[thisId]['ratedByMeVal']+&quot;' &quot;;
			str += &quot;style='vertical-align:middle;cursor:no-drop' &quot;;
		}
		str += &quot;src='/_layouts/images/arrupi.gif'&gt;&lt;/span&gt;&quot;;
		str += &quot;&lt;span style='font-weight:bold;' id='rateUp_&quot;+thisId+&quot;'&gt;&quot;+rPos+&quot;&lt;/span&gt;&quot;;
		str += &quot;&lt;span&gt;&lt;img &quot;;
		if(!itemAlreadyRate){
			str += &quot;onclick='javascript:rateMe(&quot;down&quot;,&quot;+thisId+&quot;)' title='Rate Down' &quot;;
			str += &quot;style='vertical-align:middle;cursor:pointer' &quot;;
		}else{
			str += &quot;title='You have already rated this item &quot;+currentRatingObj[thisId]['ratedByMeVal']+&quot;' &quot;;
			str += &quot;style='vertical-align:middle;cursor:no-drop' &quot;;
		}
		str += &quot;src='/_layouts/images/arrdowni.gif'&gt;&lt;/span&gt;&quot;;
		str += &quot;&lt;span style='font-weight:bold;' id='rateDown_&quot;+thisId+&quot;'&gt;&quot;+rNeg+&quot;&lt;/span&gt;&quot;;
		str += &quot;&lt;/div&gt;&quot;;
		// Write HTML
		thisTd.html(str);
	});
}

function getRating(){
// Path to webservices
wsBaseUrl = L_Menu_BaseUrl + '/_vti_bin/';
var query = &quot;&lt;Where&gt;&lt;Eq&gt;&lt;FieldRef Name='ListGuid' /&gt;&lt;Value Type='Text'&gt;&quot;+ctx.listName+&quot;&lt;/Value&gt;&lt;/Eq&gt;&lt;/Where&gt;&quot;;
var rating = queryItems('RatingForSharePoint',query,['Plus','Minus','ItemID','Author']);
	if(rating.count==-1){
		alert(&quot;An error occured in the query:n&quot;+query);
	}else if(rating.count&gt;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']&gt;0){
					ratingObj[item['ItemID']]['ratedByMeVal']=&quot;+1&quot;;
				}else{
					ratingObj[item['ItemID']]['ratedByMeVal']=&quot;-1&quot;;
				}
			}
		});
		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 &amp;&amp; currentRatingObj[thisId]['ratedByMe']==true){
			itemAlreadyRate = true;
		}
		rPos = currentRatingObj[thisId]['Plus'];
		rNeg = currentRatingObj[thisId]['Minus'];
	}
	// Rated in this session
	if(sessvars[thisListsGuid+thisId]==1 &amp;&amp; currentRatingObj!=false){
		itemAlreadyRate = true;
	}
	var thisTd = $(fields[FieldInternalName]).find('.ms-formbody');
	var str = &quot;&lt;div style='color:gray'&gt;&quot;;
	str += &quot;&lt;img &quot;;
	if(!itemAlreadyRate){
		str += &quot;onclick='javascript:rateMe(&quot;up&quot;,&quot;+thisId+&quot;)' title='Rate Up' &quot;;
		str += &quot;style='vertical-align:middle;cursor:pointer' &quot;;
	}else{
		str += &quot;title='You have already rated this item &quot;+currentRatingObj[thisId]['ratedByMeVal']+&quot;' &quot;;
		str += &quot;style='vertical-align:middle;cursor:no-drop' &quot;;
	}
	str += &quot;src='/_layouts/images/arrupi.gif'&gt;&quot;;
	str += &quot;&lt;span style='font-weight:bold;' id='rateUp_&quot;+thisId+&quot;'&gt;&quot;+rPos+&quot;&lt;/span&gt;&quot;;
	str += &quot;&lt;img &quot;;
	if(!itemAlreadyRate){
		str += &quot;onclick='javascript:rateMe(&quot;down&quot;,&quot;+thisId+&quot;)' title='Rate Down' &quot;;
		str += &quot;style='vertical-align:middle;cursor:pointer' &quot;;
	}else{
		str += &quot;title='You have already rated this item &quot;+currentRatingObj[thisId]['ratedByMeVal']+&quot;' &quot;;
		str += &quot;style='vertical-align:middle;cursor:no-drop' &quot;;
	}
	str += &quot;src='/_layouts/images/arrdowni.gif'&gt;&quot;;
	str += &quot;&lt;span style='font-weight:bold;' id='rateDown_&quot;+thisId+&quot;'&gt;&quot;+rNeg+&quot;&lt;/span&gt;&quot;;
	str += &quot;&lt;/div&gt;&quot;;
	// Write HTML
	thisTd.html(str);
}

function getRatingDispForm(itemID){
// Path to webservices
wsBaseUrl = L_Menu_BaseUrl + '/_vti_bin/';
var query = &quot;&lt;Where&gt;&lt;And&gt;&lt;Eq&gt;&lt;FieldRef Name='ListGuid' /&gt;&lt;Value Type='Text'&gt;&quot;+thisListsGuid+&quot;&lt;/Value&gt;&lt;/Eq&gt;&quot;+
			&quot;&lt;Eq&gt;&lt;FieldRef Name='ItemID' /&gt;&lt;Value Type='Text'&gt;&quot;+itemID+&quot;&lt;/Value&gt;&lt;/Eq&gt;&lt;/And&gt;&lt;/Where&gt;&quot;;
var rating = queryItems('RatingForSharePoint',query,['Plus','Minus','ItemID','Author']);
	if(rating.count==-1){
		alert(&quot;An error occured in the query:n&quot;+query);
	}else if(rating.count&gt;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']&gt;0){
					ratingObj[item['ItemID']]['ratedByMeVal']=&quot;+1&quot;;
				}else{
					ratingObj[item['ItemID']]['ratedByMeVal']=&quot;-1&quot;;
				}
			}
		});
		return ratingObj;
	}else{
		return false;
	}
}

/************************************************************
*********************** Shared Code ***********************
************************************************************/
function rateMe(upORdown,id){
if(typeof(ctx)=='object'){
	var listGuid=ctx.listName;
	var listTitle = $.trim($(&quot;td.ms-pagetitle&quot;).text());
}else{
	var listGuid=thisListsGuid;
	var listTitle = $.trim($(&quot;td.ms-pagetitle a&quot;).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($(&quot;#rateUp_&quot;+id).text());
			$(&quot;#rateUp_&quot;+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($(&quot;#rateDown_&quot;+id).text());
		$(&quot;#rateDown_&quot;+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':&quot;Rating for: &quot; + listTitle,
							'Plus':plus,
							'Minus':minus,
							'ListUrl':urlDir,
							'ListGuid':listGuid,
							'ListName':listTitle,
							'ItemID':id});
	}
}

// Attaches a call to the function to the &quot;expand grouped elements function&quot; for it to function in grouped listview's
function ExpGroupRenderData(htmlToRender, groupName, isLoaded){
	var tbody=document.getElementById(&quot;tbod&quot;+groupName+&quot;_&quot;);
	var wrapDiv=document.createElement(&quot;DIV&quot;);
	wrapDiv.innerHTML=&quot;&lt;TABLE&gt;&lt;TBODY id=&quot;tbod&quot;+groupName+&quot;_&quot; isLoaded=&quot;&quot;+isLoaded+&quot;&quot;&gt;&quot;+htmlToRender+&quot;&lt;/TBODY&gt;&lt;/TABLE&gt;&quot;;
	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&gt;0){
		var qpart = urlSearch.substring(1).split('&amp;');
		$.each(qpart,function(i,item){
			var splitAgain = item.split('=');
			qObj[splitAgain[0]] = splitAgain[1];
		});
	}
return qObj;
}

function init_fields(){
var res = {};
$(&quot;td.ms-formbody&quot;).each(function(){
if($(this).html().indexOf('FieldInternalName=&quot;')&lt;0) return;
var start = $(this).html().indexOf('FieldInternalName=&quot;')+19;
var stopp = $(this).html().indexOf('FieldType=&quot;')-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

Multi line text field in list view: Shorten text and add hovereffect to view full text

11.08.2010 Updated description to of the parameter “FieldInternalName”. Use “DisplayName” if the multiline field is of type rich or enhanced rich test.


I got this request from ronajon:

hi Alexander,
i accidently stumbled on your great blog search for a way to modify my lsitview with javascript.

here’s what i would like to accomplish:
i have this allitems view with multiple columns. One of these colums is of the multi lines of text.
When i start editing my items my multiline column cell strats growing in size due to a point that i can only see one item in my screen and need to scroll down to see other items in the list.

Is there a way to limit the number of characters in the multi line column (named “Voortgang en resultaat”) to show less characters: preferably one line of text or text up until some splitting character?

thanks,
ronajon


The text is limited to 100 characters, but when hovered, it shows the full text:
IMG

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”):
IMG

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 code for the file “LimitMultilineTextInView.js” is found below.

This will work in standard views and in grouped views, but has not been adapted to work with “Boxed” views.

Add a CEWP below the list view and add this code:

<script src="/test/English/Javascript/jquery-1.3.2.min.js" type="text/javascript"></script>
<script src="/test/English/Javascript/LimitMultilineTextInView.js" type="text/javascript"></script>
<script type="text/javascript">
// Shows the first 100 characters in the text
	limitMultiLineText('MultilineText',100);
// Shows the text 'Hover to see content' in stead of the actual text
	//limitMultiLineText('MultilineText','','Hover to see content');
</script>

Parameters explained:

  • FieldInternalName: The “FieldInternalName” of the field containing the multi line text. Use DisplayName if the multiline field is of type rich or enhanced rich text. (how to find the FieldInternalName)
  • initialLength: Limit the initial displayed text to this number of characters. This argument is overridden by the next, if supplied.
  • inPlaceHoverText: If set, this argument will override the previous, and add a “inPlace” text that can be hovered to view the full text.

When hovered there are a delay for 650 milliseconds to prevent flickering when you move the mouse rapidly over multiple cells.

The code for the file “LimitMultilineTextInView.js” looks like this:

/* Limit the length of the text in a multi line text field in a list view
* Created by Alexander Bautz
* alexander.bautz@gmail.com
* https://spjsblog.com
* v1.0
* LastMod: 21.02.2010
* ---------------------------------------------
* Requirements:
Include reference to jquery - http://jquery.com
* ---------------------------------------------
*
Call from a CEWP below list view like this:
<script src="/test/English/Javascript/jquery-1.3.2.min.js" type="text/javascript"></script>
<script src="/test/English/Javascript/LimitMultilineTextInView.js" type="text/javascript"></script>
<script type="text/javascript">
	// Shows the first 100 characters in the text
		limitMultiLineText('MultilineText',100);
	// Shows the text 'Hover to see content' in stead of the actual text
		//limitMultiLineText('MultilineText','','Hover to see content');
	
</script>
*/

function limitMultiLineText(FieldInternalName,initialLength,inPlaceHoverText){
if(typeof(FieldInternalName)!='undefined'){
intName = FieldInternalName;
initLength = initialLength;
inPlaceText = inPlaceHoverText;
$(".ms-viewheadertr th.ms-vh2-nograd").each(function(){
if($(this).text()==intName){
colIndex = $(this).attr('cellIndex');
}
});
}

$("table.ms-listviewtable tbody:not([id^='aggr']) tr:has(td.ms-vb2) >td[cellIndex=" + colIndex + "][beenthere!=1]").each(function(){
$(this).attr('beenthere',1);
var thisTd = $(this);
if(inPlaceText!='' && inPlaceText!=undefined){
var teaserText = inPlaceText;
}else{
var teaserText = thisTd.text().substring(0,initLength);
}
thisTd.wrapInner("<div style='background-color: white; border: thin silver ridge; padding: 4px; display: none;'></div>")
.prepend("<span style="cursor: default;">"+teaserText+"...</span>")
.hover(function(){
thisTd.addClass('dummyHoverClass ms-dialogSelectedRow');
setTimeout(function(){
if(thisTd.hasClass('dummyHoverClass')){
var offset = thisTd.offset();
var tdWidth = thisTd.width();
thisTd.find('div:first')
.css({'position':'absolute',
'top':offset.top,
'left':offset.left,
'width':tdWidth})
.fadeIn(250)
.prev()
.hide();
}
},650);
},function(){
if(thisTd.hasClass('dummyHoverClass')){
thisTd.removeClass('dummyHoverClass ms-dialogSelectedRow');
thisTd.find('div:first').stop(true, true).fadeOut(100).prev().show();
}
});
});
}

// Attaches a call to the function "limitMultiLineText()" to the "expand grouped elements function" for it to function in grouped listview's
function ExpGroupRenderData(d,a,e){
ULSA13:;
var c=document.getElementById("tbod"+a+"_"),b=document.createElement("DIV"),f=a.split("-");
b.innerHTML='<TABLE><TBODY id="tbod'+a+'_" isLoaded="'+e+'">'+d+"</TBODY></TABLE>";
c.parentNode.replaceChild(b.firstChild.firstChild,c);
limitMultiLineText();
}

Save the file as “LimitMultilineTextInView.js”, mind the file extension, and upload to the scriptlibrary as shown above.

Enjoy!
Alexander

Filter list view based on membership in SharePoint group

This script filters a list view based on membership in SharePoint groups. The filtering has nothing to do with item level security, it is merely a method for filtering by comparing a users group membership against a text field containing a group name. This column “VisibleTo” must contain the actual group names from the “People and Groups” list in SharePoint.

This method gives you the ability to filter the list view based on multiple criteria. If the user is member of multiple groups, the list will display all items relevant to any of the groups. You may also specify that elements where the column “VisibleTo” is empty will be displayed for all visitors. The “VisibleTo” column can be a multiple choice checkbox type column.

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”):
IMG

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 script “AccessUserProfileInWSS.js” is found here.

Note:

  • If used in grouped views, you must have the groups initially expanded, otherwise the elements are not rendered and cannot be filtered.
  • Only one level of grouping is currently supported.
  • Boxed view are not supported.
  • The filter does not apply in a datasheet view.

Add a CEWP below the list view webpart, and add this code:

&lt;script type=&quot;text/javascript&quot; src=&quot;../../Javascript/jquery-1.3.2.min.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;../../Javascript/interaction.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;../../Javascript/stringBuffer.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;../../Javascript/AccessUserProfileInWSS.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;../../Javascript/FilterListViewByGroupMembership.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot;&gt;

// Find the user login name
var ui = getUserInfo(); 
var userLoginName = ui['Name'];
// Get the group collection for the current user
var ug = getGroupCollectionObjFromUser(userLoginName);
// Set the filter - if you want the results to include elements in a specific group by default, specify it in the array below
filterVal = [];
// Define the group - find the group ID in &quot;People and Groups&quot;
var groupIdTeam1 = 145;
var groupIdTeam2 = 146;
// Build the filter - add more if's to complete your filter
// Is the user member of the group with id 145?
if(ug[groupIdTeam1]!=undefined){
	filterVal.push(ug[groupIdTeam1]);
}
// Is the user member of the group with id 146?
if(ug[groupIdTeam2]!=undefined){
	filterVal.push(ug[groupIdTeam2]);
}

// Call the function
filterListViewByGroupMembership('VisibleTo',filterVal,true,true,true);
&lt;/script&gt;

The sourcecode for the file “FilterListViewByGroupMembership.js”:

/* Filter list view by membership in SharePoint group - hide the column header and the filter column if specified
 * -----------------------------
 * Created by Alexander Bautz
 * LastMod 15.02.2010
 * -----------------------------
 * Include reference to:
	jquery - http://jquery.com
	interaction.js - http://spjslib.codeplex.com/
	stringBuffer.js - http://spjslib.codeplex.com/
	AccessUserProfileInWSS.js
	FilterListViewByGroupMembership.js
 * -----------------------------
   If used in grouped views, you must have the groups initially expanded, otherwise the elements are not rendered and cannot be filtered.
   Note: only one group level is currently supported.
*/

function filterListViewByGroupMembership(FieldInternalName,filterValueArr,includeEmpty,hideFilterCol,collapseGroupedViews){
filterOK = true;
	// Build object of all headings
	if(typeof(filterColIndex)=='undefined'){
		if(typeof(FieldInternalName)=='string'){
			intName = FieldInternalName;
			hideTD = hideFilterCol;
			fvArr = filterValueArr;
			inclEmpty = includeEmpty;
		}
		filterColIndex = '';
		$(&quot;.ms-viewheadertr th&quot;).each(function(){
			if($(this).find('table:first').attr('name')==intName){
				filterColIndex = $(this).attr('cellIndex');	
				displayName = $(this).find('table:first').attr('displayname');
				// If the parameter &quot;hideFilterCol&quot; is true, remove the column
				if(hideTD){
					$(this).remove()
				}
			}
		});
		
		if(filterColIndex==''){
			filterOK = false;
			var str = &quot;&lt;font color='red'&gt;The column with FieldInternalName  &quot;&quot; + intName + &quot;&quot;, must be in the view for the filter to work.&lt;/font&gt;&quot;;
			$(&quot;td.ms-toolbar[width='99%']&quot;).append(&quot;&lt;div id='hoverDelayInfo' class='ms-listheaderlabel' style='text-align:center;margin-top:-15px'&gt;&quot; + str + &quot;&lt;/div&gt;&quot;);
		}
	}
	
	// Apply filter
	if(filterOK){
		// Build view header text
		if(inclEmpty &amp;&amp; fvArr.length==0){
			fvArr.push('');
			var fv = displayName + &quot; = empty&quot;;
		}else if(inclEmpty &amp;&amp; fvArr.length&gt;0){
			fvArr.push('');
			var fv =  displayName + &quot; = &quot; + fvArr.join(' or ') + &quot; or empty&quot;;
		}else if(!inclEmpty &amp;&amp; fvArr.length==0){
			var fv = &quot;No filtervalues found - all items hidden&quot;;
		}else if(!inclEmpty &amp;&amp; fvArr.length&gt;0){
			var fv =  displayName + &quot; = &quot; + fvArr.join(' or ');
		}		
		// Insert view header text
		if($(&quot;#filterInfo&quot;).length==0){
			$(&quot;td.ms-toolbar[width='99%']&quot;)
				.append(&quot;&lt;div id='filterInfo' class='ms-listheaderlabel' style='text-align:center;margin-top:-15px'&gt;&quot; +
						&quot;&lt;span style='cursor:default'&gt;&lt;img src='/_layouts/images/filter.gif'&gt;&amp;nbsp;&quot; + fv + &quot;.&lt;/span&gt;&lt;/div&gt;&quot;);
		}

		// Find all rows and apply filter
		$(&quot;table.ms-listviewtable tbody:not([id^='aggr']) tr:has(td.ms-vb2) &gt;td[cellIndex=&quot; + filterColIndex + &quot;]&quot;).each(function(){	
			var thisTd = $(this);
			// Build array from all values in &quot;Visible to&quot; - column
			var arrCurrTDtext = thisTd.text().split(';');
			var keeper = false;
			// Find all keepers
			$.each(arrCurrTDtext,function(i,val){
				var thisVal = $.trim(val);
				if($.inArray(thisVal,fvArr)&gt;-1){
					keeper = true;
				}
			});	
			// Remove the ones that do not match
			if(!keeper){
				thisTd.parents('tr:first').remove();				
			}			
			// If the parameter &quot;hideFilterCol&quot; is true, remove the column			
			if(hideTD){
				$(this).remove()
			}
		});
		
		// Test whether the view is grouped twice and give alert
		if($(&quot;td.ms-gb2&quot;).length&gt;0)alert(&quot;Sorry, but this script does not handle views grouped twice.&quot;);
		// Loop trough every tbody to remove empty and to update item count
		$(&quot;tbody[isloaded='true'][beenthere!='true']&quot;).each(function(){
			$(this).attr('beenthere',true)
			var gId = $(this).attr('id');
			var gIdPart = gId.substring(4,gId.length-1);
				// Number of elements remaining
				var trLength = $(this).find(&quot;td.ms-vh-group&quot;).length;				
				if(trLength==0){	
					// Remove the group heading and remaining elements for the group			
					$(&quot;#titl&quot;+gIdPart).remove();
					$(&quot;#foot&quot;+gIdPart+&quot;_&quot;).remove();
					$(this).remove();
				}else{
					// Write the new count to the group heading
					$(&quot;#titl&quot;+gIdPart).find('td.ms-gb span:last').html(&quot;&amp;lrm;(&quot; + trLength + &quot;)&quot;);
				}
		});
		
		// Collapse all groups - they must be expanded by default for the filter to apply
		if(collapseGroupedViews){
			$(&quot;td.ms-gb&quot;).each(function(){
				$(this).find('a:first').click();
			});
		}
		
		// Fix highlighting of every other row
		var classToFind = '';
		if($(&quot;table.ms-listviewtable&quot;).find('tr.ms-alternating').length&gt;0){
			var classToFind = 'ms-alternating';
		}else if($(&quot;table.ms-listviewtable&quot;).find('tr.ms-alternatingstrong').length&gt;0){
			var classToFind = 'ms-alternatingstrong';
		}
		if(classToFind!=''){
			$(&quot;table.ms-listviewtable tr:has(td.ms-vb2)&quot;).each(function(i,row){
				if(i%2==0){
					$(this).addClass(classToFind);
				}else{
					$(this).removeClass(classToFind);
				}
				
			});
		}
		
	}
}

/*
 * Function to get the groupCollectionfrocurrent user.
 * Requires the scripts interaction.js and stringBuffer.js to be loaded
*/
function getGroupCollectionObjFromUser(userLoginName){
	var result = {};
	innerPost(wsBaseUrl + 'usergroup.asmx', 
		'http://schemas.microsoft.com/sharepoint/soap/directory/GetGroupCollectionFromUser',
		'&lt;GetGroupCollectionFromUser xmlns=&quot;http://schemas.microsoft.com/sharepoint/soap/directory/&quot;&gt;&lt;userLoginName&gt;' + userLoginName + '&lt;/userLoginName&gt;&lt;/GetGroupCollectionFromUser&gt;',
		function(data){		
			$('Group', data).each(function(idx, itemData){
				result[$(itemData).attr('ID')] = $(itemData).attr('Name');
			});
		});
	return result;
}

// Attaches a call to the function to the &quot;expand grouped elements function&quot; for it to function in grouped listview's
function ExpGroupRenderData(htmlToRender, groupName, isLoaded){
	var tbody=document.getElementById(&quot;tbod&quot;+groupName+&quot;_&quot;);
	var wrapDiv=document.createElement(&quot;DIV&quot;);
	wrapDiv.innerHTML=&quot;&lt;TABLE&gt;&lt;TBODY id=&quot;tbod&quot;+groupName+&quot;_&quot; isLoaded=&quot;&quot;+isLoaded+&quot;&quot;&gt;&quot;+htmlToRender+&quot;&lt;/TBODY&gt;&lt;/TABLE&gt;&quot;;
	tbody.parentNode.replaceChild(wrapDiv.firstChild.firstChild,tbody);
javascriptFilterView();
}

Save as “FilterListViewByGroupMembership.js”, mind the file extension, and upload to the scriptlibrary as shown above.

Let me know if something is unclear.

Regards
Alexander

Expand empty subgroups and remove group header in grouped list view

I got this request from Kale:

Do you have an ideas on how to remove “empty/blank” items when using the “group by” feature? See the example below. In other words, for category ‘Test 1′ you have to expand the blank item just to see the documents since they don’t have any subcategories assigned to them.

Example:
* Category: Test 1
* Subcategory 1:
– Document 1
– Document 2
* Category: Test 2
* Subcategory: SharePoint
– Document 3
– Document 4
* Subcategory: CRM
– Document 5
– Document 6

This is how I want it to work:
* Category: Test 1
– Document 1
– Document 2
* Category: Test 2
* Subcategory: SharePoint
– Document 3
– Document 4
etc.

Thanks!
Kale

This script checks if the “groupBy” is empty, and if so, expands the content of the group and hides the group header.

Add this script in a CEWP below the list view – alter the location of jQuery to reflect your location:

<script type="text/javascript" src="../../Javascript/jquery-1.3.2.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
	// Add onclick to the first grouping
	$(".ms-listviewtable td.ms-gb").click(function(){
		expandEmptyCategory();
	});	
	// Call function on load
	isTbodyLoaded();	
});

var isloadedCheck = 0;
// Looping function to call "expandEmptyCategory" when subgroups are loading - looping every 200ms for a maximum of 4 seconds
function isTbodyLoaded(){
	setTimeout(function(){
		tbodyIsLoadedLength = $(".ms-listviewtable tbody[isLoaded='true']").length;
		if(tbodyIsLoadedLength>0){
			expandEmptyCategory();
		}else{
			isloadedCheck += 1;
			if(isloadedCheck<=20){
				isTbodyLoaded();
			}
		}
	},200);
}

function expandEmptyCategory(){
var NameGrouping2 = $(".ms-listviewtable td.ms-gb2:first a:eq(1)").text();
	$(".ms-listviewtable td.ms-gb2").each(function(){	
		var grNameRaw = $(this).text().replace(NameGrouping2+' :','');
		grName = grNameRaw.substring(0,grNameRaw.indexOf('(')-1).replace(/s|xA0/g,'');
		if(grName.length==0){
		var parentTbody = $(this).parents('tbody:first');
			if(parentTbody.css('display')!='none'){
			var tIdRaw = parentTbody.attr('id');
			var tId = tIdRaw.substring(4);
			var tb = $("#tbod"+tId+"_");
				if(tb.attr('isLoaded')){
					if(tb.css('display')=='none'){
						$(this).find('a:first').click();
						$(this).parents('tbody:first').hide();
					}else{
						$(this).parents('tbody:first').hide();
					}
				}
			}		
		}	
	});
}
</script>

This solution is not fully tested so please post any bugs you might find.

SharePoint 2013

Here is a version that works with SP 2013. Due to the asynch loading of the view, this one uses a setInterval, and an initial delay to try to let the view finish loading before kicking in. Try it and see how it behaves, but keep in mind this is most likely not a 100% solution.

Place code in a HTML form web part, or linked using the content link option of a CEWP below the list view.

<script type="text/javascript" src="https://spjsblog.sharepoint.com/DFFS/SPJS/jquery-1.11.1.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
	setTimeout(function(){
		$("td.ms-gb").each(function(){
			$(this).parents("tbody:first").on("click",function(){
				$(this).next().find(".spjs_expanded").removeClass("spjs_expanded");
			});
		});
		setInterval(function(){
			$("td.ms-gb2:visible:not(.spjs_expanded)").each(function(i,td){
				var a = $(td).find("a:first").parent().text().split(":")[1].split("(")[0], b, c, d;
				if($.trim(a) === ""){
					b = $(td).parents("tbody:first").attr("id");
					if(typeof b === "string"){
						b = b.substring(4);
						c = $("#tbod"+b+"_").attr("isloaded") === "true";
						d = $("#tbod"+b+"_").is(":visible");
						if(!c || !d){
							$(this).find("a:first").trigger("click");
						}
						$(this).addClass("spjs_expanded");
						$(this).parents("tbody:first").hide();
					}
				}
			});		
		},500);
	},1000);
});
</script>

Alexander

Add a Success Ratio label to the first group header in a dual grouped list view

27.01.2012 Updated to support SharePoint 2010.


By request from Charlie Epes i have made a function for adding a “Success Ratio” to the ms-gb-row (first group in grouped list view):
IMG

This function takes two arguments:

  • leadText: The text added before the percentage value
  • txtToFind: The text to search for and to get the number of items from

The value is a product of the number of items in the group that matches the “txtToFind”, divided by the total number of items from the ms-gb-row (first group) times 100 to get the percentage.

Add a CEWP below the list view you want this to apply to, and add this code (Modify the text values in line 18 to match your values in the second group):

&lt;script type=&quot;text/javascript&quot; src=&quot;https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
/* Function for adding a &quot;Success Ratio&quot; to the ms-gb-row (first group in grouped list view).
 * ---------------------------------------------
 * Created by Alexander Bautz
 * alexander.bautz@gmail.com
 * https://spjsblog.com
 * LastMod: 27.01.2012
 * ---------------------------------------------
 This function takes two arguments:
   leadText - The text added before the percentage value
   txtToFind - The text to search for and to get the number of items from
 
  The value is a product of the number of items in the group that matches the &quot;txtToFind&quot; divided 
  by the total number of items from the ms-gb-row (first group) times 100 to get the percentage.
*/

successRatio('Success Ratio = ','Sold');

function successRatio(leadText,txtToFind){	
	$(&quot;.ms-gb&quot;).each(function(){
		var gbID, textRaw, gbText, gbVal, htmlRaw, part1, part2;
		gbID = $(this).parents('tbody:first')[0].id;
		textRaw = $(this).text();
		gbText = textRaw.substring(textRaw.indexOf(':')+2,textRaw.indexOf('(')-1);
		gbVal = textRaw.match(/d+/);
		getSubGroupes(leadText,gbVal,gbID,txtToFind,this);
		htmlRaw = $(this).html();
		part1 = htmlRaw.substring(0,htmlRaw.lastIndexOf('(')+1);
		part2 = htmlRaw.substring(htmlRaw.lastIndexOf('(')+1);
		$(this).html(part1 + &quot;total=&quot; + part2)	
	});
}

function getSubGroupes(lt,tot,gID,ttf,obj){
	$(&quot;tbody[id^='&quot;+gID+&quot;']:has('td.ms-gb2')&quot;).each(function(){
		var gb2ID, gb2TextRaw, gb2Text, gb2Val
		gb2ID = this.id;
		gb2TextRaw = $(this).text();
		gb2Text = gb2TextRaw.substring(gb2TextRaw.indexOf(':')+2,gb2TextRaw.indexOf('(')-2);
		gb2Val= gb2TextRaw.match(/d+/);
		if(gb2Text==ttf){
			val = Math.round((gb2Val/tot*100)*10)/10;
			$(obj).append(&quot;&lt;span style='padding-left:25px;color:red;font-weight:bold'&gt;&amp;nbsp;&quot; + lt + val + &quot;%&lt;/span&gt;&quot;);
		}
	});
}
&lt;/script&gt;

Ask if anything is unclear.

Regards
Alexander

Highlight row by value in Yes/No-column

08.10.2011 Updated with SharePoint 2010 code.


22.04.2010 Updated the code to support multiple views of the same list in the same page, as requested by Mich.

The file “HighlightRowByYesNoColumn.js” is updated.


In this post i will show you how to highlight a row in a list view based on the value in a column of type “Yes/No (check box)”. The code would work on other types of columns as well.

IMG
This is from a standard list view, it does also work in grouped view’s.

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”):
IMG

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.

Add a CEWP below the list view and insert 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/HighlightRowByYesNoColumn.js"></script>
<script type="text/javascript">
highlightRowByYesNoField('Yes','YesNo','#FFD700','Highlighted rows awaiting action...');
</script>

Parameters explained:

  • valToTriggerHighlighting: The text you want to trigger the highlighting on – “Yes”
  • FieldInternalName: The FieldInternalName of the column to get the value from – must be in view
  • highlightColor: The background color of the highlighted row
  • mouseOverOnRow: The mouse over message when hovered over a highlighted row

The sourcecode for the file “HighlightRowByYesNoColumn.js” looks like this:

SharePoint 2007
/* Highlight row in list view based on value in a Yes/No-column
 * ---------------------------------------------
 * Created by Alexander Bautz
 * alexander.bautz@gmail.com
 * https://spjsblog.com
 * v1.1
 * LastMod: 22.04.2010
 * ---------------------------------------------
 * Include reference to jquery - http://jquery.com
 * ---------------------------------------------
 * Call from a CEWP below the list view with 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/HighlightRowByYesNoColumn.js"></script>
	<script type="text/javascript">
		highlightRowByYesNoField('Yes','YesNo','#FFD700','Highlighted rows awaiting action...');
	</script>
*/

function highlightRowByYesNoField(valToTriggerHighlighting,FieldInternalName,highlightColor,mouseOverOnRow){
	if(typeof(valToTriggerHighlighting)!='undefined'){
		valToFind = valToTriggerHighlighting;
	}
	if(typeof(FieldInternalName)!='undefined'){
		fin = FieldInternalName;
	}
	if(typeof(highlightColor)!='undefined'){
		bgColor = highlightColor;
	}
	if(typeof(mouseOverOnRow)!='undefined'){
		mouseOver = mouseOverOnRow;
	}
	
	$("table.ms-listviewtable").each(function(){
		var thisListView = $(this);
		// Find colindex of YesNo-field
		thisListView.find(".ms-viewheadertr th").each(function(){
			if($(this).text()==fin){
				colIndex = $(this).attr('cellIndex');		
			}
		});
		
		// Loop trough all rows and highlight matched rows
		thisListView.find("tbody:not([id^='aggr']) tr:has(td.ms-vb2)[highlighted!='1']").each(function(){			
			var rowVal = $(this).find("td[cellIndex='"+colIndex+"']").text();
			if(rowVal==valToFind){
				$(this).attr({'highlighted':'1','title':mouseOver}).css({'background-color':bgColor});		
			}		
		});
	});
}

// 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);
highlightRowByYesNoField();
}
SharePoint 2010
/* Highlight row in list view based on value in a Yes/No-column
 * ---------------------------------------------
 * Created by Alexander Bautz
 * alexander.bautz@gmail.com
 * https://spjsblog.com
 * v1.1 for SharePoint 2010
 * LastMod: 08.10.2011
 * ---------------------------------------------
 * Include reference to jquery - http://jquery.com
 * ---------------------------------------------
 * Call from a CEWP below the list view with this code:
*/

function highlightRowByYesNoField(valToTriggerHighlighting,FieldInternalName,highlightColor,mouseOverOnRow){
	var fName;
	if(typeof(valToTriggerHighlighting)!='undefined'){
		valToFind = valToTriggerHighlighting;
	}
	if(typeof(FieldInternalName)!='undefined'){
		fin = FieldInternalName;
	}
	if(typeof(highlightColor)!='undefined'){
		bgColor = highlightColor;
	}
	if(typeof(mouseOverOnRow)!='undefined'){
		mouseOver = mouseOverOnRow;
	}
	$("table.ms-listviewtable").each(function(){
	
		var thisListView = $(this);
		// Find colindex of YesNo-field
		thisListView.find(".ms-viewheadertr th").each(function(){				
			fName = $(this).find('div:first').attr('name')
			if(fName==fin){
				colIndex = this.cellIndex;
			}
		});
		// Loop trough all rows and highlight matched rows
		thisListView.find("tbody:not([id^='aggr']) tr:has(td.ms-vb2)[highlighted!='1']").each(function(){
			var rowVal = $(this).find(">td:eq("+colIndex+")").text();
			if(rowVal==valToFind){
				$(this).attr({'highlighted':'1','title':mouseOver}).css({'background-color':bgColor});
			}
		});
	});
}

function customTimeoutLoop(id){
var obj = $("#"+id);
var isloaded = ($(obj).attr('isloaded')=='true')?true:false;
	if(!isloaded){
		$(obj).hide();
		setTimeout(function(){
			customTimeoutLoop(id);
		},10);
	}else{
		$(obj).show();
		highlightRowByYesNoField();		
	}
}

function ExpGroupRenderData(d, a, e) {
    ULSA13: {
    }
    var c = document.getElementById("tbod" + a + "_"), b = document.createElement("DIV"), f = a.split("-");
    b.innerHTML = "<TABLE><TBODY id="tbod" + a + "_" isLoaded="" + e + "">" + d + "</TBODY></TABLE>";
    c.parentNode.replaceChild(b.firstChild.firstChild, c); 
 customTimeoutLoop("tbod" + a + "_");   
}

Save the code as a file named “HighlightRowByYesNoColumn.js” – mind the file extension, and upload to the scriptlibrary as shown above.

Note when combining with other scripts that embeds the function “ExpGroupRenderData”: The function “ExpGroupRenderData” is not to be added twice. You must leave the one in the script called last, and include the function call to both/all custom function that is to be triggered by expanding grouped view’s – like in line 58 in the SP2007-code above.

Ask if something is unclear

Regards
Alexander

Show current row wrapped in a floating table when hovered

This one is an attempt to answer Charlie’s request:

Hi Alexander:
On several of my lists, I struggle with the width of the page and too many fields. I use some simple javascript to wrap the column headers but I still need to prevent users from having to scroll side to side…

IMG

This script adds a mouse over preview of the current row in a floating DIV. It is related to the script Preview metadata in list view on mouseover, but is simpler as it does only wrap up the content of the current row.

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”):
IMG

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.

Add a CEWP below the list view and add this code:

&lt;script type=&quot;text/javascript&quot; src=&quot;/test/English/Javascript/jquery-1.3.2.min.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;/test/English/Javascript/mouseOverWrapRowTableStyle.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
// Initiate mouse over when hovered for 1000 milliseconds
initShowRowAsTable(1000);
&lt;/script&gt;

This is the sourcecode for the file “mouseOverWrapRowTableStyle.js”:

function initShowRowAsTable(delay){
	if(typeof(delay)=='number'){
		hoverDelay = delay;
	}
	if(typeof(hoverDelay)=='undefined'){
		hoverDelay = 500;
	}
	// Add a &quot;description&quot; in the list view toolbar
	if($(&quot;#hoverDelayInfo&quot;).length==0){
		var str = &quot;Hover over a row for &quot; + hoverDelay/1000 + &quot; second to view content &quot;table-style&quot;&quot;;
		$(&quot;td.ms-toolbar[width='99%']&quot;).append(&quot;&lt;div id='hoverDelayInfo' class='ms-listheaderlabel' style='text-align:center;margin-top:-15px'&gt;&quot; + str + &quot;&lt;/div&gt;&quot;);
	}

	// Build object of all headings
	if(typeof(objTH)=='undefined'){
		objTH = {};
		$(&quot;.ms-viewheadertr th&quot;).each(function(){
		var colIndex = $(this).attr('cellIndex');
			if($(this).text()!='' ){
				objTH[colIndex] = $(this).text();
			}
		});
	}

	$(&quot;table.ms-listviewtable tbody:not([id^='aggr']) tr:has(td.ms-vb2)[beenthere!='1']&quot;).each(function(){	
		$(this).attr('beenthere','1');
		// Add hover function
		$(this).hover(function(e){
		// Add highlighting of &quot;hovered&quot; row
		$(this).addClass(&quot;ms-dialogHoverRow&quot;);	
		var row = $(this);
		// If hovered more than &quot;hoverDelay&quot; - show contents
		setTimeout(function(){
				if(row.hasClass('ms-dialogHoverRow')){
					$(&quot;#customHoverDiv&quot;).remove();
					pX = e.pageX + 10;
					pY = e.pageY + 15;	
					showRowAsTable(row);
				}
			},hoverDelay);
		},function(){
			// Remove highlighting of &quot;hovered&quot; row
			$(this).removeClass(&quot;ms-dialogHoverRow&quot;);
			// Remove floating div
			$(&quot;#customHoverDiv&quot;).remove();
		});
	});
}

function showRowAsTable(obj){
	// Create new hidden DIV	
	var newDiv = $(&quot;&lt;div style='display:none'&gt;&lt;/div&gt;&quot;).attr('id','customHoverDiv').html(obj.html()).appendTo(&quot;.ms-bodyareaframe&quot;);
	str = '';
	
	// Extract the contents of the row and build a new table
	newDiv.find('&gt;td').each(function(idx){
		if(objTH[idx]!=undefined){
			if($(this).attr('class')=='ms-vb-title'){
				var value = $(this).text();
			}else{
				var value = $(this).html();
			}
			str += &quot;&lt;tr&gt;&lt;td class='ms-formlabel' valign='top'&gt;&quot; + objTH[idx] + &quot;:&lt;/td&gt;&lt;td class='ms-formbody'&gt; &quot; + value + &quot;&amp;nbsp;&lt;/td&gt;&lt;/tr&gt;&quot;;		
		}
	});
	
	// Replace the DIV-content with the newly buildt table
	newDiv.html(&quot;&lt;table cellpadding='0' cellspacing='0' class='ms-formtable'&gt;&quot; + str + &quot;&lt;/table&gt;&quot;);
	var contentWidth = '400';
	var contentHeight = $(&quot;#customHoverDiv&quot;).height();			
	var winHeight = $(window).height();
	var winWidth = $(window).width();
	var winScroll = $(window).scrollTop();
	// Calculate the best position for the popup Y-axis
	if((winHeight - pY) &lt; contentHeight){
		if((pY - winScroll) &lt; contentHeight){
			pY = winScroll + 10
		}else{
			pY = (pY - contentHeight) - 30
		}
	}
	// Calculate the best position for the popup X-axis
	if((winWidth - pX) &lt; contentWidth){
		pX = (pX - contentWidth) - 30;
	}
	// Show popup			
	newDiv.css({'position':'absolute',
		'left':pX,
		'top':pY,
		'background-color':'f8f8ff',
		'border':'2px silver ridge',
		'padding':3})
	.show().mouseenter(function(){
		$(this).hide();
	});
}

// Attaches a call to the function to the &quot;expand grouped elements function&quot; for it to function in grouped listview's
function ExpGroupRenderData(htmlToRender, groupName, isLoaded){
	var tbody=document.getElementById(&quot;tbod&quot;+groupName+&quot;_&quot;);
	var wrapDiv=document.createElement(&quot;DIV&quot;);
	wrapDiv.innerHTML=&quot;&lt;TABLE&gt;&lt;TBODY id=&quot;tbod&quot;+groupName+&quot;_&quot; isLoaded=&quot;&quot;+isLoaded+&quot;&quot;&gt;&quot;+htmlToRender+&quot;&lt;/TBODY&gt;&lt;/TABLE&gt;&quot;;
	tbody.parentNode.replaceChild(wrapDiv.firstChild.firstChild,tbody);
initShowRowAsTable();
}

Save this as a file – name it “mouseOverWrapRowTableStyle.js”, and upload it to your script library as shown above.

Ask if something is unclear.

Alexander