Interactive HTML Trees with no JavaScript and no CSS
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.
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:
Younger readers are probably familiar with how Reddit displays threads:
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:
HTML<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:
HTML<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.
Python 3from 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,
Python 3# Ordered list of nodes by depth
nodeList = list(tree.expand_tree(mode=Tree.DEPTH))
Keep track of which depth we're at
Python 3# 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!
Dan Brickley said on twitter.com:
thanks for writing this up! any thoughts on the accessibility issues you mentioned?
Dan Brickley said on twitter.com:
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
Rayan said on twitter.com:
You could potentially work around the a11y issues by using <ul> tags and some rather questionable CSS: blog.alexdevero.com/multi-level-sl…