{"id":18870402,"url":"https://github.com/sebastiansulinski/cascading-dropdown","last_synced_at":"2025-04-14T15:21:17.942Z","repository":{"id":57194665,"uuid":"31624859","full_name":"sebastiansulinski/cascading-dropdown","owner":"sebastiansulinski","description":"Cascading form drop-down menus","archived":false,"fork":false,"pushed_at":"2020-05-06T18:58:59.000Z","size":541,"stargazers_count":45,"open_issues_count":1,"forks_count":10,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-28T04:11:26.787Z","etag":null,"topics":["ajax-call","javascript","jquery","jquery-plugin","json-response"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sebastiansulinski.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-03-03T22:28:17.000Z","updated_at":"2024-05-15T14:43:41.000Z","dependencies_parsed_at":"2022-09-16T05:14:33.073Z","dependency_job_id":null,"html_url":"https://github.com/sebastiansulinski/cascading-dropdown","commit_stats":null,"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sebastiansulinski%2Fcascading-dropdown","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sebastiansulinski%2Fcascading-dropdown/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sebastiansulinski%2Fcascading-dropdown/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sebastiansulinski%2Fcascading-dropdown/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sebastiansulinski","download_url":"https://codeload.github.com/sebastiansulinski/cascading-dropdown/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248606859,"owners_count":21132428,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ajax-call","javascript","jquery","jquery-plugin","json-response"],"created_at":"2024-11-08T05:20:03.400Z","updated_at":"2025-04-14T15:21:17.921Z","avatar_url":"https://github.com/sebastiansulinski.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Cascading drop-down menu\n\njQuery plugin, which allows you to populate a set of form drop-down menus based on the previous selection.\n\n## Demo\n\n[Cascading drop-down menu jQuery plugin](http://jquery-cascading-dropdown.ssdtutorials.com/)\n\n## Basic usage\n\nTo use the plugin without overwriting any default settings, you'll need to create a structure of the form with a number of drop-down (select) menus.\nIn the following example I'm using\n\n```\n\u003cform\u003e\n\n    \u003cselect\n        name=\"category\"\n        class=\"cascadingDropDown\"\n\n        data-group=\"product-1\"\n        data-target=\"make\"\n        data-url=\"data/make.json\"\n        data-replacement=\"container1\"\n\n        \u003e\n        \u003coption value=\"\"\u003eSelect category\u003c/option\u003e\n        \u003coption value=\"1\"\u003eShoes\u003c/option\u003e\n        \u003coption value=\"2\"\u003eT-shirts\u003c/option\u003e\n        \u003coption value=\"3\"\u003eJeans\u003c/option\u003e\n        \u003coption value=\"4\"\u003eHats\u003c/option\u003e\n        \u003coption value=\"5\"\u003eBelts\u003c/option\u003e\n        \u003c/select\u003e\n\n    \u003cselect\n        name=\"make\"\n        class=\"cascadingDropDown\"\n\n        data-group=\"product-1\"\n        data-id=\"make\"\n        data-target=\"colour\"\n        data-url=\"data/colour.json\"\n        data-replacement=\"container1\"\n        data-default-label=\"Select make\"\n\n        disabled\n        \u003e\n        \u003c/select\u003e\n\n    \u003cselect\n        name=\"colour\"\n        class=\"cascadingDropDown\"\n\n        data-group=\"product-1\"\n        data-id=\"colour\"\n        data-target=\"size\"\n        data-url=\"data/size.json\"\n        data-replacement=\"container1\"\n        data-default-label=\"Select colour\"\n\n        disabled\n        \u003e\n        \u003c/select\u003e\n\n    \u003cselect\n        name=\"size\"\n        class=\"cascadingDropDown\"\n\n        data-group=\"product-1\"\n        data-id=\"size\"\n        data-default-label=\"Select size\"\n        data-replacement=\"container1\"\n        data-url=\"data/final.json\"\n        data-final\n\n        disabled\n        \u003e\n        \u003c/select\u003e\n\n        \u003cinput\n            type=\"submit\"\n            class=\"small button\"\n            value=\"ADD TO BASKET\"\n            disabled\n            \u003e\n\n\u003c/form\u003e\n```\n\n### Select attributes\n\n- Each select tag should have a trigger class assigned to it - in the above example I've used `cascadingDropDown`.\n- The first select should have all items / options ready for selection.\n- All other selects should have the `disabled` attribute.\n- All selects should have their `name` attribute.\n- All selects should have the following `data-*` attributes:\n    - `data-group` : indicates association of the select elements and helps distinguish between different groups where multiple blocks of cascading drop-downs are used.\n- All selects except the last one should have the following `data-*` attribute:\n    - `data-target` : indicates the select element that should be affected by the value selected from its originator\n    - `data-url` : url that needs to be called when the `change` event is triggered on the given select ( should also be applied to the last one if selection includes the replacement of the container - see below )\n- All selects except the first one are also required to have the following `data-*` attributes:\n    - `data-id` : the id of the element corresponds to the `data-target` of the previous element\n    - `data-default-label` : the default label for the first `option` item\n- Optional `data-*` attributes:\n    - `data-replacement` : corresponding container with `data-replacement-container` - used for replacing additional content with each selection.\n\n\n### Instantiating the plugin\n\nTo use the plugin you'll need the latest version of jQuery plus the plugin itself.\n\nTo instantiate the plugin without overwriting any default settings simply call it on the element using its class.\n\n\n```\n\u003cscript src=\"js/jquery-2.1.3.min.js\"\u003e\u003c/script\u003e\n\u003cscript src=\"js/jquery.cascading-drop-down.js\"\u003e\u003c/script\u003e\n\u003cscript\u003e\n    $('.cascadingDropDown').ssdCascadingDropDown();\n\u003c/script\u003e\n```\n\n## Ajax call\n\nWhen the `change` event is triggered (once you've selected item from one of the menus), script collects all selected values associated with their names (taken from the select `name` attribute), and creates a `GET` query string which is appended to the url specified by the `data-url` attribute. It only collects values selected from menus in its own group (indicated by the `data-group` attribute) and which are not disabled. If the menu is not disabled, but doesn't have any selection - it's value is excluded from the query string.\n\n\n## Properties\n\nYou can overwrite the following settings of the plugin:\n\n```\n// data-group attribute\nattrDataGroup                   : 'group',\n\n// data-id attribute\nattrDataId                      : 'id',\n\n// data-url attribute\nattrDataUrl                     : 'url',\n\n// data-target attribute\nattrDataTarget                  : 'target',\n\n// data-default-label attribute\nattrDataDefaultLabel            : 'default-label',\n\n// data-replacement attribute\nattrDataReplacement             : 'replacement',\n\n// data-replacement-container attribute\nattrDataReplacementContainer    : 'replacement-container',\n\n// data-default-content attribute\nattrDataReplacementDefault      : 'default-content',\n\n// class associated with the receiving container\nclassReplacementContainer       : 'cascadingContainer',\n\n// json response key to indicate whether the call was successful (true) or not (false)\nindexSuccess                    : 'success',\n\n// json response key to store the error message\nindexError                      : 'error',\n\n// json response key to store the new menu items\nindexMenu                       : 'menu',\n\n// json response key to store the replacement for the content container\nindexReplacement                : 'replacement',\n\n// whether to run verification with instantiation\nverify                          : true,\n\n// method called before ajax call is made\nstartCall                       : function(trigger, props) {},\n\n// method called after ajax call returned the response\nendCall                         : function(trigger, props) {},\n\n// method to be called on each 'change' event of the select without the 'data-final' attribute\nnonFinalCallback                : function(trigger, props, data, self) {},\n\n// method to be caleld on each 'change' event of the select without the 'data-final' attribute without value\nnonFinalEmptyCallback           : function(trigger, props, self) {},\n\n// method to be called on 'change' event of the select with the 'data-final' attribute\nfinalCallback                   : function(trigger, props, data, self) {},\n\n// method to be called on 'change' event of the select with the 'data-final' attribute without value\nfinalEmptyCallback              : function(trigger, props, self) {},\n\n// method call when json response was not successful { success : false }.\n// It takes the error message plus the all data returned back with the call\nerrorCallback                   : function(message, data) { console.warn(message); }\n```\n\n## Verification\n\nBy default plugin will log the missing `data-*` attributes on all selects when it's first instantiated - so if you check console you might see something like:\n\n```\ncategory is missing attribute data-id\ncategory is missing attribute data-default-label\nsize is missing attribute data-target\ncategory is missing attribute data-id\ncategory is missing attribute data-default-label\nsize is missing attribute data-target\n```\n\nIn the above example the log is referring to the optional parameters on the first and last item.\n\nIf you'd like to disable verification simply pass in the argument `verify` set to `false`:\n\n```\n$('.cascadingDropDown').ssdCascadingDropDown({\n    verify : false\n});\n```\n\n## Start call method\n\nThe `startCall` method is called right before the ajax call is made.\nThe example below shows how to add spinner, that temporarily replaces the select while the call is in progress.\n\n```\n$('.cascadingDropDown').ssdCascadingDropDown({\n\n    startCall: function(trigger, props) {\n    \n        var spinner = '\u003cdiv class=\"spinner\"\u003e\u003ci class=\"fa fa-spinner fa-spin\"\u003e\u003c/i\u003e processing\u003c/div\u003e';\n    \n        trigger.hide();\n        trigger.parent().prepend(spinner);\n    \n    }\n    \n});\n```\n\n## End call method\n\nThe `endCall` method is called right after the ajax call has returned the response.\nThe example below shows how to remove previously added spinner and show the select again.\n\n```\n$('.cascadingDropDown').ssdCascadingDropDown({\n\n    endCall: function(trigger, props) {\n    \n        trigger.parent().find('.spinner').remove();\n        trigger.show();\n    \n    }\n    \n});\n```\n\n## Non final selection callback\n\nThe `nonFinalCallback` method is called with each `change` event on the select element that does not have `data-final` attribute.\nIt takes 4 arguments: `trigger` representing the select object, `props` with all properties of the object, `data` containing json response and `self` which represents the plugin object.\nThe example below illustrates how you can use it to ensure that the submit button is always disabled with the non-final select.\n\n```\n$('.cascadingDropDown').ssdCascadingDropDown({\n\n    nonFinalCallback: function(trigger, props, data, self) {\n\n        trigger.closest('form')\n                .find('input[type=\"submit\"]')\n                .attr('disabled', true);\n\n    }\n    \n});\n```\n\n## Final selection callback\n\nThe `finalCallback` method is called with each `change` event on the select element with the `data-final` attribute - representing last select in the collection.\nIt takes 4 arguments: `trigger` representing the select object, `props` with all properties of the object, `data` containing json response and `self` which represents the plugin object.\nThe example below illustrates how you can use it to enable the submit button if selected value is not empty and disable it otherwise.\n\n```\n$('.cascadingDropDown').ssdCascadingDropDown({\n\n    finalCallback: function(trigger, props, data, self) {\n        if (props.isValueEmpty()) {\n            trigger.closest('form')\n                    .find('input[type=\"submit\"]')\n                    .attr('disabled', true);\n        } else {\n            trigger.closest('form')\n                    .find('input[type=\"submit\"]')\n                    .attr('disabled', false);\n        }\n    }\n    \n});\n```\n\n## Error callback\n\nWhen the ajax call has returned a valid data with the `success` set to false - the `errorCallback` is executed. You can overwrite it with your own error handling by passing the function as `errorCallback` argument. Function takes 2 arguments - first being the actual error message returned in json format - second the whole json response.\n\n```\n$('.cascadingDropDown').ssdCascadingDropDown({\n    errorCallback : function(message, data) {\n\n        alert(message);\n\n    }\n});\n```\n\n## JSON response structure\n\nThe response sent back to the script should use the following format:\n\n```\n{\n  \"success\": true,\n  \"replacement\": \"Records filtered by the make and any previous selection\",\n  \"menu\": [\n    {\n      \"name\": \"White\",\n      \"value\": \"1\"\n    },\n    {\n      \"name\": \"Black\",\n      \"value\": \"2\"\n    },\n    {\n      \"name\": \"Yellow\",\n      \"value\": \"3\"\n    },\n    {\n      \"name\": \"Blue\",\n      \"value\": \"4\"\n    },\n    {\n      \"name\": \"Green\",\n      \"value\": \"5\"\n    },\n    {\n      \"name\": \"Red\",\n      \"value\": \"6\"\n    }\n  ]\n}\n```\n\nThe keys can be overwritten when instantiating the plugin:\n\n```\n$('.cascadingDropDown').ssdCascadingDropDown({\n    indexSuccess        : 'isSuccess',\n    indexError          : 'errorMessage',\n    indexMenu           : 'items',\n    indexReplacement    : 'content'\n});\n```\n\nWith the above overwritten, the response would now be:\n\n```\n{\n  \"isSuccess\": true,\n  \"errorMessage\" : \"\", // you can omit it if success is set to true\n  \"content\": \"Records filtered by the make and any previous selection\",\n  \"items\": [\n    {\n      \"name\": \"White\",\n      \"value\": \"1\"\n    },\n    {\n      \"name\": \"Black\",\n      \"value\": \"2\"\n    },\n    {\n      \"name\": \"Yellow\",\n      \"value\": \"3\"\n    },\n    {\n      \"name\": \"Blue\",\n      \"value\": \"4\"\n    },\n    {\n      \"name\": \"Green\",\n      \"value\": \"5\"\n    },\n    {\n      \"name\": \"Red\",\n      \"value\": \"6\"\n    }\n  ]\n}\n```\n\n## Content replacement\n\nWith each select, apart from fetching new set of `option`s for the next drop-down, you can also update the content of some container.\n\nThe value for the container is passed with the json response with the default index of `replacement`, which can obviously be overwritten (see above).\n\nTo do this, you need to add the `data-replacement` attribute to each `select` tag that you want to update the container with the value corresponding to container's `data-replacement-container` attribute.\n\n```\n\u003cselect\n    name=\"category\"\n    class=\"cascadingDropDown\"\n\n    data-group=\"product-1\"\n    data-target=\"make\"\n    data-url=\"data/make.json\"\n    data-replacement=\"container1\"\n\n    \u003e\n    \u003coption value=\"\"\u003eSelect category\u003c/option\u003e\n    \u003coption value=\"1\"\u003eShoes\u003c/option\u003e\n    \u003coption value=\"2\"\u003eT-shirts\u003c/option\u003e\n    \u003coption value=\"3\"\u003eJeans\u003c/option\u003e\n    \u003coption value=\"4\"\u003eHats\u003c/option\u003e\n    \u003coption value=\"5\"\u003eBelts\u003c/option\u003e\n    \u003c/select\u003e\n```\n\nContainer should also have a `data-default-content` attribute, which is to store some default message when the page first loads and when the first menu in the group selects the first (empty) option.\nIt should also have a `class` attribute set to `cascadingContainer`.\n\n```\n\u003cdiv\n    class=\"cascadingContainer\"\n    data-replacement-container=\"container1\"\n    data-default-content=\"To see results please make your selection using menus above.\"\n    \u003e\u003c/div\u003e\n```\n\nAgain, you can overwrite these attributes like so:\n\n```\n$('.cascadingDropDown').ssdCascadingDropDown({\n    attrDataReplacementContainer    : 'rep-container',\n    attrDataReplacementDefault      : 'def-content'\n    classReplacementContainer       : 'container'\n});\n```\n\nWith the above, the structure of our container would now be:\n\n```\n\u003cdiv\n    class=\"container\"\n    data-rep-container=\"container1\"\n    data-def-content=\"To see results please make your selection using menus above.\"\n    \u003e\u003c/div\u003e\n```\n\nThe default content is placed within the container on plugin instantiation so no need for doing it manually.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsebastiansulinski%2Fcascading-dropdown","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsebastiansulinski%2Fcascading-dropdown","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsebastiansulinski%2Fcascading-dropdown/lists"}