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.

image

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.

image

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.

image

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.

image

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.

image

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.

 image

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.

image

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>
  1. Erick Souza
    October 28th, 2009 at 18:58
    Reply | Quote | #1

    Thanks Boris, your blog is the best of sharepoint cutomizations… you have anything about caml querys??
    i have trouble on this.

    thanks in advance.

  2. Boris Gomiunik
    November 2nd, 2009 at 00:21
    Reply | Quote | #2

    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

  3. Erick Souza
    November 3rd, 2009 at 19:10
    Reply | Quote | #3

    Hi, Borris!!

    Thanks this solves my problem!!!

  4. Eddie
    November 24th, 2009 at 16:10
    Reply | Quote | #4

    What about Lookup with Filtered Values?
    This is an option on field types

  5. Boris Gomiunik
    December 14th, 2009 at 08:35
    Reply | Quote | #5

    Hi, Eddie! This is a topic for one of my next articles.

  6. vaishnavi
    January 14th, 2010 at 01:53
    Reply | Quote | #6

    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.

  7. Boris Gomiunik
    January 25th, 2010 at 09:38
    Reply | Quote | #7

    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).

  8. Scott
    January 28th, 2010 at 18:12
    Reply | Quote | #8

    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. ???

  9. Scott
    January 28th, 2010 at 21:06
    Reply | Quote | #9

    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!

  10. Alpa
    February 2nd, 2010 at 08:46

    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

  11. Boris Gomiunik
    February 3rd, 2010 at 21:30

    @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.

  12. Costin
    February 25th, 2010 at 15:06

    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…

  13. Boris Gomiunik
    March 1st, 2010 at 20:16

    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

  14. March 24th, 2010 at 21:38

    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.comSharepoint Lookup Field

    • Boris Gomiunik
      March 25th, 2010 at 22:51

      Useful. Thank you for sharing. I see you’re starting a new blog. Great job. :)

  15. Mike Culp
    April 1st, 2010 at 21:35

    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?

  16. Hozcar
    April 6th, 2010 at 03:17

    Hermano. Excelente este artículo. Muy bacana la información de tu página!

  17. May 31st, 2010 at 10:20

    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

  18. dax martin
    July 8th, 2010 at 04:23

    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!