Pages

Wednesday, June 16, 2010

SharePoint Lookup Fields with 20 or more items - Single Click

I recently ran into an issue with SharePoint Lookup Columns containing 20 or more items. Many of you might have already noticed that the with the above criteria the control renders differently (this is the default feature). This is to provide ease to the users to filter the data as and when you enter the data.

With the above criteria the control renders as Input Type followed by an Image. So far we have discussed about the way it renders, now lets get into details of the issue and its solution.


Issue Description:

Whenever, we click on that image it displays us with a drop-down containing all the values, but the funniest part is you can select a particular item either by double click or by selecting it once and clicking it else where on the page or by tabbing system of the keyboard. But I want it work like a normal drop-down selection in other words just with a single click and also the control should be closer to other controls from usability perspective.

Solution:

The above functionality can be achieved by making few changes to the CORE.JS file available in the layouts folder, but being a SharePoint developer I wouldn't recommend doing that for many reasons. We may also want to have this only for few pages and not all. So what are the other options we have ? I would choose to override the function which causes this problem in just one page or the required pages. In order to achieve the above functionality we have to follow few steps:

1) First have "Defer" Tag Set to "False" (it is where you reference the core.js file, it can either be in the master page or the current page itself)
SharePoint:ScriptLink language="javascript" name="core.js" Defer="false" runat="server"/>

Defer = "true" specifies that the page need not wait for the script to be loaded, with this if we have to override the CORE.JS function with ours it may not be possible, so we need to set this to "false".

2)Look for the function "FilterChoice" in CORE.JS file, (which has to be overridden, as the double click functionality is handled in this script). Copy this function and place it in your page where you want this functionality to reflect.

3) After copying the code find "ondblclick" and replace it with "onclick" (marked in Red below) this would suffice our functionality of single click.

4) With this it looks like we are all set to use it, but here comes another problem a small JavaScript error ""id" is null or not an object" on the left hand side of the progress bar. The functionality would still work but this error may quite annoying to few users.

5) This happens because when we push this overridden function (that is FilterChoice) it cannot recognize the parameters passed such as "opt", "ctrl" etc and the function uses the parameters attributes without checking if those are actually defined objects. once we add that condition we are all set to use it without even a hint of error. The condition added is marked in brown below.

I have added the updated function below for reference.

_spBodyOnLoadFunctionNames.push("FilterChoice");


function FilterChoice(opt, ctrl, strVal, filterVal)
{
if(typeof(opt) != "undefined")
{
var i;
var cOpt=0;
var bSelected=false;
var strHtml="";
var strId=opt.id;
var strName=opt.name;
var strMatch="";
var strMatchVal="";
var strOpts=ctrl.choices;
var rgopt=strOpts.split("|");
var x=AbsLeft(ctrl);
var y=AbsTop(ctrl)+ctrl.offsetHeight;
var strHidden=ctrl.optHid;
var iMac=rgopt.length - 1;
var iMatch=-1;
var unlimitedLength=false;
var strSelectedLower="";
if (opt !=null && opt.selectedIndex >=0)
{
bSelected=true;
strSelectedLower=opt.options[opt.selectedIndex].innerText;
}
for (i=0; i < i=""> {
var strOpt=rgopt[i];
while (i < length="="> {
strOpt=strOpt+"|";
i++;
if (i <>
{
strOpt=strOpt+rgopt[i+1];
}
i++;
}
var strValue=rgopt[i+1];
var strLowerOpt=strOpt.toLocaleLowerCase();
var strLowerVal=strVal.toLocaleLowerCase();
if (filterVal.length !=0)
bSelected=true;
if (strLowerOpt.indexOf(strLowerVal)==0)
{
var strLowerFilterVal=filterVal.toLocaleLowerCase();
if ((strLowerFilterVal.length !=0) && (strLowerOpt.indexOf(strLowerFilterVal)==0) && (strMatch.length==0))
bSelected=false;
if (strLowerOpt.length > 20)
{
unlimitedLength=true;
}
if (!bSelected || strLowerOpt==strSelectedLower)
{
strHtml+=""+STSHtmlEncode(strOpt)+"";
bSelected=true;
strMatch=strOpt;
strMatchVal=strValue;
iMatch=i;
}
else
{
strHtml+=""+STSHtmlEncode(strOpt)+"";
}
cOpt++;
}
}
var strHandler=" onclick=\"HandleOptDblClick()\" onkeydown=\"HandleOptKeyDown()\"";
var strOptHtml="";
if (unlimitedLength)
{
strOptHtml=" < tabindex="\" ctrl="\" name="\" id="\">
}
else
{
strOptHtml=" < class="\" tabindex="\" ctrl="\" name="\" id="\">
}
if (cOpt==0)
{
strOptHtml+=" style=\"display:none;position:absolute;z-index:2;left:"+x+ "px;top:"+y+ "px\" onfocusout=\"OptLoseFocus(this)\">";
}
else
{
strOptHtml+=" style=\"position:absolute;z-index:2;left:"+x+ "px;top:"+y+ "px\""+ " size=\""+(cOpt <=8 ? cOpt : 8)+"\""+ (cOpt==1 ? "multiple=\"true\"" : "")+ " onfocusout=\"OptLoseFocus(this)\">"+ strHtml+ "";
}
opt.outerHTML=strOptHtml;
var hid=document.getElementById(strHidden);
if (iMatch !=0 || rgopt[1] !="0" )
hid.value=strMatchVal;
else
hid.value="0";
if (iMatch !=0 || rgopt[1] !="0" )
return strMatch;
else return "";
}
}


Now lets look at the usability part, of replacing that image with something which is closer to other drop-downs. I know the input type box has some broader dimensions, but I am not showing how to change its dimensions (I haven't tried this, but I am pretty sure it can be done using JavaScript and CSS ), all I'll show is to replace the image, next to the input box.

Below is the JavaScript function used, it is very straight forward. However, you can add few more conditions to restrict it to only few images.

_spBodyOnLoadFunctionNames.push("fillDefaultValues");

function fillDefaultValues() {
var vImg = document.getElementsByTagName("img");
if(vImg.length>0)
{

for(i=0; i <>
{
if(vImg[i].alt == "Display lookup values")
{
vImg[i].src = "Give your Image URL here";
}
}
}

That is it, and you are all set to go.. Finally I would like to thank Hari, a friend of mine who actually wanted this functionality to be achieved and I am glad I could help.

Thanks for visiting my blog!!

Note: For some reason this editor doesn't show the proper data, specially the script with Select Tag. I would recommend you to copy the script from CORE.JS and make the necessary modifications as suggested in this blog

No comments:

Post a Comment