Interactive HTML Trees with no JavaScript and no CSS

by @edent | # # | 3 comments | Read ~2,794 times.

Many text based conversations threads can be visualised as a tree. This is a follow-up to yesterday’s blog post about Twitter conversation trees.

Mailing list archives often use nested <ul> to show a conversation.

An indented list of messages.

That’s fine, but has the major drawback of not being interactive. There’s no way to collapse a branch of a tree if you’re not interested in that strand of the conversation.

Older readers may be familiar with how USENET displayed threads:
An old fashioned interface showing messages about Bob Dylan.

Younger readers are probably familiar with how Reddit displays threads:

A visual list of indented messages about soup.

That uses a lot of JavaScript to make the tree interactive – that is, being able to open and close sub-branches of a conversation.

Is there a way to create an interactive HTML tree without JavaScript?

The <details> and <summary> elements are one of the few interactive parts of the HTML5 spec which don’t require JavaScript. Here’s how they work:

This is the summary. Click me!

I am the content inside the detail. Click the summary to close.

The code for that is:

<details>
   <summary>This is the summary. Click me!</summary>
   <p>I am the content inside the detail. Click the summary to close.</p>
</details>

So, we can nest <details> within <details> and make an interactive tree!

WARNING! This is a misuse of these HTML elements. While it is valid markup, it may not be accessible. This is an experiment and not intended for production use.

What’s your favourite Pizza topping? (2 replies)
I like pineapple (1 reply)
What’s wrong with you? (0 replies)
Sweetcorn is my favourite! (0 replies)

And the code is:

<details>
   <summary>Top level message (2 replies)</summary>
   <details>
      <summary>2nd level message (1 reply)</summary>
      <details open>
         <summary>3rd level message (0 replies)</summary>
      </details>
   </details>
   <details open>
      <summary>2nd level message (0 replies)</summary>
   </details>
</details>

Python Tree To HTML Tree

Python has a number of Tree structure data-types. I use TreeLib.

Here’s how to create a Tree. This assumes that every Node has its own ID and references the ID of its parent.

from treelib import Node, Tree
tree = Tree()
tree.create_node("root", "1")  # root node
tree.create_node("reply", "2", parent="1")
tree.create_node("deeper", "3", parent="2")
tree.create_node("much deeper", "4", parent="3")
tree.create_node("another", "5", parent="2")
tree.create_node("top level reply", "6", parent="1")
tree.show()

This is what the Tree looks like:

root
├── reply
│   ├── another
│   └── deeper
│       └── much deeper
└── top level reply

Here’s how to walk / traverse the tree and output nested HTML. (I’m sure there are more efficient ways of doing this!)

First, get a list of nodes,

#  Ordered list of nodes by depth
nodeList = list(tree.expand_tree(mode=Tree.DEPTH))

Keep track of which depth we’re at

#  Keep track of which depth we're at
previousDepth = 0
#  Set up the output
htmlOutput = ""

#  Traverse the tree
for n in nodeList:
    #   How deep are we?
    currentDepth = tree.depth(n)
    #   Text to display
    messageText = tree.get_node(n).tag
    #   How many *direct* children of this node?
    numberOfChildren = len(tree.children(n))
    #   Pluralise the (number of replies) text
    replyText = "reply" if numberOfChildren == 1 else "replies"
    #   If this is the root level, there's nothing to do
    if (currentDepth == 0):
        pass
    #   If this is the same level as the previous message, keep the <details> element unclosed
    elif (currentDepth == previousDepth):
        htmlOutput += "\n"
    #   If we're less deep, we need to close at least one <details>
    elif (currentDepth < previousDepth):
        for x in range(0, (previousDepth-currentDepth)):
            htmlOutput += "</details>"
    #   Add the number of replies
    if (numberOfChildren == 0):
        #   Set the details as open
        htmlOutput += "<details open><summary>" + messageText + "</summary></details>"
    else:
        htmlOutput += "<details><summary>" + messageText + " (" + str(numberOfChildren) + " "+replyText+")</summary>"
    #   All done, reset the depth
    previousDepth = currentDepth

htmlOutput +="</details>"
print(htmlOutput)

I doubt this is the best, or most Pythonic, way to do it. But it works!

3 thoughts on “Interactive HTML Trees with no JavaScript and no CSS

  1. thanks for writing this up! any thoughts on the accessibility issues you mentioned?


  2. there seems to be an ARIA role=tree that might be helpful? (I don’t know what I don’t know on all this)

    eg levelaccess.com/creating-an-ac… has links to a few articles


  3. Rayan says:

    You could potentially work around the a11y issues by using <ul> tags and some rather questionable CSS: blog.alexdevero.com/multi-level-sl…


Leave a Reply

Your email address will not be published. Required fields are marked *