From SharePoint Magazine: Variations in Multiselect Controls in Different SharePoint Language Versions

[SharePoint Magazine date=”December 23, 2010″]

Consistency in one’s development practices is a critical part of generating reliable, maintainable code. Most large software contains some inconsistencies, but in your development efforts with SharePoint, you should try your best not to introduce any inconsistencies of your own.

In this article, I’ll show you the effect of one of SharePoint’s little inconsistencies and why it was an issue in my development of SPServices.

If you’ve ever tried to implement jQuery in multi-language SharePoint solutions, you really need to pay attention.

Recently, I had to fix something in SPServices for someone who was using the German version of SharePoint 2010. The issue was with the way some Document Object Model (DOM) elements are identified in the German version. In fact, I’ve run into these inconsistencies before. These inconsistencies can add up to larger bugs not just for people like me who are trying to understand the DOM to make script work, but they probably contribute to issues that arise from installing service packs, hotfixes, and so on.

Whomever did the translations internally to some of the different languages wasn’t consistent in how they made some of the changes to things such as DOM element IDs. The specific case here is with multiselect columns. This inconsistency is the same whether you’re working with SharePoint 2007 or SharePoint 2010.

Anatomy of a Multiselect Column’s Markup

When SharePoint renders the rather complex controls for a multi-select column, it looks something like the image shown here. This is an example from a SharePoint 2010 installation, but it looks basically the same as it does in SharePoint 2007. The control is also the same whether you’re working with a NewForm or an EditForm.

image_thumb4The City column is a multiselect Lookup column. This seemingly simple type of column actually consists of many different DOM elements as well as some fairly complicated underlying JavaScript. (Yes, Virginia, SharePoint uses a lot of JavaScript right out of the box.)

Here is a snapshot of the DOM for the control at a high level:

SNAGHTML4bbbec4_thumbLet’s break that markup down into its components to see how it works:

SNAGHTML4bb3451_thumbBefore we even get to anything visual, there is a set of hidden INPUT elements that the scripts use to manage things. Each of the three INPUT elements have long IDs preceded by [long GUID-based stuff] and ending with the names in the following table:

MultiLookupPicker Used to store the currently selected set of values, using the odd separator ‘|t’, as in ’1|tAbington|t2|tActon|t3|tAcushnet’.
MultiLookupPicker_data Stores the full set of allowable values, like this ’1|tAbington|t |t |t2|tActon|t |t |t3|tAcushnet|t |t ‘ (and so on). It’s not clear why there are extra separators in this one.
MultiLookupPicker_initial Stores the initial value of the column as of page load.

Next comes a table that contains the visual elements of the control:

image_thumb5The left side of the control is a SELECT that has some script attached to its dblclick and change events. The important thing we need to know about this SELECT element is its title. In an English installation, the title=”City possible values”. It’s the column name followed by ‘ possible values’.

<TD class=ms-input>
  <SELECT style="WIDTH: 143px; HEIGHT: 125px; OVERFLOW: scroll" id=ctl00_m_g_856b69b7_94a3_499a_af52_e1789266b73a_ctl00_ctl05_ctl02_ctl00_ctl00_ctl04_ctl00_ctl00_SelectCandidate title="City possible values" ondblclick="GipAddSelectedItems(ctl00_m_g_856b69b7_94a3_499a_af52_e1789266b73a_ctl00_ctl05_ctl02_ctl00_ctl00_ctl04_ctl00_ctl00_MultiLookupPicker_m); return false" multiple onchange=GipSelectCandidateItems(ctl00_m_g_856b69b7_94a3_499a_af52_e1789266b73a_ctl00_ctl05_ctl02_ctl00_ctl00_ctl04_ctl00_ctl00_MultiLookupPicker_m); name=ctl00$m$g_856b69b7_94a3_499a_af52_e1789266b73a$ctl00$ctl05$ctl02$ctl00$ctl00$ctl04$ctl00$ctl00$SelectCandidate>
    <OPTION title=Abington selected value=1>Abington</OPTION>
    <OPTION title=Acton value=2>Acton</OPTION>
    <OPTION title=Acushnet value=3>Acushnet</OPTION>
    <OPTION title=Wrentham value=350>Wrentham</OPTION>
    <OPTION title=Yarmouth value=351>Yarmouth</OPTION>

Next comes a 10-pixel-wide spacer TD. It’s not very interesting, but I mention it for completeness. Usually you’d probably do spacing like this in the CSS rather than having a dedicated TD for it.

<td style="padding-left: 10px;"/>


Next come the two buttons. Both buttons have some script attached to their click event that manages the movement of the values between the left and right boxes.

<TD class=ms-input vAlign=center align=middle>
  <BUTTON id=ctl00_m_g_856b69b7_94a3_499a_af52_e1789266b73a_ctl00_ctl05_ctl02_ctl00_ctl00_ctl04_ctl00_ctl00_AddButton class=ms-ButtonHeightWidth onclick="GipAddSelectedItems(ctl00_m_g_856b69b7_94a3_499a_af52_e1789266b73a_ctl00_ctl05_ctl02_ctl00_ctl00_ctl04_ctl00_ctl00_MultiLookupPicker_m); return false" type=submit>Add &gt;</BUTTON>
  <BUTTON id=ctl00_m_g_856b69b7_94a3_499a_af52_e1789266b73a_ctl00_ctl05_ctl02_ctl00_ctl00_ctl04_ctl00_ctl00_RemoveButton class=ms-ButtonHeightWidth disabled onclick="GipRemoveSelectedItems(ctl00_m_g_856b69b7_94a3_499a_af52_e1789266b73a_ctl00_ctl05_ctl02_ctl00_ctl00_ctl04_ctl00_ctl00_MultiLookupPicker_m); return false" type=submit>&lt; Remove</BUTTON>


Now another 10-pixel-wide spacer TD:


And finally, we have the right side box. Like the left side, this is also a SELECT with some script attached to its dblclick and change events. The important thing we need to know about this SELECT element to work with it is its title. In an English installation, title=”City selected values”. It’s the column name followed by ‘ selected values’.

<TD class=ms-input>
  <SELECT style="WIDTH: 143px; HEIGHT: 125px; OVERFLOW: scroll" id=ctl00_m_g_517395ec_085b_47db_b888_b55773e76d86_ctl00_ctl05_ctl02_ctl00_ctl00_ctl04_ctl00_ctl00_SelectResult title="City selected values" ondblclick="GipRemoveSelectedItems(ctl00_m_g_517395ec_085b_47db_b888_b55773e76d86_ctl00_ctl05_ctl02_ctl00_ctl00_ctl04_ctl00_ctl00_MultiLookupPicker_m); return false" multiple onchange=GipSelectResultItems(ctl00_m_g_517395ec_085b_47db_b888_b55773e76d86_ctl00_ctl05_ctl02_ctl00_ctl00_ctl04_ctl00_ctl00_MultiLookupPicker_m); name=ctl00$m$g_517395ec_085b_47db_b888_b55773e76d86$ctl00$ctl05$ctl02$ctl00$ctl00$ctl04$ctl00$ctl00$SelectResult>
    <OPTION title=Abington value=1>Abington</OPTION>
    <OPTION title=Acton value=2>Acton</OPTION>
    <OPTION title=Acushnet value=3>Acushnet</OPTION>


There’s a lot more to this simple little thing that you might have expected, eh? And I haven’t even gone into how the underlying script works. In fact, that’s so complicated that I’m going to leave it for another time.

The Inconsistency

But back to the whole point of this article, which is the inconsistency across language versions of SharePoint. The key is to be able to find the important piece parts of the control in the DOM with SPServices (in my case) and be sure that we are working with the right one if there are multiple multiselect columns in the form. To do this, I have a function in SPServices called dropdownCtl. Its little purpose in life is to find a “dropdown” and return an object reference to it in the DOM as well as its “Type”. The functions SPCascadeDropdowns, SPDisplayRelatedInfo, and SPLookupAddNew in SPServices all use the dropdownCtl function to find the right elements in the DOM to work with.

One other tricky part is that the are three types of “dropdowns” we can have in the form. (I’m using “dropdowns” in quotes because it’s a little bit of a stretch to call the multiselect control a dropdown.) Each type has different representations in the DOM elements, so dropdownCtl needs to find the right element and also decide which type of element it is.

The first two types are what you usually would think about as dropdowns, and they are easier to find in the DOM. They are either a “simple SELECT,” which is rendered if there are fewer than 20 options for the column, or a “complex SELECT,” which SharePoint uses if there are 20 or more options. The “complex SELECT” is actually a hybrid INPUT/SELECT with some script to drive it.

In the case of a “multiselect dropdown,” it’s a little trickier. The best way to find the various elements in the page for a “multiselect dropdown” is to look for the SELECTs’ titles. This is where the translation differences come into play.

It’s understandable (perhaps) that the English titles should be translated into the other languages. What doesn’t make sense is that the format of those translations is quite different. Let’s look at what the City column’s right box would have as its title in three different languages:

English City selected values
Russian Выбранных значений: City
(this is for the possible values, but the point is the same)
German Ausgewählte Werte für “City”

As you can see, the pattern of the text varies, not just the words themselves. I don’t speak Russian or German, but it’s hard to imagine that it’s necessary to change the construct from ‘City selected values’ to ‘Selected values: City’ or ‘Selected values for “City”‘ in order to reflect some sort of nationalistic thinking.

In my code, I don’t want to care about what language is being used; I just want to be able to find the right title. By using different patterns, SharePoint makes this more difficult than it should be.

The Solution

Because of the differences between the titles in the different language versions of SharePoint, in my dropdownCtl function I have to have these three conditions:

// Multi-select: This will find the multi-select column control in English and most other languages sites where the Title looks like 'Column Name possible values'
} else if ((this.Obj = $("select[ID$='SelectCandidate'][Title^='" + colName + " ']")).html() != null) {
  this.Type = "M";
  // Multi-select: This will find the multi-select column control on a Russian site (and perhaps others) where the Title looks like 'Выбранных значений: Column Name'
} else if ((this.Obj = $("select[ID$='SelectCandidate'][Title$=': " + colName + "']")).html() != null) {
  this.Type = "M";
  // Multi-select: This will find the multi-select column control on a German site (and perhaps others) where the Title looks like 'Mögliche Werte für &amp;quot;Tops&amp;quot;.'
} else if ((this.Obj = $("select[ID$='SelectCandidate'][Title$='\"" + colName + "\".']")).html() != null) {
  this.Type = "M";

Obviously, I can handle this in my code, but every time I hear from someone who uses a different language version of SharePoint and sees a bug, I need to revisit what the pattern of the text is in that language.

The Moral of the Story

The moral of the story is that seemingly tiny decisions can actually make more significant differences. Consider very carefully why you want to change the style of something like an ID or markup that you’re rendering in a control because there can be ramifications later. We all want to leave a little bit of our personal style in our code, but try not to do so at the expense of reusability, interoperability, or consistency.


Since I wrote this article back in 2010, I’ve made adjustments to handle additional languages. I’ve added Italian and I’ve recently been asked for a fix for Portuguese. Should you find another inconsistency, please let me know by posting in the SPServices Discussions on Codeplex.

Here’s what my DropdownCtl function looks like in version 2013.01:

// Find a dropdown (or multi-select) in the DOM. Returns the dropdown onject and its type:
// S = Simple (select);C = Compound (input + select hybrid);M = Multi-select (select hybrid)
function DropdownCtl(colName) {
  // Simple
  if ((this.Obj = $("select[Title='" + colName + "']")).length === 1) {
    this.Type = "S";
    // Compound
  } else if ((this.Obj = $("input[Title='" + colName + "']")).length === 1) {
    this.Type = "C";
    // Multi-select: This will find the multi-select column control in English and most other languages sites where the Title looks like 'Column Name possible values'
  } else if ((this.Obj = $("select[ID$='SelectCandidate'][Title^='" + colName + " ']")).length === 1) {
    this.Type = "M";
    // Multi-select: This will find the multi-select column control on a Russian site (and perhaps others) where the Title looks like 'Выбранных значений: Column Name'
  } else if ((this.Obj = $("select[ID$='SelectCandidate'][Title$=': " + colName + "']")).length === 1) {
    this.Type = "M";
    // Multi-select: This will find the multi-select column control on a German site (and perhaps others) where the Title looks like 'Mögliche Werte für &quot;Column name&quot;.'
  } else if ((this.Obj = $("select[ID$='SelectCandidate'][Title$='\"" + colName + "\".']")).length === 1) {
    this.Type = "M";
    // Multi-select: This will find the multi-select column control on a Italian site (and perhaps others) where the Title looks like "Valori possibili Column name"
  } else if ((this.Obj = $("select[ID$='SelectCandidate'][Title$=' " + colName + "']")).length === 1) {
    this.Type = "M";
  } else {
    this.Type = null;
} // End of function DropdownCtl

SPServices v0.7.1ALPHA13 Available for Testing

Faster and more furious, that’s what I always aim for.


I’ve been working hard on this next release of SPServices. I’ve been lucky to have some down time between projects to do it. Yes, I’m happy to have the gap so that I can do more and do it better; I’ve been able to make even more significant improvements because I’ve has the focused time. It’s a great feeling to look at my old code and know that I can do a lot better now. This version of SPServices is going to be leaner and faster than ever before. As I tweeted the other day:

Alpha13, which I just posted is, I think, a good one to think of as RC1. Before I get too busy again with client work, I want to get it out there for some of you to test. If you are an SPServices user and can do some testing, especially regression testing, I’d really appreciate it.

There’s also some new stuff that I’ve built into the last few alphas:

  • Better error messages for SPCascadeDropdowns, SPDisplayRelatedInfo, and SPFilterDropdown. If you have debug mode on and the listName is incorrect, you’ll get a clearer message.
  • Improvements to the new SPXmlToJson function. All of the documented options now work as advertised and I’ve optimized the code quite a bit.
  • SPCascadeDropdowns and SPDisplayRelatedInfo now have a matchOnId option. If matchOnId is set to true, the ID of the Lookup column is used rather than the text value. This can be handy where the same text value means different things, like the place-name of Springfield, which can be found in 34 of the US States. By default the behavior of the function remains the same, of course.
  • New operations: Lists.ApplyContentTypeToList, Lists.CreateContentType, Lists.DeleteContentType, Lists.GetListItemChanges, Lists.UndoCheckOut, Lists.UpdateContentType, SiteData.GetWeb
  • Numerous (and I do mean numerous) improvements for efficiency.

Enjoy, and let me know if you have a chance to try it out.

SPServices v0.7.1ALPHA7 Available for Testing

I posted a new alpha of SPServices v0.7.1 today.logo250x150

Even though I just released v0.7.0 in early December, I’ve been working steadily to get some great new functionality and performance improvements into v0.7.1 as well as to fix some rather obscure bugs.

You can see the details of what is included in this alpha on the download page, but perhaps what will be of widest interest are some significant performance improvements to SPCascadeDropdowns and SPDisplayRelatedInfo. If you use those functions, I’d really appreciate you trying the alpha out and letting me know whether the improvements are noticeable to you. The initial setup time in the form won’t be faster, but the cascading in the page ought to be. (The better tuned your SharePoint farm and the more horsepower you have, the less improvement you are likely to see.)

I’ve also added a new function called SPXmlToJson. With it, I hope to offer a generic XML to JSON conversion utility. The first focus is on the XML returned by GetListItems, but I plan to expand upon it as people request additional capabilities. If you’re into playing with JSON, please take a look and give me your thoughts and suggestions. Be kind and remember that this is just an alpha.

Image via Wikipedia

The main reason for getting v0.7.0 (the prior release) out there was to release a version which was compatible with jQuery 1.7.x. As goeth jQuery, so goeth SPServices; the jQuery team doesn’t sit still for long, so I can’t really, either. In fact, jQuery 1.7.1 has already been out for a while and we can see 1.8 on the horizon. As part of the new jQuery releases, there are always some significant performance improvements as well as new capabilities. As with most software, there are also capabilities which are deprecated, which may by removed in the future.

Because of all of these changes to jQuery itself, along with my accompanying changes to SPServices, I would strongly encourage you to try to keep up with the releases as best you can. I know that this can be difficult, especially in large enterprise environments where the release cycles can be complex and time consuming.

Given the constraints that many people probably have when it comes to upgrading SPServices, I’m wondering what a good release cycle would be. There have already been over 2000 downloads of v0.7.0, yet I’m getting near the time when I might want to put v0.7.1 out there. What would work best for you? Fewer releases with more changes or more releases with fewer changes? There are many considerations to this, I know. If nothing else, the documentation can only represent one point in time (I’m loathe to try to keep historical versions; maintenance would be a nightmare). If you are using an older version of SPServices, like say v0.5.8, then the docs are pretty far off for you, as will be my blog posts and articles elsewhere. Let me know what you think!

Enhanced by Zemanta