Creating a collapsible and expandable menu tree from XML with XSL: A step-by-step guide

My XML file is structured as follows:

<WORLD>
<COUNTRY COUNTRYID="INDIA">
<STATE STATEID="ANDHRAPRADESH">
<CITY>HYDERABAD</CITY>
<CITY>VIZAG</CITY>
<CITY>KURNOOL</CITY>
</STATE>
<STATE STATEID="TAMILNADU">
<CITY>CHENNAI</CITY>
<CITY>COIMBATORE</CITY>
<CITY>SALEM</CITY>
</STATE>
<STATE STATEID="KARNATAKA">
<CITY>BANGALORE</CITY>
<CITY>MYSORE</CITY>
<CITY>BALLARI</CITY>
</STATE>
</COUNTRY>
<COUNTRY COUNTRYID="AUSTRALIA">
<STATE STATEID="NEW SOUTH WALES">
<CITY>PERTH</CITY>
<CITY>BRIABANE</CITY>
<CITY>HOBART</CITY>
</STATE>
</COUNTRY>
</WORLD>

I am looking to create an expandable/collapsible tree structure using XSL and possibly some javascript, complete with plus and minus signs.

> WORLD  |  |
>     INDIA
>           |
>           |
>            ANDHRAPRADESH
>                      |
>                      |
>                   HYDERABAD
>                   VIZAG
>                   KURNOOL
>            KARNATAKA
>                      |
>                      |
>                   BANGALORE
>                   BALLARI   
>             AUSTRALIA  
>                |
>                |
>             NEW SOUTH WALES
>                    |
>                    |
>                    PERTH
>                    BRIABANE

Is it possible to achieve this using just XSL, along with the potential addition of some javascript or pure CSS?

The sample XML I have provided is from a general form of the problem mentioned. My goal is to create a similar interactive tree structure like the one demonstrated here, but solely through XSL transformation of my XML document. Any suggestions?

Note: I do not have much experience with XSL or javascript, however, I do have some familiarity with Python. If there is a Python library or solution that can help, please advise.

Answer №1

If you wish to implement the code provided in the link, below is a sample stylesheet that creates a nested, unordered ul list structure. It uses the Javascript and CSS code along with images from the linked sample; however, in a real scenario, you may need to copy the images and host them on your server:

<xsl:stylesheet
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output method="html" version="5.0" indent="yes" doctype-system="about:legacy-compat"/>

<xsl:template match="/">
  <html>
    <head>
      <title>list test</title>
      <style>
/********************/
/* EXPANDABLE LIST  */
/********************/
#listContainer{
  margin-top:15px;
}

#expList ul, li {
    list-style: none;
    margin:0;
    padding:0;
    cursor: pointer;
}
#expList p {
    margin:0;
    display:block;
}
#expList p:hover {
    background-color:#121212;
}
#expList li {
    line-height:140%;
    text-indent:0px;
    background-position: 1px 8px;
    padding-left: 20px;
    background-repeat: no-repeat;
}

/* Collapsed state for list element */
#expList .collapsed {
    background-image: url(http://jasalguero.com/demos/expandableList/img/collapsed.png);
}
/* Expanded state for list element
/* NOTE: This class must be located UNDER the collapsed one */
#expList .expanded {
    background-image: url(http://jasalguero.com/demos/expandableList/img/expanded.png);
}
#expList {
    clear: both;
}

.listControl{
  margin-bottom: 15px;
}
.listControl a {
    border: 1px solid #555555;
    color: #555555;
    cursor: pointer;
    height: 1.5em;
    line-height: 1.5em;
    margin-right: 5px;
    padding: 4px 10px;
}
.listControl a:hover {
    background-color:#555555;
    color:#222222; 
    font-weight:normal;
}      
      </style>
      <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
      <script>
/**************************************************************/
/* Prepares the cv to be dynamically expandable/collapsible   */
/**************************************************************/
function prepareList() {
    $('#expList').find('li:has(ul)')
    .click( function(event) {
        if (this == event.target) {
            $(this).toggleClass('expanded');
            $(this).children('ul').toggle('medium');
        }
        return false;
    })
    .addClass('collapsed')
    .children('ul').hide();

    //Create the button funtionality
    $('#expandList')
    .unbind('click')
    .click( function() {
        $('.collapsed').addClass('expanded');
        $('.collapsed').children().show('medium');
    })
    $('#collapseList')
    .unbind('click')
    .click( function() {
        $('.collapsed').removeClass('expanded');
        $('.collapsed').children().hide('medium');
    })

};


/**************************************************************/
/* Functions to execute on loading the document               */
/**************************************************************/
$(document).ready( function() {
    prepareList()
});      
      </script>
    </head>
    <body>
      <xsl:apply-templates/>
    </body>
  </html>
</xsl:template>

<xsl:template match="WORLD">
        <div id="listContainer">
            <div class="listControl">
                <a id="expandList">Expand All</a>
                <a id="collapseList">Collapse All</a>
            </div>
            <ul id="expList">
              <li>World
                 <xsl:apply-templates/>
              </li>
            </ul>
         </div>
</xsl:template>

<xsl:template match="COUNTRY">
  <ul>
    <li><xsl:value-of select="@COUNTRYID"/>
      <xsl:apply-templates/>
    </li>
  </ul>
</xsl:template>

<xsl:template match="STATE">
  <ul>
    <li><xsl:value-of select="@COUNTRYID | @STATEID"/>
      <ul>
        <xsl:apply-templates/>
      </ul>
    </li>
  </ul>
</xsl:template>

<xsl:template match="CITY">
  <li>
    <xsl:apply-templates/>
  </li>
</xsl:template>

</xsl:stylesheet>

The XML document then simply refers to above XSLT with

<?xml-stylesheet type="text/xsl" href="sheet1.xsl"?>
<WORLD>
<COUNTRY COUNTRYID="INDIA">
<STATE STATEID="ANDHRAPRADESH">
<CITY>HYDERABAD</CITY>
<CITY>VIZAG</CITY>
<CITY>KURNOOL</CITY>
</STATE>
<STATE STATEID="TAMILNADU">
<CITY>CHENNAI</CITY>
<CITY>COIMBATORE</CITY>
<CITY>SALEM</CITY>
</STATE>
<STATE STATEID="KARNATAKA">
<CITY>BANGALORE</CITY>
<CITY>MYSORE</CITY>
<CITY>BALLARI</CITY>
</STATE>
</COUNTRY>
<COUNTRY COUNTRYID="AUSTRALIA">
<STATE STATEID="NEW SOUTH WALES">
<CITY>PERTH</CITY>
<CITY>BRIABANE</CITY>
<CITY>HOBART</CITY>
</STATE>
</COUNTRY>
</WORLD>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Twitter bootstrap does not support the display of Glyphicons

Having trouble getting glyphicons to display properly. Currently, a small square is appearing instead of the glyphicon symbol. I have attempted to reposition folders and troubleshoot without success. I did not customize bootstrap - I simply downloaded the ...

Styling Result received from Web API call within an array

I am currently working on a function call that retrieves the result of an SQL query in an array format. app.get('/alluser', (req, res) => { console.log("All Users") user.getAllUsers(function (err, result) { if (!err) ...

Observing changes in VueJS using $watch and accessing DOM elements

How can I monitor changes in Vue $refs? I'm trying to apply some logic to a child component that is nested within my current Vue instance. However, when trying to access '$refs.childcomponent' inside the 'ready' callback, it appea ...

Achieve inline or floating displays seamlessly without the need for media queries to prevent breaking

Whenever I attempt to apply float left or display inline, it causes issues. The form currently has a max-width of 1000px. I was hoping that the first and last names would automatically align side by side if there is enough space. Perhaps setting a min-widt ...

How can I incorporate javaScript libraries such as camanjs, lena.js, and jimp into my Angular application?

How can I import image processing libraries like camanjs, lena.js, and jimp into my Angular application for frontend image manipulation? I have successfully used tesseract.js to extract text from images but now need to process the images themselves. Whil ...

Step-by-step guide: How to serialize a form using Express

Having trouble obtaining form data in Express after submitting with serialize. Jquery: function submitSettingsCustom() { $('#reception-source-custom-form, expedition-source-custom-form').on("submit", function(event) { al ...

Customizing Column Visibility in AgGrid React

In my React application, I have a backend (BE) that returns an Array of objects representing columns. Each object has the structure {headerName: 'string', field: 'string', visible: boolean}. However, the 'visible' parameter se ...

Utilizing JavaScript to access and parse an XML file within a client-side environment

I've recently started learning JavaScript and I'm facing some challenges while trying to reference an xml file uploaded by a user. My goal is to eventually parse the XML contents, update them, and allow users to download the modified file. Here ...

Error is caused by state variable triggering mutation

In my store module, I am encountering an issue with the following pseudo-code: const state = { users: [] } const actions = { addUsers: async ({commit, state}, payload) => { let users = state.users // <-- problem // fetching new users fo ...

Utilizing HighCharts in Your Project

View the graph here $(function () { var chart; $(document).ready(function() { chart = new Highcharts.Chart({ chart: { renderTo: 'container', type: 'column', margin: [ 50, 50, 100, 80] ...

"Potential Memory Leak Issue: Assigning dataUrl to img.src May Cause Memory

Here is a demonstration of a simple test case where setting an img tag's src to different dataUrls leads to memory leakage. It appears that the image data is not unloaded even after the src is changed. <!DOCTYPE html> <html> <head> ...

Encountering issues with custom.css.scss not being properly overridden in Rails while using Bootstrap

My Configuration I made modifications to the .col-lg-4 class in Bootstrap for Rails and added them to my custom.css.scss file after the import statements to remove padding completely. The Issue at Hand I am trying to override the styling of .embed-respo ...

Struggling to establish a connection with AngularJS

I recently got my hands on the AngularJS SPA template for Visual Studio and dove straight into my first application. However, I'm already encountering a multitude of issues! Here's a glimpse of my PersonView : <!DOCTYPE html> <html ng ...

Customizing an external module class using CSS module pattern in React

Is there a way to locally override an external module's CSS class for a specific component? Here is my current code: import `style` from './style.module.css' // local CSS module import `ExternalComponent` from 'ExternalComponent&apos ...

What causes the occurrence of the error message "Please input a valid date" in jquery.validate.js?

I'm currently using jQuery UI version 1.12.1 and jQuery Validation v1.15.0 To set up a datepicker, I used the following code: if ($.fn.datepicker) $(':input.date').datepicker({ changeMonth: true, changeYear: true }) ...

Guide on showcasing the most recent four blog entries using JSON API within a div element

How can I showcase only the most recent four blog posts from the WordPress API on my HTML website? HTML: <div id="content" class="content"></div> JavaScript: <script> $(document).ready(function() { $.getJSON("https://startupet.c ...

My app.js failed to launch on Heroku, receiving a Code H10 status 503 error

Starting with some screenshots: https://i.sstatic.net/E0pyj.png https://i.sstatic.net/SkZDv.png https://i.sstatic.net/HJ3Iw.png https://i.sstatic.net/LKFv2.png The full error log is below: 2020-06-15T10:46:45.640400+00:00 heroku[web.1]: Starting pro ...

Increasing the margin-left automatically in Bootstrap 3.0.0

I am looking to automatically generate margin-left in the ".card" class "style" element every time a post is entered on a page. My jQuery version is 1.12.4 This is my idea: If the .card CSS style has a margin-left of 0 and width of 479px, set position to ...

Stagnant updates in component's styling

I have a list box style stored in the component's data. It is bound to a listbox element. One of the styles within it is position: absolute. My goal is to position the box directly below the input element. To achieve this, I am trying to dynamically m ...

Ways to obtain the value associated with a particular key within an object and its descendant elements?

When working with form outputs, I encountered an object structure that includes field names and a key "value" representing the value. Sometimes, the field itself is another object with its own "value" key. My goal is to extract only the values stored withi ...