'Dynamic XML flow problem with jQuery parsing to HTML page

The problem I have is that this jQuery:

<script type="text/javascript">
    $(document).ready(function(){
        $.get('CampusList.xml', function(xmltree){
            $('#settings').before('<ul id="campuses">');
            $(xmltree).find('campus').each(function(){
                $('#settings').before('<li class="campus">'+$(this).attr('name')+'<ul id="buildings">');
                $(this).find('building').each(function(){
                    $('#settings').before('<li class="building">'+$(this).attr('name')+'</li>');
                });
                $('#settings').before('</ul>');
                $('#settings').before('</li>');
            });
            $('#sidebar').before('</ul>');
        });
    });
</script>

And this is the associated XML:

<campuses name='Campuses'>
  <campus name='Clearwater'>
    <building name='Clearwater Building 1' img='someimg.png' />
    <building name='Clearwater Building 2' img='someimg.png' />
    <building name='Clearwater Building 3' />
  </campus>
  <campus name='Saint Petersburg'>
    <building name='St. Pete Building 1' />
    <building name='St. Pete Cafe Building' />
    <building name='Other Building' />
    <building name='Secret Chiller Plant Connection' />
  </campus>
  <campus name='Epicenter'> 
    <building name='District Offices' img='epi_do_floor1.png' />
    <building name='Services' img='epi_services.png' />
    <building name='Tech Building' img='epi_tech.png' />
  </campus>
</campuses>

And finally, this is the erroneous output: (I screenshotted the image from chrome's inspector because otherwise it would be all garbled.)

HTML output



Solution 1:[1]

The problem is that your logic is trying to build the DOM as a string, which is not what happens when you use jQuery.

When you're doing the following:

$('#settings').before('<ul id="campuses">');

jQuery puts it into the DOM properly, closing the tag. That's why you've got an empty <ul id="campuses"></ul> at the top.

The solution is to build your output as a tree, saving the parent references as you go, and append the nested elements into their respective containers.

Something like this should work:

<script type="text/javascript">
    $(document).ready(function(){
        $.get('CampusList.xml', function(xmltree){
            var $campuses = $('<ul id="campuses">').insertBefore('#settings');
            $(xmltree).find('campus').each(function(){
                var $campus = $('<li class="campus">'+$(this).attr('name')+'</li>').appendTo($campuses);
                var $buildings = $('<ul id="buildings">').appendTo($campus);
                $(this).find('building').each(function(){
                    $buildings.append('<li class="building">'+$(this).attr('name')+'</li>');
                });
            });
        });
    });
</script>

Solution 2:[2]

@DarthJDG, while your answer does work, I much prefer the simplicity of the modified version that I made that still utilizes append but just takes advantage of the :last css pseudoclass for a selector. Here's the code:

<script type="text/javascript">
$(document).ready(function(){
    $.get('CampusList.xml', function(xmltree){
        $(xmltree).find('campus').each(function(){
            $('#campuses').append('<li class="campus">'+$(this).attr('name')+'<ul class="buildings">');
            $(this).find('building').each(function(){
                $('.buildings:last').append('<li class="building hidden" img="'+$(this).attr('img')+'">'+$(this).attr('name')+'</li>');
            });
        });
    });
});
</script>

I would feel bad marking my answer as the right one though, so I'll give it to you, but I think my approach is probably a bit more efficient.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 DarthJDG
Solution 2 Vap0r