0

I've seen more then a few a few posts on SO which are looking to take a list of links and turning them into a drop-down. I've taken those examples and have not had too much luck applying them to my markup which is a bit different. I need to turn a nested list of radio buttons with labels and convert them into a drop down list with <optgroup> headings, removing nested radio inputs and labels.

Basically the list of radio buttons Im working with is a wildly out of control and a clean drop down list would be much better usability and screen real estate. Once I handle the conversion, the plan is to hide the original radio button set, and then map the user drop-down selection to the corresponding radio buttons to be process on submission of the form. But first I need to generate the drop-down list . . .

Here is a simplified sample of the starting markup:

<ul>
    <li>
        <label><input type="radio">USA</label>
        <ul class="children">
            <li>
                <label><input type="radio" >Northeast</label>
                <ul class="children">
                    <li>
                        <label><input itype="radio">Mid-Atlantic</label>
                    </li>
                    <li>
                        <label><input type="radio">New England</label>
                    </li>
                </ul>
            </li>
            <li>
                <label><input type="radio">South</label>
            </li>
            <li>
                <label><input type="radio">West</label>
            </li>
        </ul>
    </li>
    <li>
        <label><input type="radio" checked="checked">No Venue Region</label>
    </li>
</ul>

Im trying to convert the above to the following:

<select>
    <optgroup label="USA">
        <optgroup>Northeast</option>
            <option>Mid-Atlantic</option>
            <option>New England</option>
        <optgroup>
        <option>South</option>
        <option>West</option>
    </optgroup>
    <option>No Region</option>
</select>

You might notice that the top level <ul> in the first example, also has a radio button associated with it –– but this is not desired as we want the user to be as precise a possible in their selection. These should have been headings, but unfortunately I have no control of what's generated.

They type of solution I favour is to use a function to process the existing markup into a new entity which I can then append to the parent DOM element after hiding the original.

In this SO thread @Enki offers a solution with this approach, but I've not been able to apply it, as I just cant quite understand how its iterating though all the elements. His solution goes like this:

function sitemapCycle(){
        if(typeof(sitemapNode)==="undefined") sitemapNode= $("#sitemap");
        if($(this).find("ul").length)
        {
            sitemapNode= $(sitemapNode).append('<optgroup label="'+$(this).children().first().text()+'" />').children().last();
            $(this).find("ul li").each(sitemapCycle);
            sitemapNode= $(sitemapNode).parent();
        }
        else
        {
             $(sitemapNode).append('<option value="'+$(this).children().attr("href")+'">'+$(this).text()+'</option>');
        }
    }
    var sitemapNode;
    $("#sitemap").removeAttr("id").after('<select id="sitemap" />').children().each(sitemapCycle).parent().remove();

The closest I've been able to get, which doesn't use @Enki's approach, and doesn't create optgroup top levels and is truly incomplete is this:

$(function() {
    $('#venue-regionchecklist').each(function() {
            $(this).find('label').each(function() {
                var cnt = $(this).contents();
                $(this).replaceWith(cnt);
            });
        var $select = $('<select />');
        $(this).find('li').each(function() {
            var $option = $('<option />');
             $(this).html($(this).html());
            $option.attr('id', $(this).attr('id')).html($(this).html());
            $select.append($option);
        });
        $(this).replaceWith($select);
    });
});

Here is a unsimplified sample of the radio list if anyone wants to take a whack at it. I would love to know how you would approach this kind of problem.

Community
  • 1
  • 1
orionrush
  • 566
  • 6
  • 30

1 Answers1

1

Cheers mate!

I believe recursivity can be of help here:

function buildDropDownFromUL(CurrentUL, level) {
   var dropDownHTML = "";
   CurrentUL.children("li").each(function () { // Cycle through all LI elements
      if ($(this).children("ul").length == 0) {
         // There is no UL element in this LI so it means LI is a selectable option
         dropDownHTML = dropDownHTML + "<option value='" + $(this).children("label").children("input").attr("value") + "'>" + NBSPs(level) + $(this).children("label").text() + "</option>";
      } else {
         // There is a UL in the LI so it means the LABEL in the current LI is a OPTGROUP 
         // and the UL should be again processed
         dropDownHTML = dropDownHTML + "<optgroup label='" + NBSPs(level) + $(this).children("label").text() + "'>" + "</optgroup>" + buildDropDownFromUL($(this).children("ul"), level + 1);
      }
   });
   return dropDownHTML + "<optgroup label='&nbsp;'></optgroup>";
}

There is something to watch for: the function assumes that if a given LI element contains only a LABEL element (and no UL element) then that LABEL is a selectable option and if the LI contains a LABEL and a UL then the LABEL is a option group.

And here is the the Fiddle

I hope it helps!

Fabricio
  • 839
  • 9
  • 17
  • @Fabrico sorry for the long delay in saying thank you. This works perfectly for my needs, and your comments thought make it especially useful for me to learn how everything is operating. – orionrush Oct 19 '13 at 22:44
  • @Fabrico also the `NBSPs(level)` function is a really nice touch for indention! – orionrush Oct 20 '13 at 09:31
  • @ orionrush Cool! I'm glad it worked for you. And thanks for coming back to it after all this time! :-) – Fabricio Oct 21 '13 at 22:49