Nested List -> Tree jQuery
Based on http://homework.nwsnet.de/news/ea21_turn-nested-lists-into-a-collapsible-tree-with-jquery
by donabrams
HTML
<p>This is outdated. Please use <a href="http://jsfiddle.net/donabrams/MLnRg/">http://jsfiddle.net/donabrams/MLnRg/</a> instead.
</p>
<ul class="tree">
<li>item 1
<ul>
<li>item 1.1</li>
<li>item 1.2</li>
<li>item 1.3</li>
</ul>
</li>
<li>item 2
<ul>
<li>item 2.1
<ul>
<li>item 2.1.1</li>
<li>item 2.2.2</li>
</ul>
</li>
</ul>
</li>
<li>item 3</li>
</ul>
<input type="test" name="something">
<p>This is actually called a "tree" and has ARIA extensions:
<a href="http://www.w3.org/TR/wai-aria/roles#tree">http://www.w3.org/TR/wai-aria/roles#tree</a></p>
<p>
This is the gist of it:
<ul>
<li>Structure is of nested <ul>s</li>
<li>Tree has one outer <ul> with the role "tree"</li>
<li>Inner <li> have the role "treeitem"</li>
<li>Inner <ul> have the role "group" not "treeitem"</li>
<li>Expanded lists with aria-expanded=true</li>
<li>Collapsed lists with aria-expanded=false</li>
<li> <li>s have aria-labelledby attribute.</li>
<li>The currently selected element has a tabindex of 0, while all
others in the tree have a tabindex of -1. This means tab skips the
entire list while up/down/left/right navigates, contracts, or expands
the lists.</li>
</ul>
</p>
CSS
ul.tree, ul.tree ul {
list-style: none;
margin: 0;
padding: 0;
}
ul.tree li {
background-color: red;
background-position: 0 1px;
background-repeat: no-repeat;
padding-left: 20px;
margin-left: 0px;
}
ul.tree li.folder {
background-color: yellow;
}
a {
color: #000000;
cursor: pointer;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
ul.tree .leaf-hidden {
display: none;
}
ul {
list-style: disc;
}
li {
margin-left: 20px
}
JavaScript
// Add the aria role tree to the parent list
var trees = $('ul.tree').attr("role", "tree");
var i = 0;
$('li > ul', trees).each(function(i) {
var sub_ul = $(this);
// Add the aria role treeitem to the parent list item
var parent_li = sub_ul.parent('li').attr("role", "treeitem");
// Temporarily remove the sub list so a
// link can be created aroud the rest of the content
sub_ul.remove();
parent_li.addClass('folder')
// Add the aria-role
.attr("role", "group")
// Create the link
.wrapInner('<a/>')
// Add the sub list back
.append(sub_ul)
.find('a')
.attr("href", "javascript:void(0);")
.attr("selectable", "selectable")
// Set the link id to be used by aria-labeled by later
.attr("id", "folder-label-" + (++i))
// Set click to toggle expansion
.click(function(event) {
sub_ul.toggleClass("leaf-hidden")
.attr("aria-expanded",!sub_ul.hasClass("leaf-hidden"));
event.stopPropagation();
})
// Set tab index to -1 on blur
.blur(function(event) {
$(this).attr("tabindex", -1);
event.stopPropagation();
})
// Set tab index to 0 on focus
.focus(function(event) {
$(this).attr("tabindex", 0);
event.stopPropagation();
})
// Make right key expand the list, and left collapse it
// Make up travel to the last link, down to the next
.keydown(function(event) {
if (event.keyCode === 37) {
sub_ul.addClass("leaf-hidden")
.attr("aria-expanded",false);
event.stopPropagation();
} else if (event.keyCode === 39) {
sub_ul.removeClass("leaf-hidden")
...