Cascading Dropdown Columns in a SharePoint Form – Part 1

UPDATE 2009-08-26: I’ve translated this logic into my jQuery Library for SharePoint Web Services. I *strongly* suggest that you look at that as an option, as it is far more robust.  And free!

UPDATE 2009-07-19: I’ve added a second post on this with a simpler example. It also contains a link to a demo page which shows how it all fits together.

Over in the MSDN SharePoint – Design and Customization Forum there are frequent questions about “cascading” dropdowns.  This means having two (or more) dropdowns “connected” based on the choices the user makes.  I dug out an example from a client project that demonstrates this technique fairly well so that I could refer folks to it.

In this example, the client had Divisions, each with a set of Branches.  We stored the Division / Branch pairs in a simple SharePoint Custom List in the root site of the Site Collection which had just those two columns.  (We actually used the Title column in the list for the Branch just to keep it really simple.  This list wasn’t one that anyone outside of the administrators was going to be looking at.)

We wanted to have the cascading work in the forms for a Document Library where each document would be categorized as belonging to a Division and Branch.  (The Division and Branch columns were both Lookup columns into lists at the Site Collection root.)  To make the Division and Branch columns cascade in the forms, we needed to have these pieces in place:

  • A hidden Data View Web Part (DVWP) which put the Division / Branch pairs into a table on the form for use by the
  • JavaScript to change the available values for Branch once the Division was selected.

We didn’t actually need to customize the form itself; we left the default List Form Web Part (LFWP).  The major reason for this was that we had upward of 30 Content Types that could be stored in the Document Library, and we wanted the Content Type selection to work the clean, default way.  All of the changes we made to the form were done with the client-side JavaScript.

Here are the main XSL templates from the hidden DVWP.  Note that there is nothing fancy about this DVWP except that I gave the table an id=”BranchesTable” so that I could locate it in the DOM with the JavaScript:

<xsl:template name="dvt_1">
    <xsl:variable name="Rows" select="/dsQueryResponse/Rows/Row"/>
   
<table id="DivisionTable">
    <xsl:for-each select="$Rows">
        <xsl:sort select="@Branch" order="ascending"/>
        <xsl:call-template name="dvt_1.rowview" />
    </xsl:for-each>
   </table>
</xsl:template>

<xsl:template name="dvt_1.rowview">
<tr>
<td>
      <xsl:value-of select="@Division"/></td>
<td>
      <xsl:value-of select="@Title"/>
    </td>
     
<td>
      <xsl:value-of select="@ID"/>
    </td>
  </tr>
</xsl:template>

Next was the JavaScript.  As I always do, I stored the JavaScript in a Document Library in the root site of the Site Collection called JavaScript.  This way I could reuse it anywhere I needed to.  There were a lot of other things I was doing in the JavaScript to make the form work more richly, but I’ve snipped out the pieces that are relevant to this specific task (hopefully not breaking anything in so doing!).

First, I needed to attach an onchange event to the Division column’s dropdown.  This code snippet was called as part of the form load with _spBodyOnLoadFunctionNames.push();

// Find the Branch column
var Branches = getTagFromIdentifierAndTitle("input","","Branch");
// Save the Branch choices so that we can rebuild as needed
var savedBranchesChoices = Branches.choices + "|";
// Find the Division column
Division = getTagFromIdentifierAndTitle("select","","Division");
// Attach the onchange event
Division.attachEvent('onchange', DivisionChanged);

Second, here’s the JavaScript to handle the onchange event.  A few notes that are important here:

  • This was an environment where Internet Explorer 6+ was the supported browser.  We didn’t need to worry about any other browsers working well with the JavaScript (though I don’t think I was doing anything that wouldn’t work in other browsers).
  • There were more than 20 Branches.  When this is the case, SharePoint generates the dropdown control for the column differently than if there are fewer than 20 values.  I won’t got into the details of this; suffice it to say that some of the peculiarities below are there to manage this difference.
function DivisionChanged() {
    var chosenDivision = Division.options[Division.selectedIndex].text;

    // Find the hidden table with the Division / Branch / ID information
    var DivisionTable = document.getElementById("DivisionTable");
    var DivisionsTableRows = DivisionsTable.getElementsByTagName("TR");
    BranchesChoices = savedBranchesChoices;
    // See which choices ought to remain for this Division
    var newChoicesList = '';
    while(BranchesChoices.length > 0) {
        // Grab the first choice in the list
        var thisBranch = BranchesChoices.substring(0, BranchesChoices.indexOf("|"));
        // Trim the first choice out of the choices
        BranchesChoices = BranchesChoices.substring(thisBranch.length + 1);
        // Grab this choice's index
        var thisBranchIndex = BranchesChoices.substring(0, BranchesChoices.indexOf("|"));
        // Trim the first index out of the choices
        BranchesChoices = BranchesChoices.substring(thisBranchIndex.length + 1);
        // See if this Branch is in the table for this Division
        for (var j=0; j < DivisionsTableRows.length; j++) {
            var DivisionsTableRowsDetails = DivisionsTableRows[j].getElementsByTagName("TD");
            if(DivisionsTableRowsDetails[0].innerHTML == chosenDivision &&
                    DivisionsTableRowsDetails[2].innerHTML == thisBranchIndex)
                newChoicesList = newChoicesList + thisBranch + "|" + thisBranchIndex + "|";
        }
    }
    // The choices now become the filtered list, allowing for no selection: (None)
    if(newChoicesList.length > 0)
        Branches.choices = "(None)|0|" + newChoicesList.substring(0, newChoicesList.length - 1);
    else
        Branches.choices = "(None)|0";        
    Branches.value = "";
}

Similar Posts

47 Comments

    1. The code here is specific to having 20+ options for the Branch. It needs to be different for fewer than 20. My jQuery function handles all of that for you.

      M.

  1. Hi Marc,
    I need some help/guidence.
    I have two lists and a document library.(same as you mentioned in this blog.
    A Division List with one column DivisionName and a Branch List with two columns namely BranchName and DivisionName.In this BranchList DivisionName is a look up column.I have a document library which have documents belonging to each branch. This library has three columns with Title,DivisionName and BranchName, where DivisionName and BranchName are look up columns.)
    I have bound two asp.net dropdown boxes to two SPDatasources linked to these lists. I have also a DataFormWebPart bound to Document library showing Title,DivisionName and BranchName.
    Documents are uploaded and not created new, so there is no New Form.

    I want to pass values from Division DropDown to Branch DropDown and then after selecting a particular Branch pass value to Doucument DataFormWebpart so that only those documents are shown which belong to that Branch. With asp.net control I can only do AutoPostback but no OnChange or any othere event. Also the form I used to put DropDown and DataFormWebPart is a new aspx form attached to default master page.
    I want to thank you in advance for your advice or help.
    Bharat.

  2. When I am trying to access the Demo of Cascading Dropdown at [http://www.sympraxisconsulting.com/Demos/Demo%20Pages/CascadingDropdowns.aspx], my IE8 hangs!

    Is there any known issue in this Demo page?

    1. Kamlesh:

      I just checked, and the page loaded fine for me in IE8. Maybe the site was down temporarily (it’s hosted, so I don’t control the downtime). Give it another try!

      M.

  3. Hi Marc,

    Greate post!
    I was trying to get the table id by the document.getelementbyid(“mytableid”) but it seems I cant get it. I always get null.
    Do you know why?

    Cheers

    1. Did you give your table an id?

      Note that this is a really old post, and I have a note at the top recommending that you use SPServices instead. The method here is really klunky and only works in certain circumstances.

      M.

    1. “No code” means different things to different people. SPServices is considered “no code” by many because it isn’t managed code.

      M.

  4. Marc,
    I’m looking for a cascading dropdowns for custom webpart page. I see most of them using the jQuery are for list forms.
    Can you please let me know?

    Thanks.

    1. Lak:

      I built SPCascadeDropdowns to work on list forms only. If you want to build a cascading dropdown function on some other page, you can use SPServices to drive it, but you”ll need to build it yourself.

      M.

  5. Hai,
    How to compare list column with another list column in sharepoint 2010 using web services.i.e
    list1:column_skill1
    list2:cloumn_skill2
    if skill1=skill2
    retrieve that user name
    if any information related this issue please share me.
    thanks,
    suresh

  6. Hi Marc,

    I don’t know whether this is the right place to put my question.

    I just wanted to know how to trigger an event when the second cascading dropdown fills with value. so that i can enable or disable a button for searching purpose in a dataview.

    Appreciate a quick response from you.

    Thanks
    Raj

    1. Raj:

      If you’re using SPServices, the best place to pose your questions is in the discussions on the Codeplex site for SPServices.

      You can bind to the change event for the dropdown (it’s more complex if there are 20+ options) in your script.

      M.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.