31.03.2010 updated the code and reworked the article:
This solution enables you to edit a column of type “Date and Time (Date Only)”, “Single line of text”, “Number”, “Currency”, “Yes/No” and “single choice people picker” directly in a list view.
I’m planning on updating with support for columns columns later on.
Double click on a “TD” to edit – this action is per “TD”.
In this picture all “TD’s” are double clicked on.
The people picker is created with the jQuery UI widget “Autocomplete”.
When saved OK – a picture is appended to indicate “Save OK”.
A different picture is appended on save error.
This can be used in lists and document library’s, but requires that a the ID column is in the view (it can be hidden in the script by setting the argument “hideIdColumn” to true).
I have added support for “date”, “single line text”, “number”, “currency”, “boolean” and “single choice people picker” columns. I will update this article with support for choice columns later on.
All these columns – for filtering purposes – supply their FieldInternalName in the list header. This way i do not have to specify the column index where it is found – it dynamically adapts to changing column order.
This solution can be used with plain list view’s and 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”):
In addition to the above scripts, i have the jQuery UI 1.8 in a separate folder. See the CEWP code and point the links to your jQuery UI location.
The jQuery UI-library is found here. The pictures and the sourcecode refers to jquery-ui-1.8. The autocomplete widget is not found in previous releases.
The jQuery-library is found here. The pictures and the sourcecode refers to jquery-1.4.min. The autocomplete widget is not supported in previous releases.
The scripts “interaction.js” and “stringBuffer.js” is created by Erucy and published on CodePlex.
Read here how to find the list Guid of your list, and how to find the FieldInternalName of your columns.
I have made one modification in the file “jquery-ui-1.8.custom.css” to correct the datepicker positioning of the date and year dropdowns. The style “.ui-datepicker select.ui-datepicker-year { width: 49%;}” is modified like this: “.ui-datepicker select.ui-datepicker-year { width: auto;}”. This might not affect your setup, and is merely a detail.
The sourcecode for “EditInListView.js” is provided below.
Add a CEWP below the list view webpart, and add this code:
<style type="text/css">
.ui-menu .ui-menu-item {
font-size:xx-small;
}
</style>
<link type="text/css" href="/test/English/jQueryUI18/smoothness/jquery-ui-1.8.custom.css" rel="stylesheet" />
<script type="text/javascript" src="/test/English/Javascript/jquery-1.4.min.js"></script>
<script type="text/javascript" src="/test/English/jQueryUI18/jquery-ui-1.8.custom.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/EditInListView.js"></script>
<script type="text/javascript">
// Set variables
hoverImgSrc = '/_layouts/images/DOWNARRW.GIF';
hoverImgMouseOver = 'Double click to edit';
successImgSrc = '/_layouts/images/ServiceInstalled.gif';
failureImgSrc = '/_layouts/images/ServiceNotInstalled.gif';
dateFormat = 'm/d/yy'; // alternative d.m.yy
decimalSeparator = '.'; // The symbol used to mark the boundary between the integral and the fractional parts of a decimal number
boolYesNoText = 'Yes|No'; // The display text in list view for a Yes/No-field
userListGuid = '570D772F-0EAB-45A8-8C54-9CCD4EC6A0AF';
userListBaseUrl = ''
arrToEdit = ['MyDateField','SingleLine','Number','YesNo','MyPeoplePicker'];
// Call function
initCustomEditFunction(true);
</script>
Edit the array “arrToEdit” to hold your FieldInternalNames, the “userListGuid” to hold your list guid, and other parameters if necessary.
Parameter’s explained:
- hoverImgSrc: The source of the image indicating editable field
- hoverImgMouseOver: The mouse over on the “hoverImgSrc-image”
- successImgSrc: The source of the image displayed when saving the new value succeeds
- failureImgSrc: The source of the image displayed when saving the new value fails
- dateFormat: ‘m/d/yy’ – alternative ‘d.m.yy’
- decimalSeparator: The symbol used to mark the boundary between the integral and the fractional parts of a decimal number
- boolYesNoText: The display text in list view for a Yes/No-field – format: Yes|No
- userListGuid: The list guid for the userlist, used for the people picker
- userListBaseUrl: Base URL for the list “People and Groups”. If in a managed path, reflect this path, else set to “” (blank string)
- arrToEdit: Array of FieldInternalNames to address
The sourcecode for the file “EditInListView.js” looks like this:
/* Edit date, single line text, number, currency, boolean or single choice people picker directly in list view
* ---------------------------------------------
* Created by Alexander Bautz
* alexander.bautz@gmail.com
* https://spjsblog.com
* v1.1
* LastMod: 31.03.2010
* ---------------------------------------------
* Must include reference to:
* jquery-1.4 - http://jquery.com
* jquery-ui-1.8 - http://jqueryui.com/
* interaction.js - http://spjslib.codeplex.com
* stringBuffer.js - http://spjslib.codeplex.com
* ---------------------------------------------
*
* Call from CEWP BELOW the list view like this:
<style type="text/css">
.ui-menu .ui-menu-item {
font-size:xx-small;
}
</style>
<link type="text/css" href="/test/English/jQueryUI18/smoothness/jquery-ui-1.8.custom.css" rel="stylesheet" />
<script type="text/javascript" src="/test/English/Javascript/jquery-1.4.min.js"></script>
<script type="text/javascript" src="/test/English/jQueryUI18/jquery-ui-1.8.custom.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/EditInListView.js"></script>
<script type="text/javascript">
// Set variables
hoverImgSrc = '/_layouts/images/DOWNARRW.GIF';
hoverImgMouseOver = 'Double click to edit';
successImgSrc = '/_layouts/images/ServiceInstalled.gif';
failureImgSrc = '/_layouts/images/ServiceNotInstalled.gif';
dateFormat = 'm/d/yy'; // alternative d.m.yy
decimalSeparator = '.'; // The symbol used to mark the boundary between the integral and the fractional parts of a decimal number
boolYesNoText = 'Yes|No'; // The display text in list view for a Yes/No-field
userListGuid = '570D772F-0EAB-45A8-8C54-9CCD4EC6A0AF';
userListBaseUrl = ''
arrToEdit = ['MyDateField','SingleLine','Number','YesNo','MyPeoplePicker'];
// Call function
initCustomEditFunction(true);
</script>
*/
function initCustomEditFunction(hideIdColumn){
if(typeof(hideIdColumn)!='undefined'){
hideId = hideIdColumn;
$(".ms-viewheadertr th").each(function(){
if($(this).find('table:first').attr('name')=='ID'){
IDcolIndex = $(this).attr('cellIndex');
// Hide ID column
if(hideId){
$(this).addClass('dummyHideClass');
}
}
});
}
if(typeof(IDcolIndex)=='undefined'){
alert("The ID column must be in the view.nYou may hide it in the script call by setting the argument "hideIdColumn" to true.");
}
if(typeof(arrTH)=='undefined'){
arrTH = [];
$(".ms-viewheadertr th").each(function(){
var colIndex = $(this).attr('cellIndex');
var table = $(this).find('table');
if(table.attr('name')!=undefined){
var fldIntName = table.attr('name');
var fldType = table.attr('fieldtype');
if($.inArray(fldIntName,arrToEdit)>-1){
arrTH.push({'colIndex':colIndex,'fldType':fldType,'fldIntName':fldIntName});
}
}
});
}
addCustomEditFunction(arrTH);
// Hide ID column if specified
if(hideId){
$(".dummyHideClass").hide();
}
}
function addCustomEditFunction(arrToInclude){
$("table.ms-listviewtable tbody").each(function(){
if($(this).attr('id').match('aggr')==null){
$(this).find("tr:has(td.ms-vb2)[beenthere!='1']").each(function(){
$(this).attr('beenthere','1');
var itemID = $(this).find(">td[cellIndex=" + IDcolIndex + "]").text();
// Hide ID column
if(hideId){
$(this).find("td[cellIndex="+IDcolIndex+"]").addClass('dummyHideClass');
}
var pTR = $(this);
$.each(arrToInclude,function(idx,obj){
var col = obj['colIndex'];
var type = obj['fldType'];
var intName = obj['fldIntName'];
var TD = pTR.find(">td[cellIndex=" + col + "]");
var currVal = TD.text();
// Add onclick and append image
TD.dblclick(function(){editCurrentLine(type,itemID,intName)})
.attr({'id':'customEdit_'+intName+"_"+itemID,'fieldType':type})
.css({'cursor':'pointer'});
if(type=='Number' || type=='Currency'){
TD.find('div').append("&nbsp;<img style='vertical-align:middle' title='" + hoverImgMouseOver + "' src='" + hoverImgSrc + "'>");
}else if(type=='User'){
if(TD.find('tr').length>0){
TD.find('tr:first').append("<td>&nbsp;<img style='vertical-align:middle' title='" + hoverImgMouseOver + "' src='" + hoverImgSrc + "'></td>");
}else{
TD.append("<div style='padding-left:12px'>&nbsp;<img style='vertical-align:middle' title='" + hoverImgMouseOver + "' src='" + hoverImgSrc + "'></div>");
}
}else{
TD.append("&nbsp;<img style='vertical-align:middle' title='" + hoverImgMouseOver + "' src='" + hoverImgSrc + "'>");
}
});
});
}else if($(this).attr('id').match('aggr')!=null && $(this).attr('beenthere')!='1' && hideId){
$(this).attr('beenthere','1');
$(this).find("td[cellIndex="+IDcolIndex+"]").addClass('dummyHideClass');
}
});
}
function editCurrentLine(type,id,intName){
var currField = $("#customEdit_" + intName+"_"+id);
var currVal = currField.text();
// Remove &nbsp;
currVal = $.trim(currVal.replace(/xA0/g,''));
if(type=='DateTime'){
if(currVal.indexOf(' ')>-1){
currVal = currVal.substring(0,currVal.indexOf(' ')); // Strip off any "time-value"
}
if($("#customEditInput_"+intName+"_"+id).length==0){
currField.css({'width':currField.width()}).html("<div>&nbsp;<a title='Cancel' href='javascript:' onclick='javascript:cancelCustomSaveLine(""+intName+"",""+id+"",""+currVal+"")'>cancel</a>&nbsp;|&nbsp;" +
"<a title='Clear' href='javascript:' onclick='javascript:customSaveLine(""+intName+"",""+id+"",""+currVal+""," + true + ")'>clear</a><br>" +
"<input class='ms-input' id='customEditInput_"+intName+"_"+id+"' style='width:1px;height:1px;border:0px' value='" + currVal + "' /></div>");
// Datepicker
$("#customEditInput_" + intName+"_"+id).datepicker({
dateFormat: dateFormat,
changeMonth: true,
changeYear: true,
showButtonPanel: true,
onSelect: function(dateText, inst){
customSaveLine(intName,id,currVal);
}
}).datepicker('show');
}
}else if(type=='Text' || type=='Number' || type=='Currency'){
if($("#customEditInput_"+intName+"_"+id).length==0){
currField.css({'width':currField.width()}).html("<div>&nbsp;<a title='Save' href='javascript:' onclick='javascript:customSaveLine(""+intName+"",""+id+"",""+currVal+"")'>save</a>&nbsp;|&nbsp;" +
"<a title='Cancel' href='javascript:' onclick='javascript:cancelCustomSaveLine(""+intName+"",""+id+"",""+currVal+"")'>cancel</a><br>" +
"<input class='ms-input' id='customEditInput_"+intName+"_"+id+"' style='width:99%' value='" + currVal + "' /></div>");
// Focus on the input
setTimeout(function(){currField.find('input').focus();},250);
// Add keyup/down function
currField.find('input').keyup(function(e){
// If number - restrict input to numbers only - may need a little more tweaking
if(type=='Number' || type=='Currency'){
var reg = new RegExp("[^0-9|\" + decimalSeparator + "]",'g');
var thisVal = $(this).val().replace(reg,'');
$(this).val(thisVal);
}
}).keydown(function(e){
// Enter = save
if(e.keyCode==13){
customSaveLine(intName,id,currVal)
}
}).keyup();
}
}else if(type=='Boolean'){
if($("#customEditInput_"+intName+"_"+id).length==0){
if(currVal=='' || currVal.toLowerCase().indexOf('n')>-1){ // 'n' is found in 'No' and in Norwegian 'Nei'
chk='';
}else{
chk='checked';
}
currField.css({'width':currField.width()}).html("<div>&nbsp;<a title='Cancel' href='javascript:' onclick='javascript:cancelCustomSaveLine(""+intName+"",""+id+"",""+currVal+"")'>cancel</a><br>" +
"<input type='checkbox' id='customEditInput_"+intName+"_"+id+"' " + chk + "/></div>");
$("#customEditInput_"+intName+"_"+id).click(function(){
customSaveLine(intName,id,currVal)
}).focus();
}
}else if(type=='User'){
if($("#customEditInput_"+intName+"_"+id).length==0){
currField.css({'width':currField.width()}).html("<div>&nbsp;<a title='Cancel' href='javascript:' onclick='javascript:cancelCustomSaveLine(""+intName+"",""+id+"",""+currVal+"")'>cancel</a>&nbsp;|&nbsp;" +
"<a title='Clear' href='javascript:' onclick='javascript:customSaveLine(""+intName+"",""+id+"",""+currVal+""," + true + ")'>clear</a><br>" +
"<input title='Type into the textfield to get a list of users' class='ms-input' id='customEditInput_"+intName+"_"+id+"' style='width:99%' value='" + currVal + "' /></div>");
// Autocomplete
if(typeof(allUsers)=='undefined'){
allUsers = getUsers();
}
currField.find('input').autocomplete({
source: allUsers,
select: function(event, ui){
$(this).attr('hiddenVal',ui.item.userID);
customSaveLine(intName,id,currVal);
return false;
}
});
// Focus on the input
setTimeout(function(){currField.find('input').focus();},250);
}
}
}
function getUsers(){
var query = "<Where><And><IsNotNull><FieldRef Name='EMail' /></IsNotNull>" +
"<Eq><FieldRef Name='ContentType' /><Value Type='Text'>Person</Value></Eq></And></Where>" +
"<OrderBy><FieldRef Name='Title' Ascending='TRUE'/></OrderBy>";
wsBaseUrl = userListBaseUrl + '/_vti_bin/';
var res = queryItems(userListGuid,query,['ID','Title','Name','EMail','ContentType']);
var ret = [];
$.each(res.items,function(idx,item){
ret.push({label:item['Title']+"<br>"+item['EMail']+"<br>"+item['Name'],value:item['Title'],userID:item['ID']});
});
return ret;
}
function cancelCustomSaveLine(intName,id,currVal){
var pTD = $("#customEdit_"+intName+"_"+id);
var type = pTD.attr('fieldType');
if(type=='Number' || type=='Currency'){
pTD.html("<div align='right'>" + currVal + "&nbsp;<img style='vertical-align:middle' src='" + hoverImgSrc + "'></div>");
}else if(type=='User'){
pTD.html("<div style='padding-left:12px'>" + currVal + "&nbsp;<img style='vertical-align:middle' src='" + hoverImgSrc + "'></div>");
}else{
pTD.html(currVal + "&nbsp;<img style='vertical-align:middle' src='" + hoverImgSrc + "'>");
}
}
function customSaveLine(intName,id,currVal,clear){
wsBaseUrl = ctx.HttpRoot + '/_vti_bin/';
listGuid = ctx.listName;
if(clear==undefined)clear=false;
var pTD = $("#customEdit_"+intName+"_"+id);
var type = pTD.attr('fieldType');
var inpField = pTD.find('input');
// Determine type and get value
if(type=='Boolean'){
var bSplit = boolYesNoText.split('|');
var newVal = inpField.attr('checked');
if(newVal){
newVal='1';
newValText=bSplit[0];
}else{
newVal='0';
newValText=bSplit[1];
}
}else{
var newVal = $.trim(inpField.val());
}
// Check if value has changed
if(newVal!=currVal || clear){
var data = {};
if(type=='DateTime'){
if(clear){
data[intName] = '';
newVal = '';
}else{
var isoDate = parseISO8601Date(newVal);
data[intName] = isoDate;
}
}else if(type=='User'){
if(clear){
data[intName] = "";
newVal = '';
}else{
data[intName] = inpField.attr('hiddenVal');
}
}else{
data[intName] = newVal;
}
// Write back data
var res = updateItem(listGuid,id,data);
// Success or failure
if(res.success){ // Save success
if(type=='Number'){
pTD.html("<div align='right'>" + newVal + "&nbsp;<img style='vertical-align:middle' title='Saved' src='" + successImgSrc + "' width='14' height='14' border='0'></div>");
}else if(type=='Boolean'){
pTD.html(newValText + "&nbsp;<img style='vertical-align:middle' title='Saved' src='" + successImgSrc + "' width='14' height='14' border='0'>");
}else if(type=='User'){
pTD.html("<div style='padding-left:12px'>" + newVal + "&nbsp;<img style='vertical-align:middle' title='Saved' src='" + successImgSrc + "' width='14' height='14' border='0'></div>");
}else{
pTD.html(newVal + "&nbsp;<img style='vertical-align:middle' title='Saved' src='" + successImgSrc + "' width='14' height='14' border='0'>");
}
}else{ // Failed to save
if(type=='Number'){
pTD.html("<div align='right'>" + currVal + "&nbsp;<img style='vertical-align:middle' style='vertical-align:middle' title='" + res.errorText + "' src='" + failureImgSrc + "' width='14' height='14' border='0'></div>");
}else if(type=='User'){
pTD.html("<div style='padding-left:12px'>" + currVal + "&nbsp;<img style='vertical-align:middle' title='" + res.errorText + "' src='" + failureImgSrc + "' width='14' height='14' border='0'></div>");
}else{
pTD.html(currVal + "&nbsp;<img style='vertical-align:middle' title='" + res.errorText + "' src='" + failureImgSrc + "' width='14' height='14' border='0'>");
}
}
}else{ // If value is not changed, restore currVal
if(type=='Number'){
pTD.html("<div align='right'>" + newVal + "&nbsp;<img style='vertical-align:middle' title='" + hoverImgMouseOver + "' src='" + hoverImgSrc + "'></div>");
}else if(type=='User'){
pTD.html("<div style='padding-left:12px'>" + newVal + "&nbsp;<img style='vertical-align:middle' title='" + hoverImgMouseOver + "' src='" + hoverImgSrc + "'></div>");
}else{
pTD.html(newVal + "&nbsp;<img style='vertical-align:middle' title='" + hoverImgMouseOver + "' src='" + hoverImgSrc + "'>");
}
}
}
function parseISO8601Date(str){
if(str!=''){
var strSplit = str.split(dateFormat.charAt(1));
switch(dateFormat){
case 'm/d/yy':
return strSplit[2] + "-" + strSplit[0] + "-" + strSplit[1] + "T12:00:00Z"; // set's clock to 12:00 PM
break;
case 'd.m.yy':
return strSplit[0] + "-" + strSplit[2] + "-" + strSplit[1] + "T12:00:00Z"; // set's clock to 12:00 PM
break;
}
}else{
return '';
}
}
// 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);
initCustomEditFunction();
}
// Size of datepicker
$("body").css({'font-size':'55%'});
Save this code as “EditInListView.js”, and upload to your scriptlibrary as shown above.
Note: Due to the number of DOM-modifications there might be a performance issue (prolonged load time of the page) when used.
To minimize the issue you can try:
- Limit the number of columns this feature is applied to
- Limit the item count in each page
- Group your items and set “By default, show groupings: Collapsed”
Test it in your environment to find what settings suites you.
Ask if something is unclear.
Regards
Alexander
Like this:
Like Loading...