I am trying to make a simple double drop down list. I have the basic structure down I believe, but I am stuck on making the javascript work correctly.

I want the double drop downs to look as such: enter image description here

The first dropdown allows you to select a department (i.e. accounting, HR, etc.). The second dropdown lets you choose a task inside of that department.

I currently have the links stored in a list like such: enter image description here

My thought process was to make a column for departments, and then each of those departments should have their own column. That way, when the department is selected, I can use the text in that field to pull data from the correct department's column. I believe I have a good start, but I am looking for either a quicker way to do this, or a fix to my javascript.

Currently I am stuck on an error, and I understand the reasoning but not how to fix it. For reference, my code is below:

    <script type="text/javascript">
        ExecuteOrDelayUntilScriptLoaded(retrieveListItems, "sp.js");

        function retrieveListItems() {
            var clientContext = new SP.ClientContext.get_current(); //create client context at this site collection.
            var oList = clientContext.get_web().get_lists().getByTitle('LinkList'); //create list object by the title of the, projectParameters is the list to look through
            var camlQuery = new SP.CamlQuery(); //create CAMLquery to pull in list items.
            camlQuery.set_viewXml('<View><RowLimit>100</RowLimit></View>'); //define the query, here we pull in the records where the project is active 
            this.collListitem = oList.getItems(camlQuery);//get the items from the list object based on the query defined above.

            clientContext.load(collListitem, 'Include(LinkTitle, Active, Acc, HR, Technology)');//telling the object model to prepare to execute the following object by loading it into the client context.

            clientContext.executeQueryAsync(Function.createDelegate(this, this.onQuerySucceeded), Function.createDelegate(this, this.onQueryFailed));  
        function onQuerySucceeded(sender, args) {
            var LinkTitle = '';
            var active = '';
            var accountingLink = '';
            var accountingTask = '';
            var hrLink = '';
            var hrTask = '';
            var technologyLink = '';
            var technologyTask = '';

            var listItemEnumerator = collListitem.getEnumerator();

            while (listItemEnumerator.moveNext()) {
                var oListItem = listItemEnumerator.get_current();
                LinkTitle += oListItem.get_item("LinkTitle") + ' ';
                active += oListItem.get_item("Active") + ' ';
                accountingLink += oListItem.get_item("Acc").get_url() + ' ';
                accountingTask += oListItem.get_item("Acc").get_description() + ' ';
                hrLink += oListItem.get_item("HR").get_url() + ' ';
                hrTask += oListItem.get_item("HR").get_description() + ' ';
                technologyLink += oListItem.get_item("Technology").get_url() + ' ';
                technologyTask += oListItem.get_item("Technology").get_description() + ' ';

            //put items into loopable arrays
            var LinkTitleArray = LinkTitle.split(' ');
            var ActiveArray = active.split(' ');
            var accountingLinkArray = accountingLink.split(' ');
            var accountingTaskArray = accountingTask.split(' ');
            var hrLinkArray = hrList.split(' ');
            var hrTaskArray = hrTask.split(' ');
            var technologyLinkArray = technologyLink.split(' ');
            var technologyTaskArray = technologyTask.split(' ');

            var deptList = document.getElementById("deptList");
            var taskList = document.getElementById('taskList');
            var deptChoice = deptList.options[deptList.selectedIndex].text;

            var taskText = '';
            var taskLink = '';

            for(var i = 0; i<ActiveArray.length-1; i++){  //modify the two drop downs
                if(deptChoice == "accounting"){
                    taskText = accountingTaskArray[i];
                    taskLink = accountingLinkArray[i];
                else if(deptChoice == "hr"){
                    taskText = hrTaskArray[i];
                    taskLink = hrLinkArray[i];
                else if(deptChoice = "technology"){
                    taskText = technologyTaskArray[i];
                    taskLink = technologyLinkArray[i];
                console.log(taskText + "//" + taskLink);
                var newOption = document.createElement("option"); //create new link
                newOption.value = taskLink; //add link in the value field
                newOption.innerHTML = taskText; //add text of the new option
                taskList.appendChild(newOption); //add new option to dropdown

        function onQueryFailed(sender, args) {
            alert('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());

        function JumpToUrl(url){
            location.href = url;

    <div id="LinkDisplay">
        <h1> What do you want to do? </h1>

        <table border="0">
                <th>   </th>
                <td><select name= "deptList" id="deptList"  onchange="javascript:retrieveListItems(deptList.options[deptList.selectedIndex].text)">

                <td>   </td>

                <td><select name="taskList" id="taskList" onchange="javascript:JumpToUrl(taskList.options[taskList.selectedIndex].value)">

In the onQuerySucceeded function, I try to get the url/description from the hyperlink fields in the List. The issue with this is that not all of the departments will have the same number of links, so I shouldn't be pulling all of the fields in. However, I am not sure the best workaround. Maybe add an extra parameter to onQuerySucceeded so that I can pass the selected department using the onChange function. I pass the necessary value to retrieveListItems, just not all the way through the succeeded function. Workarounds anyone?

  • If you are trying to implement cascading drop-downs, I would highly recommend looking at the third-party jQuery library SPServices. It has an implementation of cascading drop-downs that uses backing lists as configuration, but in a slightly different way to how you are doing it. Commented Aug 19, 2016 at 20:46

2 Answers 2


You don't need a workaround, you need basic JavaScript

As you said:

Maybe add an extra parameter to onQuerySucceeded so that I can pass the selected department using the onChange function. I pass the necessary value to retrieveListItems, just not all the way through the succeeded function

This is an example of Async code passing data to the succeed function:

Test it as a Chrome Snippet:

function getItems(listName,siteUrl){
    var request={};
    request.siteUrl=siteUrl || _spPageContextInfo.siteAbsoluteUrl;
    var ctx = new SP.ClientContext(request.siteUrl);
    var list = ctx.get_web().get_lists().getByTitle(request.listName);
    ctx.executeQueryAsync(  processItems.bind(request)  ,failure);
function processItems() {
    var received=this;//received 'request' from Caller
    var listEnumerator = received.itemCollection.getEnumerator();
    while (listEnumerator.moveNext()) {
        var listitem = listEnumerator.get_current();
    console.info(received.listName,'has',received.items.length,'items IDs:',received.items);
function failure(sender, args){
for(var cycle=1;cycle<4;cycle++){// call multiple times to show ASYNC result in the console
    console.log('Calling 3 lists a couple of times.. cycle:',cycle);

.bind() has been standard JavaScript for a very long time, ofcourse Microsoft got late into the game and had its own (oldskool) Function.CreateDelegate implementation, since IE9 they comply with the standard
See: Differing ways to make executeQueryAsync calls - Function.createDelegate (is old IE8 code)

You can bind anything you want, and Objects are passed by Reference, not by value, so you could built a whole datastructure in your main thread

iJS:Passing data from an async function to a processing function with .bind


Is using InfoPath an option for you? Rules can be set up to show / hide fields and sections when you select specific choices.

Example I found online: https://manojssharepointblog.wordpress.com/2012/12/23/infopath-2013-web-browser-creating-cascading-dropdown-fields-with-no-code/

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.