SharePoint Lookup field: how does it work and how to add JavaScript event handler function to it?
Due to a lot of comments and questions to my last year’s post about Attaching functions to SharePoint form fields concerning especially lookup fields on which the demo was made, I’ve decided to take a deeper dive into SharePoint’s Lookup Field and how to manipulate it client-side, since it behaves different in different situations. On the image below you can see two lookup fields – rendered different.
Even though new SharePoint is about to go public beta I’ll still show how to handle this in SharePoint 3.0 or 2007
How does it work?
Before explaining how to attach a functon or event handler to a SharePoint lookup field let’s take a look on how SharePoint lookup field is rendered in HTML and which is its behavior – considering client-side.
Lookup to a list with less than 20 items:
If your lookup field is reading from a list that has 19 or less items, the lookup field is rendered like a normal HTML dropdown field (HTML <SELECT> tag). The options in the select tag present possible choices. The text of the option is the lookup value, the value of the option is the lookup ID which SharePoint uses to properly link to the item.
The select has a title that equals the field display name, so you can easily use the getField function that is published here. So you can also easily bind an “onchange” event to it.
Lookup to a list with 20 or more items
When a list that the lookup field is reading from a list with 20 and more items, the story becomes more complicated. But only in IE. In FireFox and other browsers the lookup field with 20+ items still gets rendered as a SELECT element. In IE the story starts:
When a Lookup field has 20+ items in IE it gets rendered as a simulation of a combo box (SELECT). It is rendered as an INPUT text box with an image beside. The reason for this is to enable “auto suggest”/filtering feature as you type values in the INPUT textbox.
The image next to input element is pretty straightfoward. It has an “onclick” event that triggers the select.
The input field on the other hand has a lot of event handlers bound to it (keypress, keydown, keyup, etc. shown in the screenshot from IE Developer Tools below) that enable the combo box simulation. The input field also has a “Title” attribute that has a value of the field display name.
One of the attributes of the input field is also choices which contains all the ids and values of the lookup separated with the “|” character.
The first time you press a key or click the image next to the input field, a special function creates a SELECT element and positions it under the INPUT element. Each additional keypress when typing into the input field (typing in the lookup value) functions already attached to the input re-create the input with filtered values. The generated SELECT’s ID is determined with an attribute “opt” on the Input field.
The problem on DOM
This DOM presents an issue to attach an “onchange” event to it since the input field already has so many onkeyup, onkeydown, onkeypress,… events. And since the Select element is generated on the fly it doesn’t exist when the page is rendered and there is only one SELECT element for all the lookup fields with 20+ items in the page. So we can’t attach an event handler to it. At least not easily.
How to attach an event handler to a lookup field?
The good news is that the re IS a way to add a function to trigger when the lookup field value has changed. The input field has another attribute – “optHid” which reveals the ID of a hidden input field that contains the selected lookup ID. That on the other hand is free of any event handlers and we can abuse it.
Since we won’t be typing into a hidden field we can’t add an “onkeyup” or “onkeypress” event to it. But we can add an “onpropertychange” event to it. I’ve read somewhere that this is working only in IE, but that’s ok since we are modifying the behavior for IE. Because the hidden field doesn’t have the title attribute we need to refference it indirectly through the input:
document.getElementById(getField('input','[field_display_name']').optHid)
Enough theory – let’s make a practical example
Let’s make a simple example. When you change a lookup field its ID and text should be coppied to another text field.
Because the SharePoint field can be rendered in two different ways we need to predict both. What we will need first is slightly modified function getField.
function getField(fieldType,fieldTitle) {
var docTags = document.getElementsByTagName(fieldType);
for (var i=0; i < docTags.length; i++) {
if (docTags[i].title == fieldTitle) {
return docTags[i];
} }
return false;
}
The modification is in bold. What this change does is that the function returns false if it can’t find the specified element. Like that we can use it to specify if the field is INPUT (with 20+ items) or SELECT (with 19- items).
function copyLookupIdAndTxt() {
if(getField('select','20+ Lookup'))
{
//if lookup has 19 or less items - SELECT
lookupField = getField('select','20+ Lookup');
lookupSelectedItem = lookupField.options[lookupField.selectedIndex];
getField('input','Title').value = lookupSelectedItem.value + "-" + lookupSelectedItem.text;
}
else
{
//if it has 20 or more items - INPUT
lookupFieldText = getField('input','20+ Lookup');
lookupFieldId = document.getElementById(lookupFieldText.optHid);
getField('input','Title').value = lookupFieldId.value + "-"+lookupFieldText.value;
}
}
Next we create a function that will attach the created function to the lookup field (select or hidden input)
function addHandler() {
if(getField('select','20+ Lookup'))
{
getField('select','20+ Lookup').onchange = function() { copyLookupIdAndTxt() }
}
else
{
document.getElementById(getField('input','20+ Lookup').optHid).onpropertychange = function() { copyLookupIdAndTxt() }
}
}
and finally to make sure the handler gets added let’s add this to the spbodyonloadfunctionnames array
_spBodyOnLoadFunctionNames.push('addHandler');
and the function is adapted to SharePoint’s lookup field.
To recap: the entire code is as follows:
<script type="text/javascript">
function getField(fieldType,fieldTitle) {
var docTags = document.getElementsByTagName(fieldType);
for (var i=0; i < docTags.length; i++) {
if (docTags[i].title == fieldTitle) {
return docTags[i];
}
}
return false;
}
function copyLookupIdAndTxt() {
if(getField('select','20+ Lookup'))
{
//if lookup has 19 or less items - SELECT
lookupField = getField('select','20+ Lookup');
lookupSelectedItem = lookupField.options[lookupField.selectedIndex];
getField('input','Title').value = lookupSelectedItem.value + "-" + lookupSelectedItem.text;
}
else
{
//if it has 20 or more items - INPUT
lookupFieldText = getField('input','20+ Lookup');
lookupFieldId = document.getElementById(lookupFieldText.optHid);
getField('input','Title').value = lookupFieldId.value + "-"+lookupFieldText.value;
}
}
function addHandler() {
if(getField('select','20+ Lookup'))
{
getField('select','20+ Lookup').onchange = function() { copyLookupIdAndTxt() }
}
else
{
document.getElementById(getField('input','20+ Lookup').optHid).onpropertychange = function() { copyLookupIdAndTxt() }
}
}
_spBodyOnLoadFunctionNames.push('addHandler');
</script>



Thanks Boris, your blog is the best of sharepoint cutomizations… you have anything about caml querys??
i have trouble on this.
thanks in advance.
Hello, Erick! Thanks for those kind words. I usually build CAML queries with U2U CAML Builder tool. You can find info about it on http://www.u2u.info/Blogs/Patrick/Lists/Posts/Post.aspx?ID=1315
and download it here: http://www.u2u.be/Res/Tools/CamlQueryBuilder.aspx
Hi, Borris!!
Thanks this solves my problem!!!
What about Lookup with Filtered Values?
This is an option on field types
Hi, Eddie! This is a topic for one of my next articles.
Hello Boris,
Need some help from you. I know you are extremely busy as is evident by such complex work you are engaged in. I need some help with sharepoint list.
I wanted to implement a simple sharepoint list solution.
I have a list with a column “status” and “last modified date”
Now I want to check the status of the various entries in that list and based on that it will compare the difference between “last modified date” and today’s date and display different traffic light symbol in a third column say “signal”.
I came across many solutions using but not what I was looking for and when I tried to implement it i am not able to get the desired results.
Unfortunately SPD is blocked in my company intranet and hence cant use any of those.
Can you please suggest me a simple yet powerful solution to resolve this please?
I hope you reply
Thanks in advance
Sid
Sorry for not responding for so long. If SPD is not allowed the only other alternative is to create a custom webpart or implement the solution with JavaScript by adding a Content Editor WebPart to the page. The way I’d do it is to loop through the rows of the html table that holds the list view, on each parse dates with JavaScript and insert a column with the “traffic lights”
Hi,
Very nice post.
I am trying to do something similar for lookup field and textbox.
I am trying to fire an event when user enters value in textbox and comparing it all values in lookup field.
If value entered by user exists in lookup field then a message will be thrown. “Value already exists”.
I am using the document.getElementId(lookupfield.optHid);
but it does not take care of all the values in lookup field.
Could you please suggest me what i might be doing wrong?
Any help is appreciated.
-Thanks.
Hi. If you’re comparing from text field, you don’t need to bother with attaching an event to lookup. You’d need to attacht the event to the text field (described in http://www.sharepointboris.net/2008/04/add-functions-and-events-to-sharepoint-form-fields/ – but you can use the function on a text field). What you can do is to parse the lookup field’s “choices” attribute or options (if lookup has less than 20 items).
I have an issue with doing batch updates via the webservice in MOSS 2007 with Lookup field with more than 19 values in that lookup list. I am trying to update a list that has a column that is a lookup field (has more than 20 records) when I pass the value this specific field fails. HOwever when I reduce that underlying lookup list to 19 records I am able to update the other list no problem. ???
Ignore my last post, the issue was resolved after recreating the site column and look up field. Had to scrub the data in that field to not include special character!
hi,
I need to add a separate column for attachments saying(account map), i have a requirement where the attachment should be the mandatory field. I have converted the custom list into data view & done certain modification for the same but cannot achieve the required result as i am facing lot of problems with it. Can u suggest me some solution for ths.
Thanx,
Alpa
@Alpa: I’m not sure I understand the problem. If you’re trying to surface attachments to a field it’s needed to be done with a custom event receiver.
Hi ,
Is there a way to copy the value selected into a lookup column, into another column ( maybe hidden)( on the same row)? I can’t find a way to use the value selected in a lookup column…
SharePoint Designer team blog has a post on how to manipulate a lookup field:
http://blogs.msdn.com/sharepointdesigner/archive/2007/06/13/using-javascript-to-manipulate-a-list-form-field.aspx
Hi,
i would like to offer tutorial on how to create basic sharepoint lookup field within visual studio. here is a tutorial with screenshots and comments. it could help to someone.
http://sharepoint-anthony.blogspot.com – Sharepoint Lookup Field
Useful. Thank you for sharing. I see you’re starting a new blog. Great job.
Hi Boris,
I like this article. Quick ? about Lookup field
We have sharepoint designer workflows and custom forms referencing a lookup field. Any ideas on how to change the lookup field to single line of text without having to rework the workflows and custom forms?
Hermano. Excelente este artículo. Muy bacana la información de tu página!
Hi…
Thanks for an interesting article. By the way, is it possible to create a lookup field of a column in another web? The two lists are in different subwebs in my site. The GUI only suggests lists in the same location as the targetlist.
Thanks in advance/Jesper Wilfing
i would like to set the value of a lookup field base on a selected value from another field. is it possible? any idea how?
thanks!
I’m trying to use this, but I came upon a situation. If I have multiple forms on the page, and the two forms have a field with the same name, how do I differentiate? For example, if I had TWO new item forms, and they each had a column named “20+ Lookup”.
Or is there any way to just tell SharePoint to always use a select box? My code stopped working once I added more than 20 items. It would be so much simpler if I could just switch it back to a regular select tag.
Hi,
I am a newbee in sharepoint.
I am facing the similar issue.
Could you please tell me where to write this getField function, I can’t find it in core.js.
Does this function need to be written in Newform or EditForm. If so, could you please suggest an alternate approach as we cannot have custom newform or editform.
Thanks,
Mitu
@Mitu: Here’s an article explaining on how to add the code: http://www.sharepointboris.net/articles/the-power-of-content-editor-webpart/inserting-cewp-and-managing-code/
I have a field named link that contains a URL of an internal site. But if another field closed is set to true then I want to set the text in SPD 2003 to CLOSED else display the URL in field link.
How do I do that? Thanks much.
Hi, I have one problem,Iam created one list in sharepoint2007 With cols (name , list) ,In another list Contain column (list Lookup).my next step is to edit the list column name that time it checks the the column name is allready used in next feild or not ,once it will used send error other wise update
thanks
how it will possible,please tell me in clear steps.
pllllllllllllzzzzzzzzzzzz
Thank you so much for this great explanation of how to attach a javascript event handler to a SharePoint Lookup Field!
I’m trying to leverage this to modify the rendered select element (for 20+ items) so that it closes in a single-click rather than double-click. I’m sure it must be possible, but how?
I figure I need to watch for the property change, then attach my onClick function to the newly created select element, but I’m having trouble determining what the newly rendered item’s ID is/will be. Any help would be much appreciated.
Hi, and thanks for the article. Do you think it’s possible to make the sharepoint lookup field that has more than 20 items behave like it does in Firefox, i.e. not but so they can’t type in character by character?
sorry it formatted the tags, i asked to behave like a ‘select’ vs. ‘input’ so users would not be able to type in letter by letter
The event seems to be fired many times (also onload), not only when you click the dropwdown. Also fired when start writing and filtering the values, not only on doubleclick. Have you experienced this problem?
Thanks very much for this. It may be useful in some situations however it is still a bit of a nightmare.
The onpropertychange event fires several times during a dropdown change. You also can’t use this information to set another dropdown as (I think) only one of them can be on the go at a time. In the end I’ve had to hook my onchange to another field (could have used a button) I guess. I’ve got a car and I’m copying values between the Near Side Front and the other wheels. In the end I’ve had to use do something like the following which opens up each dropdown in turn, sets it value and shuts it down. This big dropdowns are a nightmare, I wish they hadn’t used them:
var myNSFWidth = getField(‘input’,'NSF Width’)
ShowDropdown(myNSFWidth.id); //this function is provided by SharePoint
var optNSF=document.getElementById(myNSFWidth.opt);
var optNSFIndex=optNSF.selectedIndex;
OptLoseFocus(optNSF); //this function is provided by SharePoint
var myOSFWidth = getField(‘input’,'OSF Width’)
ShowDropdown(myOSFWidth.id); //this function is provided by SharePoint
var optOSF=document.getElementById(myOSFWidth.opt);
optOSF.selectedIndex = optNSFIndex;
OptLoseFocus(optOSF); //this function is provided by SharePoint
A link to understand look up column
http://www.fewlines4biju.com/2011/05/look-up-column-in-sharepoint-2010.html
Hi.
Great blog, by the way. I have just started using sharepoint (old dog trying to learn new tricks) and i find myself in need of having a label on a form change dynamically depending on the content of a pulldown on the same form. can this code be applied to this issue, and if so can you point me in the right direction? Thanks in advance
Nice post Boris. Thanks for the clarity and solution.
Cheers.
Hi Boris, great blog! I was looking for Cascading dropdowns and reached your blog, which helped me a lot. But of course, nothing is just as easy as it should be when we get to complicate it… I was able to create all the dropdowns I wanted in one given list (1), and I wanted to get rid of some old fields in the NewForm of this list,without having to delete these fields, for the sake of backup data. Then, I used Sharepoint Designer and created a NewForm2, assigning it to (1). I didn’t like the results, and went back to NewForm. But, this time, the cascading lookups did not work, when before they worked wonderfully. I didn’t do a single change to the code. I tested other sites I created that shared the same javascript code location, and they worked normally. Would you what could have happened?
So I tried this.. it didn’t work.. or my implementation didn’t work.. lol
I tried as is.. then I tried again where I replaced all instances of ’20+ Lookup’ with the display name of my column .. like ‘Companies Lookup’ or something like that..
I’m using sharepoint 2010..
also.. there was a little adlib .. I didn’t have a ribbon in the editform.aspx to get to the edit html source.. so I made a new page.. added a content editor web part.. edited the source.. pasted the code.. then I exported the webpart, uploaded it with a new name.. and on the editform.aspx page, I added the custom content editor web part.. I also tried moving the webpart below the editform webpart that is on the editform.aspx page.
ok.. hold up.. I don’t think I have a reference to the jquery library in the master page.. or I think that is missing.. I’m looking into how to do that.. and redeploy my master page..
ok, I put the 1.7 jquery library in the layouts folder where the 1.4.4 one was, updated the link in the masterpage to reference the new library.. and re deployed.. I’m not noticing any change on the lookup field with over 20 list items.. and there are two lookup columns on the editform.aspx page..
I cannot see the images above? Did you remove them?
Thanks for pointing out. I’ve updated the article.
ChildLookupTargetField is Calculated so it apper like string;#name1, name2
Yes, calculated field values are being served as type;#value, so you have to parse this value (.split(“;#”)[1])
I have two formfield on same page with same title, only Id is different. Both have 20+ items so create INPUT tag on client side. I am not able to attach onchange event on both of them. We have only 1 optHid field for both. So how will i differentiate? Thanks in advance.
You could use the “getField” function to get the field by its title. (www.sharepointboris.net/js/library) from there on you can reference. Don’t reference the select element if you have more than 20 elements, but the input field.
It didn’t work for me….The change event on dropdown doesn’t fire at all..Here is my code….
function getField(fieldType,fieldTitle) {
var docTags = document.getElementsByTagName(fieldType);
for (var i=0; i < docTags.length; i++) {
if (docTags[i].title == fieldTitle) {
return docTags[i]
}
}
return false;
}
function copyLookupIdAndTxt() {
if(getField('select','Nominee-Name'))
{
//if lookup has 19 or less items – SELECT
lookupField = getField('select','Nominee-Name');
lookupSelectedItem = lookupField.options[lookupField.selectedIndex];
getField('input','Title').value = lookupSelectedItem.value + "-" + lookupSelectedItem.text;
}
else
{
//if it has 20 or more items – INPUT
lookupFieldText = getField('input','Nominee-Name');
lookupFieldId = document.getElementById(lookupFieldText.optHid);
getField('input','Title').value = lookupFieldId.value + "-"+lookupFieldText.value;
}
}
function addHandler() {
if(getField('select','Nominee-Name'))
{
getField('select','Nominee-Name').onchange = function() { copyLookupIdAndTxt() }
}
else
{
document.getElementById(getField('input','Nominee-Name').optHid).onpropertychange = function() { copyLookupIdAndTxt() }
}
}
_spBodyOnLoadFunctionNames.push('addHandler');
Brilliant article, thank you so much. I am working on my first full-fledged custom item list as an issue tracker with a built-in discussion thread. I have ran into many unexpected issues with sharepoint 2007 (wss 3) that I’ve never experienced with other CMS, and this is the most accurate, well written article I have read thus far.
I made the mistake of converting the initial list to xslt, which has broken the list item links from list views (now links point to root.site.com/?ID=1 instead of root.site.com/child/site/lists/example/dispform.aspx?ID=1… I’ve found multiple posts online explaining claimed ways of fixing this, but none of them seem to work. This lists already has hundreds of items, so I would prefer avoiding having to create a new list. Any suggestions?
Brilliant article, thank you so much. I am working on my first full-fledged custom item list as an issue tracker with a built-in discussion thread. I have ran into many unexpected issues with sharepoint 2007 (wss 3) that I’ve never experienced with other CMS, and this is the most accurate, well written article I have read thus far.
I made the mistake of converting the initial list to xslt, which has broken the list item links from list views (now links point to root.site.com/?ID=1 instead of root.site.com/child/site/lists/example/dispform.aspx?ID=1… I’ve found multiple posts online explaining claimed ways of fixing this, but none of them seem to work. This lists already has hundreds of items, so I would prefer avoiding having to create a new list. Any suggestions?