First of all, there are 2 statements that I do not understand (lines 7-8):
var areaOption = []; dropdownObject.push('<option value=""></option>');
Maybe the first one is useful somewhere else, so good (although you are probably better off putting it outside the success: function).
But I do not understand how the second can be legal.
The structure of the source data.
As for the nested levels, I am surprised that you have 3 levels, while it seems that only 2 can do the job. When there are children, you build something like this:
<optgroup label="level-1-text"> <option value="level-3-id">Level-2 text: Level-3 text</option> </optgroup>
where level 2 is only useful for getting "level 2 text."
Therefore, it seems schematically that the corresponding source structure was as follows:
calling Id: "~some guid~" returns {Id: "level-1-id", Text: "Level-1 text", HasChildren: true} >>> <optgroup label="level-1-text"> calling Id: "level-1-id" returns {Id: "level-2-id", Text: "Level-2 text", HasChildren: true} >>> (nothing) calling Id: "level-2-id" returns {Id: "level-3-id", Text: "Level-3 text", HasChildren: false} >>> <option value="level-3-id">Level-2 text: Level-3 text</option>
So, if you have a hand in the source data structure, you'd better use something like this:
calling Id: "~some guid~" returns {Id: "level-1-id", Text: "Level-1 text", HasChildren: "Level-2 text"} >>> <optgroup label="level-1-text"> calling Id: "level-1-id" returns {Id: "level-3-id", Text: "Level-3 text", HasChildren: false} >>> <option value="level-3-id">Level-2 text: Level-3 text</option>
Note: here I saved the names "level-2" and "level-3" for consistency, whereas now there are only 2 levels.
Using jQuery.
I was surprised that you $(data).each(function() over the resulting JSON array using $(data).each(function() : this form is designed to handle jQuery objects, whereas for arrays and other non-jQuery objects we should use $.each(data, function() .
So, I had some tests, and I was surprised to find that this works too, although it somehow shouldn't! You can find a detailed study reported in the question I posted about this .
Another point is how you address each element that has passed the above iteration: $(this)[0].HasChildren unnecessarily complex because it sequentially encapsulates this into a jQuery object and then retrieves it!
You should just use this.HasChildren .
The proposed simple way to achieve all the work.
Building HTML as a linear stream of strings is not an easy path because:
- closing tag for
<optgroup> should appear _after internal <option> been created - but asynchronous Ajax mode is hard to figure out when to do this
In contrast, it is easy to use jQuery to build <option> and <optgroup> each in turn when necessary.
Note that this means that for this we must use jQuery.append() instead of jQuery.html() .
Regarding the nested aspect of the source data, the easiest way is to use recursion rather than using $.ajax several times at nested levels.
It also has the advantage of reducing code and making it more readable.
Here is a snippet based on all of the above considerations.
Note that I also simplified this by following my previous sentence, just use HasChildren (renamed Children for clarity) to provide information on nested <optgroup> . For <option> it should not even be present.
function buildMenu(Id, $parent, prependage) { $parent = $parent || $('#dropdown'); $.ajax({ url:"../../api/getEnum", data: {itemFilter: "", Id: Id}, dataType: "json", success: function(data) { $.each(data, function() { if (!this.Children) { // create a simple <option> $parent.append('\ <option value="' + this.Id + '">\ ' + (!!prependage ? (prependage + ': ') : '') + this.Text + '\ </option>\ '); } else { // create an <optgroup> $parent.append('\ <optgroup label="' + this.Text + '">\ </optgroup>\ '); // call for <option>s in this optgroup buildMenu(this.Id, $('optgroup:last', $parent), this.Children); } }); } }) } buildMenu('~some guid~');
Of course, I tested a slightly modified version, which you can check, works as expected:
<?php $menus = [ '~some guid~' => [ ["Id" => "level-1-1", "Text" => "Text-1-1"], ["Id" => "level-1-2", "Text" => "Text-1-2"], ["Id" => "level-1-3", "Text" => "Text-1-3", "Children" => "level-1-3"], ["Id" => "level-1-4", "Text" => "Text-1-4"], ["Id" => "level-1-5", "Text" => "Text-1-5", "Children" => "level-1-5"], ], 'level-1-3' => [ ["Id" => "level-1-3-1", "Text" => "Text-1-3-1"], ["Id" => "level-1-3-2", "Text" => "Text-1-3-2"], ["Id" => "level-1-3-3", "Text" => "Text-1-3-3"], ], 'level-1-5' => [ ["Id" => "level-1-5-1", "Text" => "Text-1-5-1"], ["Id" => "level-1-5-2", "Text" => "Text-1-5-2"], ["Id" => "level-1-5-3", "Text" => "Text-1-5-3"], ], ]; if ($Id = @$_REQUEST['Id']) { echo json_encode($menus[$Id]); exit; } ?> <select id="dropdown"> </select> <script src="http://code.jquery.com/jquery-1.11.3.js"></script> <script> function buildMenu(Id, $parent, prependage) { $parent = $parent || $('#dropdown'); $.ajax({ url:'', data: {itemFilter: "", Id: Id}, dataType: "json", success: function(data) { $.each(data, function() { if (!this.Children) { </script>