Replacing Multi-select Lookup field with Checkboxes

The Situation

In SharePoint 2010, when you create a lookup field that allows for multiple selections, you end up with a very non-standard html widget that includes 2 boxes for moving “possible” values to “selected” values. Like so:

Out of the box Multi-select UI

Out of the box Multi-select UI

That’s all well and good if you have short values and a long list of them. But, if you happen to have long values and a short list of them, SharePoint’s UI choice is less than spectacular. As you can see in the image, most of the values are hidden in the select box. Marc Anderson has a function in his popular SpServices library to resize the select boxes to show the complete value. But, that’s not always preferable – as that can make your page very, very wide.

The Solution

Wouldn’t it be nice if you could just make a checkbox interface instead of the multi-select interface? Why, yes, yes it would. And now you can. There are a few moving parts here. I’ll try to break it down.

First, we need a way to select and unselect choices in the multi-select UI. It’s not as easy as just highlighting a value and using jQuery to “click” the add or remove button. Believe me, I tried that. Happily, I found someone else that grappled with this issue, and he got me most of the way there. I had to tweak a couple of things, because I had multiple multi-select fields on a single form, and because I know my users well enough to know that they’ll go crazy checking and unchecking things. So, here are my add choice and remove choice functions.

Add Choice

function addChoice(text, columnName) {

	// let's get the id from the column name
	var thisID = $("[title='" + columnName + " possible values']").prop('id');
	thisID = thisID.substr(0, thisID.indexOf('_SelectCandidate'));
	selector = "[id$=" + thisID + "_MultiLookupPicker]";
    $("[title='" + columnName + " possible values'] option").each(function () {   
        if ($(this).text() == text) {
            $(this).appendTo($("[title='" + columnName + " selected values']"));

            var multilookupPickerVal = $(selector).val();
            if ($(selector).val() == undefined || $(selector).val().length == 0) {
                $(selector).val($(this).val() + "|t" + $(this).text());
            else {
                $(selector).val(multilookupPickerVal + "|t" + $(this).val() + "|t" + $(this).text());

Remove Choice

function removeChoice(text, columnName) {
// let's get the id from the column name
	var thisID = $("[title='" + columnName + " selected values']").prop('id');
	thisID = thisID.substr(0, thisID.indexOf('_SelectResult'));
	selector = "[id$=" + thisID + "_MultiLookupPicker]";

	// loop through the selected options
    $("[title='" + columnName + " selected values'] option").each(function () {

        if ($(this).text() == text) {
            $(this).appendTo($("[title='" + columnName + " possible values']"));

            var multilookupPickerVal = $(selector).val();

            // call the internal function which creates a clean array out of the weird string
            var splitValue = GipSplit(multilookupPickerVal);

            // set the 2 array nodes we want to remove
            var valToRemove = $(this).val();
            var textToRemove = $(this).text();

            // Kill the value and text in the array
            splitValue = jQuery.grep(splitValue, function(value) {
        		return value != valToRemove;

      		splitValue = jQuery.grep(splitValue, function(value) {
        		return value != textToRemove;

      		var newValue = '';

      		// loop through the cleaned up array and rebuild the selector value
      		for (var i = 0; i < splitValue.length; i++) {
      			if (newValue.length == 0) {
      				newValue = splitValue[i];
      			else {
      				newValue = newValue + '|t' + splitValue[i];


			 } // end for loop

          	// set the new value to the selector


Next, we need a function to draw the checkboxes to the screen. I wanted to make this as simple as possible, so this same function draws the checkboxes, hides the old UI, and attaches the remove and add choice functions to the click events of the checkboxes.

Draw Checkboxes

// function used to draw the checkboxes instead of a multi-select lookup
function drawCheckboxes(columnName) {

	// remove spaces from columnName
	var divName = columnName.split(' ').join('') + 'Checkboxes';

	// find the parent td, hide the span, clear the div
	$('div[id="' + divName + '"]').remove();
	$("[title='" + columnName + " possible values']").closest('span').after('<div id="' + divName + '">New div</div>');
	$("[title='" + columnName + " possible values']").closest('span').hide();
	var thisDiv = $('div[id="' + divName + '"]').html('');

	// loop through all the possible options and draw the checkboxes
    $("[title='" + columnName + " possible values'] option").each(function () {
        var thisText = $(this).text();
        var thisVal = $(this).val()
        var thisSnippet = "<input type='checkbox' name='" + columnName + "' value='" + thisText + "' id='" + columnName + "Checkbox" + thisVal + "'/><label for='" + columnName + "Checkbox" + thisVal + "'>" + thisText + "</label><br/>";


    // loop through all the selected options and draw the checkboxes
    $("[title='" + columnName + " selected values'] option").each(function () {
        var thisText = $(this).text();
        var thisVal = $(this).val()
        var thisSnippet = "<input type='checkbox' name='" + columnName + "' value='" + thisText + "' id='" + columnName + "Checkbox" + thisVal + "'/><label for='" + columnName + "Checkbox" + thisVal + "'>" + thisText + "</label><br/>";


    // Loop through the checklist for outcome statement lookup 

	var boxes = $('input[name="' + columnName + '"]');
	boxes.each(function(index) {

	$(this).click(function() { 
		// get the label

		if( $(this).is(':checked')){
			addChoice($(this).val(), columnName);
		else {
			removeChoice($(this).val(), columnName);

	});  // End loop

} //end draw checkboxes

The basic solution

Great – now we have functions that will draw checkboxes and add and remove choices when the checkboxes are clicked. What do you do with it? First put all your scripts together in a text file and upload your text file to your site. (Note, there is one line you’ll need to edit in this text file.) (I typically use the site assets library.) On your page with your data form web part, you’ll need to include a content editor web part and link to your text file.  Run your page, and voila – checkboxes! (Note that in this image, I’ve shown both interfaces.)

Showing both the standard and checkbox UI.

Showing both the standard and checkbox UI.

Of course, around here, we’re integrating this with SPServices, to filter the multi-select. For that, you need a few more steps. First, you’ll need a copy of SPServices in your site, and you’ll need to include a reference to SPServices in your text file. Next, you’ll need to make a function that will work as a call-back to the SPServices.SPFilterDropdown or SPCascadeDropdowns function. Functions passed as callbacks can’t have parameters (at least, I haven’t figured out how to do it). So, you’ll need a wrapper function for drawCheckboxes that passes in the column you want. This is convenient because you can also do other stuff at the same time, if need be. Here’s my example, using a filter.

Wrapper Function

// need this function to pass a parameter-free function to the complete function of the dropdown filter
function drawProgramAreaCheckboxes() {
	drawCheckboxes("Collaborators Program Area");	
//end drawProgramAreaCheckboxes

Calling the SpServices Filter function with callback

//Filter and draw checkboxes for program areas
  relationshipList: "Program Area",
  relationshipListColumn: "Title",
  relationshipListSortColumn: "ID",
  columnName: "Collaborators Program Area",
  CAMLQuery: " 1",
  completefunc: drawProgramAreaCheckboxes,
  debug: true

//End program areas

Here’s the complete file for using checkboxes with SpServices. Note, there are multiple parts you’ll need to customize in here.

Editing Forms

Now that we have adding forms figured out, we need to do something about editing. It’s essentially the same, but you need to set the checkboxes that require setting. Again, for this, we’re using SPServices, and we need a few more function calls.

Step one – helper functions to set checkbox values

We need a couple of helper functions to set the checkboxes. The first sets a checkbox’s selected property to true based on the value of the checkbox.

function checkByValue(value) {

	$(":checkbox").filter(function() {
        return this.value == value;
    }).prop("checked", "true");


The second takes in a string as returned by spSpervices GetListItems for multi-select fields and the column name and loops through the string, setting each checkbox. The string returned is a series of ID’s and values, separated by semi-colons and hash tags. So, we’ll split the string on the ;# combo, and then check only when we have a value, not a number. Caveat – if your values ARE numbers, this won’t work for you, and you’d need to do check every other value, instead of the textual values.

function setMultiSelectValueCheckboxes(inString, column) {

 	var outString = inString.split(";#");

 	$.each(outString, function( index, value ) {

		  if (isNaN(value)) {


} // end set MultiSelectValueCheckboxes

Step two – get the ID from the query string.

SpServices has a helper function that lets you get parameters from the query string. SharePoint always passes an ID parameter to edit forms. So, you just need to grab it, like so:

// This function from SPServices gets all of the Query String parameters 
  	var queryStringVals = $().SPServices.SPGetQueryString();
  	var effortID = queryStringVals.ID;

Step three – build the checkboxes

Just like in the add scenario, you’ll need to build your checkboxes. You want to build them before you try to check them, or this won’t work. For brevity, you can refer to the completed text file for this section, since it’s no different than the add form.

Step four- use GetListItems to get the thing you’re editing

My test list is called “Educational Effort.” Here I’m passing in a CAML query to get just the item we’re editing. I’ve returned more viewfields than you’d really need. But, you get the idea. If we have a successful status, we then set the value of the checkboxes, using our helper function from above.

// Get the related effort
		    operation: "GetListItems",
		    async: true,
		    listName: "Educational Effort",
		    CAMLQuery: "" + effortID + "",
		    CAMLViewFields: "",
		    completefunc: function (xData, Status) {

				if (Status == 'success') {

				$(xData.responseXML).SPFilterNode("z:row").each(function() {

 				// get outcome statement and program area values
		        var outcomeStatementLookup = $(this).attr("ows_OutcomeStatementLookup");
		        var collaboratorsProgramArea = $(this).attr("ows_CollaboratorsProgramArea");

		        // set checkboxes
				setMultiSelectValueCheckboxes(outcomeStatementLookup, 'Outcome Statement Lookup'); 
 				setMultiSelectValueCheckboxes(collaboratorsProgramArea , 'Collaborators Program Area'); 

		      });	// end each function	
		      } //end if status = success.		
		    } // end complete function
		  }); // end spservices
// End get the related effort

And, voila – automatically checked checkboxes.

Edit page with auto-checked checkboxes.

Edit page with auto-checked checkboxes.

Here’s the complete file for an edit form. Again, note that you’ll need to customize for your environment.

As usual, feel free to point out places where this could be optimized, tweaked, etc. I’ve testing in IE 10, Chrome and Firefox, and it seems to be working for me.