neighbour-loader.js

On every note page, neighbour-loader.js, a lightweight script is loaded via the partial neighbour-panel.html. It has no external dependencies compared to graph.js .

The panel is populated on demand when the user clicks “Neighbours” — or immediately on page load if sessionStorage records it was previously open. It renders a neighbour list directly into #neighbour-panel in the aside, above the TOC. Closing the panel clears its contents and removes the sessionStorage flag.

The Neighbour panel topbar includes a graph icon linking to the note’s entry point in the graphical view.

The neighbour list is rendered via neighbour-utils.js which owns the data fetch, grouping into Links and Backlinks, and returns a DOM fragment to the caller. _fetchAborted guards against a race condition where destroy() is called while a fetch is still in flight — if the flag is set when the callback fires, the panel is cleared silently.

Initialisation

This section has trigger guard, lnk, _fetchAborted

const trigger = document.getElementById("neighbour-trigger");
if (!trigger) return;

const lnk = trigger.dataset.lnk;
let _fetchAborted = false;

Render List

This section includes topbar construction and NeighbourUtils.buildList call.


function renderList() {
    const panel = document.getElementById("neighbour-panel");
    if (!panel) return;
    _fetchAborted = false;

    const topBar = document.createElement("div");
    topBar.className = "np-topbar";
    const count = document.createElement("span");
    count.className = "np-count";
    const graphLink = document.createElement("a");
    graphLink.href      = `/map/?focus=${encodeURIComponent(lnk)}#graph-wrapper`;
    graphLink.title     = "Explore visually";
    graphLink.className = "np-graph-link";
    graphLink.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"
    viewBox="0 0 24 24" fill="none" stroke="currentColor"
    stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
    aria-hidden="true">
  <circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/>
  <circle cx="18" cy="19" r="3"/>
  <line x1="8.59" y1="13.51" x2="15.42" y2="17.49"/>
  <line x1="15.41" y1="6.51" x2="8.59" y2="10.49"/>
</svg>`;
    const closeBtn = document.createElement("button");
    closeBtn.textContent = "✕";
    closeBtn.className = "np-close";
    closeBtn.setAttribute("aria-label", "Close neighbour panel");
    closeBtn.addEventListener("click", destroy, { once: true });
    topBar.appendChild(graphLink);
    topBar.appendChild(count);
    topBar.appendChild(closeBtn);
    panel.innerHTML = "";
    panel.appendChild(topBar);
    panel.scrollIntoView({ behavior: "smooth", block: "start" });

    NeighbourUtils.buildList(lnk, "np-list",
                                 (n, fragment) => {
                                     if (_fetchAborted) { panel.innerHTML = ""; return; }
                                     count.textContent = `${n} neighbour${n !== 1 ? "s" : ""}`;
                                     if (fragment) panel.appendChild(fragment);
                                     else { const m = document.createElement("div"); m.className = "np-empty"; m.textContent = "This note has no connections yet."; panel.appendChild(m); }
                                 },
                                 () => { panel.innerHTML = '<div class="np-empty">Could not load.</div>'; }
                                );
}

Destroy

This section includes the abort flag and clears the panel and sessionStorage flag.

function destroy() {
    _fetchAborted = true;
    const panel = document.getElementById("neighbour-panel");
    if (panel) panel.innerHTML = "";
    sessionStorage.removeItem("neighbourPanelOpen");
}

Event Listeners

Handles sessionStorage check on load, click toggle

if (sessionStorage.getItem("neighbourPanelOpen") === "1") {
    renderList();
}
trigger.addEventListener("click", () => {
    const panel = document.getElementById("neighbour-panel");
    if (panel && panel.innerHTML !== "") {
        destroy();
    } else {
        sessionStorage.setItem("neighbourPanelOpen", "1");
        renderList();
    }
});

Assembly

The complete IIFE shell with noweb references is as follows. The complete tangled version of this file can be found here↗ .

(function (){
    //guard → renderlist → destroy → eventlisteners
 })();

© Prabu Anand K 2020-2026