JSFiddle - React, Tailwind, and code Playground
by Haze32
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.14.1/jquery-ui.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.fancytree/2.38.4/jquery.fancytree.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/src/jquery.fancytree.childcounter.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/src/jquery.fancytree.filter.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.contextMenu.min.js"></script>
<h1>Handbook Tree Structure</h1>
<p style="margin-bottom: -5px;"><input type="text" id="filterInput" placeholder="Search by title or HWID..." /><button id="btnResetSearch" disabled="disabled">×</button><span id="matches"></span></p>
<p class="note">Note: Folders greyed out do not contain search object. Folders with regular text contain the object. Bold text match the search object</p>
<fieldset>
<legend>Options</legend>
<br>
<label for="toggleFilterMode">
<input type="checkbox" id="toggleFilterMode">
Hide unmatched nodes
</label>
<label for="toggleAutoExpand">
<input type="checkbox" id="toggleAutoExpand">
Auto expand
</label>
</fieldset>
<div id="tree"></div>
CSS
span.fancytree-node:not(.fancytree-folder) .fancytree-icon { display: none; }
.link-icon { margin-left: 5px; display: inline-block; }
.search-icon { margin-left: 5px; display: inline-block; }
span.fancytree-title i { font-size: 16px; }
.fa > span { font-weight: 400; }
#filterInput { margin: 10px 0 10px 10px; padding: 5px; width: 200px; border-radius: 6px 0 0 6px !important; border: 1px solid #666; height: 18px; }
#filterInput:focus-visible { outline: none; }
button#btnResetSearch { height: 30px; border-radius: 0 6px 6px 0; border: 1px solid #666; width: 28px; }
button#btnResetSearch:not(:disabled) { background: #140be3; color: #fff; cursor: pointer; }
.note { padding-left: 10px; font-size: 12px; color: grey; }
JavaScript
$(document).ready(function () {
function updateLinkIcon(node) {
var $nodeSpan = $(node.span)
$nodeSpan.find(".link-icon").remove()
$nodeSpan.find(".search-icon").remove()
$nodeSpan.append(
"<span class='link-icon'><a href='" +
node.data.link +
"' target='_blank'><i class='fa fa-link'></i></a></span>" +
"<span class='search-icon'><a href='#' data-id='" +
node.key +
"'><i class='fa fa-search'></i></a></span>",
)
$nodeSpan
.find("span.search-icon")
.off("click")
.on("click", function (e) {
e.preventDefault() // Prevent default link behavior
const dataId = $(this).find("a").attr("data-id")
const input = document.querySelector("#filterInput")
input.value = dataId
// Dispatch input event to trigger Fancytree search
input.dispatchEvent(
new Event("input", { bubbles: true, cancelable: true }),
)
})
}
function toggleFilterMode(isChecked) {
var tree = $.ui.fancytree.getTree("#tree")
if (!tree || !tree.options.filter) {
console.error("Tree or filter extension not initialized")
return
}
// Determine new mode: checked = "hide", unchecked = "dimm"
var newMode =
isChecked !== undefined
? isChecked
? "hide"
: "dimm"
: tree.options.filter.mode === "dimm"
? "hide"
: "dimm"
// Update filter mode
tree.options.filter.mode = newMode
// Sync checkbox state
$("#toggleFilterMode").prop("checked", newMode === "hide")
// Get current search term
var searchTerm = $("#filterInput").val()
// Clear current filter
tree.clearFilter()
// Reapply filter with new mode if there's a search term
if (searchTerm) {
tree.filterNodes(
function (node) {
var titleMatch =
node.title.toLowerCase().indexOf(searchTerm.toLowerCase()) >= 0
var keyMatch =
...