Ad Widget

Collapse

[HOW-TO] Zabbix 7.0 UI: Click-Only Sidebar & Honeycomb-to-Item Navigator Filter

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • Abinash
    Junior Member
    • May 2026
    • 13

    #1

    [HOW-TO] Zabbix 7.0 UI: Click-Only Sidebar & Honeycomb-to-Item Navigator Filter

    Hi Zabbix Community,

    In dense Zabbix 7.0 dashboard environments, two common usability challenges often arise:
    1. Sidebar Hover Auto-Expansion: The collapsed navigation sidebar automatically expands when hovering your cursor over it, causing unwanted UI layout shifts during routine monitoring.
    2. Widget Inter-Communication: When clicking a host cell in the Honeycomb widget, there is no native dynamic filtering passed to the Item Navigator widget tree to isolate that host's metrics.

    To solve this, I developed self-contained bash/Python deployment scripts for Zabbix 7.0 (tested on Zabbix 7.0.31 on Ubuntu 24.04 / Debian). Key Features
    • Pristine Backups: Creates .og original backups of core files before applying any changes.
    • Click-Only Sidebar: Completely disables all mouseenter triggers on class.sidebar.js.
    • Dynamic Host Filtering: Intercepts Honeycomb cell clicks in class.svghoneycomb.js and targets custom-scoped search inputs in class.itemnavigator.js to isolate host metrics without populating global menu search bars.
    Complete Deployment Script


    Run this single, self-contained deployment script on your Zabbix web server terminal as sudo:

    Bash
    #!/bin/bash
    set -e

    FILE_HONEYCOMB="/usr/share/zabbix/widgets/honeycomb/assets/js/class.widget.js"
    FILE_SVG="/usr/share/zabbix/widgets/honeycomb/assets/js/class.svghoneycomb.js"
    FILE_SIDEBAR="/usr/share/zabbix/js/class.sidebar.js"
    FILE_NAV="/usr/share/zabbix/widgets/itemnavigator/assets/js/class.itemnavigator.js"

    echo "=== [1/3] Restoring Pristine Backups if Present ==="
    for FILE in "$FILE_HONEYCOMB" "$FILE_SVG" "$FILE_SIDEBAR" "$FILE_NAV"; do
    if [ -f "$FILE" ] && [ ! -f "${FILE}.og" ]; then
    sudo cp "$FILE" "${FILE}.og"
    echo "[+] Backup created: ${FILE}.og"
    fi
    done

    echo "=== [2/3] Applying Scoped Click Interceptor & Single Search Bar Patch ==="
    sudo python3 -c '
    # 1. Patch Item Navigator to create ONLY ONE search input and give it an explicit unique class
    file_nav = "/usr/share/zabbix/widgets/itemnavigator/assets/js/class.itemnavigator.js"
    with open(file_nav + ".og", "r") as f:
    nav_content = f.read()

    target_nav = "this.#container.appendChild(this.#navigation_ tree .getContainer());"

    replacement_nav = """\t\ttry {
    \t\t\tif (!this.#container.querySelector(".zbx-custom-nav-search")) {
    \t\t\t\tconst filterDiv = document.createElement("div");
    \t\t\t\tfilterDiv.className = "zbx-custom-nav-search-wrapper";
    \t\t\t\tfilterDiv.style.cssText = "padding: 4px 6px; background: #fff; border-bottom: 1px solid #dfe4e8; margin-bottom: 4px;";

    \t\t\t\tconst filterInput = document.createElement("input");
    \t\t\t\tfilterInput.type = "text";
    \t\t\t\tfilterInput.placeholder = "Search items...";
    \t\t\t\tfilterInput.className = "form-input zbx-custom-nav-search";
    \t\t\t\tfilterInput.style.cssText = "width: 100%; height: 24px; padding: 0 6px; font-size: 11px; box-sizing: border-box;";

    \t\t\t\tfilterInput.addEventListener("input", (e) => {
    \t\t\t\t\tconst val = e.target.value.toLowerCase().trim();
    \t\t\t\t\tconst nodes = this.#container.querySelectorAll("li, .tree-item, a, [class*=\x27navigation-tree\x27]");
    \t\t\t\t\tnodes.forEach(node => {
    \t\t\t\t\t\tconst txt = (node.innerText || node.textContent || "").toLowerCase();
    \t\t\t\t\t\tif (val === "" || txt.includes(val)) {
    \t\t\t\t\t\t\tnode.style.setProperty("display", "", "important");
    \t\t\t\t\t\t} else {
    \t\t\t\t\t\t\tnode.style.setProperty("display", "none", "important");
    \t\t\t\t\t\t}
    \t\t\t\t\t});
    \t\t\t\t});

    \t\t\t\tfilterDiv.appendChild(filterInput);
    \t\t\t\tthis.#container.appendChild(filterDiv);
    \t\t\t}
    \t\t} catch(err) { console.error("Item Navigator patch error:", err); }

    \t\tthis.#container.appendChild(this.#navigation_t ree.getContainer());"""

    if target_nav in nav_content:
    with open(file_nav, "w") as f:
    f.write(nav_content.replace(target_nav, replacement_nav))
    print("[+] Scoped Item Navigator search input patched cleanly!")

    # 2. Patch Honeycomb SVG to target strictly .zbx-custom-nav-search inputs (skipping left sidebar menu inputs)
    file_svg = "/usr/share/zabbix/widgets/honeycomb/assets/js/class.svghoneycomb.js"
    with open(file_svg + ".og", "r") as f:
    svg_content = f.read()

    target_svg = """\t#drawCell(cell) {
    \t\tcell
    \t\t\t.call(cell => this.#drawLabel(cell))
    \t\t\t.on(\x27click\x27, (e, d) => {
    \t\t\t\tif (this.selectCell(d.itemid)) {
    \t\t\t\t\tthis.#svg.dispatch(CSVGHoneycomb.EVENT_C ELL_CLICK, {
    \t\t\t\t\t\tdetail: {
    \t\t\t\t\t\t\thostid: d.hostid,
    \t\t\t\t\t\t\titemid: d.itemid
    \t\t\t\t\t\t}
    \t\t\t\t\t});
    \t\t\t\t}
    \t\t\t})"""

    replacement_svg = """\t#drawCell(cell) {
    \t\tcell
    \t\t\t.style(\x27cursor\x27, \x27pointer\x27)
    \t\t\t.call(cell => this.#drawLabel(cell))
    \t\t\t.on(\x27click\x27, (e, d) => {
    \t\t\t\tif (this.selectCell(d.itemid)) {
    \t\t\t\t\tthis.#svg.dispatch(CSVGHoneycomb.EVENT_C ELL_CLICK, {
    \t\t\t\t\t\tdetail: {
    \t\t\t\t\t\t\thostid: d.hostid,
    \t\t\t\t\t\t\titemid: d.itemid
    \t\t\t\t\t\t}
    \t\t\t\t\t});
    \t\t\t\t}

    \t\t\t\tsetTimeout(() => {
    \t\t\t\t\ttry {
    \t\t\t\t\t\tlet vmName = "";
    \t\t\t\t\t\tif (d && d.labels && d.labels.primary) {
    \t\t\t\t\t\t\tif (d.labels.primary.lines) {
    \t\t\t\t\t\t\t\tvmName = Array.from(d.labels.primary.lines).join(" ");
    \t\t\t\t\t\t\t} else if (d.labels.primary.text) {
    \t\t\t\t\t\t\t\tvmName = d.labels.primary.text;
    \t\t\t\t\t\t\t}
    \t\t\t\t\t\t}
    \t\t\t\t\t\tif (!vmName && e) {
    \t\t\t\t\t\t\tconst t = e.currentTarget || e.target;
    \t\t\t\t\t\t\tif (t) vmName = t.textContent || t.innerText || "";
    \t\t\t\t\t\t}

    \t\t\t\t\t\tif (vmName) {
    \t\t\t\t\t\t\tvmName = vmName.includes(" - ") ? vmName.split(" - ").pop().trim() : vmName.trim();

    \t\t\t\t\t\t\t// Target strictly custom item navigator search inputs
    \t\t\t\t\t\t\tconst navInputs = document.querySelectorAll("input.zbx-custom-nav-search");
    \t\t\t\t\t\t\tnavInputs.forEach(inp => {
    \t\t\t\t\t\t\t\tinp.value = vmName;
    \t\t\t\t\t\t\t\tinp.dispatchEvent(new Event("input", { bubbles: true }));
    \t\t\t\t\t\t\t\tinp.dispatchEvent(new Event("change", { bubbles: true }));
    \t\t\t\t\t\t\t});
    \t\t\t\t\t\t}
    \t\t\t\t\t} catch(err) { console.error("SVG click handler error:", err); }
    \t\t\t\t}, 10);
    \t\t\t})"""

    if target_svg in svg_content:
    with open(file_svg, "w") as f:
    f.write(svg_content.replace(target_svg, replacement_svg))
    print("[+] Honeycomb SVG click handler updated with explicit class scoping!")

    # 3. Patch Sidebar Click-Only Mode
    file_sidebar = "/usr/share/zabbix/js/class.sidebar.js"
    with open(file_sidebar + ".og", "r") as f:
    sidebar_content = f.read()

    sidebar_content = sidebar_content.replace("this.on(\x27mouseenter\x2 7, this._events.mouseenter);", "// disabled hover open")
    sidebar_content = sidebar_content.replace("item.hasSubmenu() && item.on(\x27mouseenter\x27", "// item.hasSubmenu() && item.on(\x27mouseenter\x27")
    sidebar_content = sidebar_content.replace("this.on(\x27mouseenter\x2 7, this._events.cancelExpandSelected);", "// disabled cancel expand")

    with open(file_sidebar, "w") as f:
    f.write(sidebar_content)
    print("[+] Sidebar global click-only patch applied!")
    '

    echo "=== [3/3] Setting Permissions & Restarting Web Stack ==="
    sudo chown www-data:www-data "$FILE_HONEYCOMB" "$FILE_SVG" "$FILE_SIDEBAR" "$FILE_NAV"
    sudo chmod 644 "$FILE_HONEYCOMB" "$FILE_SVG" "$FILE_SIDEBAR" "$FILE_NAV"

    sudo systemctl restart php*-fpm 2>/dev/null || true
    sudo systemctl restart nginx apache2 2>/dev/null || true

    echo "=== Deployment Complete ==="
    Last edited by Abinash; Yesterday, 09:43.
  • kyus
    Senior Member
    • Feb 2024
    • 208

    #2
    The honeycomb changes seem interesting, but regarding the sidebar, is this different from the "Hide" option that's already available?

    Comment

    • Abinash
      Junior Member
      • May 2026
      • 13

      #3
      Hi kyus,

      Thanks for taking a look! Yes, it is fundamentally different from the native "Hide" (collapsed) sidebar option. Why Native "Hide" Behavior Is Disruptive in Dense Monitoring


      When you click the native back-arrow at the bottom to collapse (hide) the Zabbix 7.0 sidebar, Zabbix enters Hover-to-Expand mode:
      • Moving your mouse anywhere over the left edge or collapsed icons instantly expands the full sidebar menu over your dashboard.
      • In high-density NOC screens or multi-widget dashboard setups, accidental cursor sweeps repeatedly pop open the sidebar, obscuring high-priority widgets (like Honeycombs, Top Hosts, or Item Navigators) and shifting layout focus.
      What This Script Changes


      This patch converts the collapsed sidebar into a Strict Click-Only Accordion:
      1. Disables Mouseenter Triggers: Hovering over icons or submenus no longer triggers auto-expansion.
      2. Intentional Navigation: The sidebar stays strictly collapsed until you explicitly click a menu item or the expand toggle.


      Comment

      • kyus
        Senior Member
        • Feb 2024
        • 208

        #4
        Oh I meant this little icon:
        Click image for larger version

Name:	image.png
Views:	6
Size:	14.9 KB
ID:	518338

        It fully hides the sidebar and disables the mouse hover feature.
        The downside is that you have to click 2 times to bring back the sidebar, not sure why.

        Comment

        • Abinash
          Junior Member
          • May 2026
          • 13

          #5
          Thanks for pointing that out! The "Hide completely" mode (the view mode toggle) is definitely great when you want maximum screen real estate and zero sidebars on dedicated display boards or NOC screens.

          The main difference with this patch is for users who want to keep the collapsed icon bar visible (for quick 1-click access to specific sections like Monitoring, Dashboards, or Inventory) without the annoying hover auto-expand triggering every time the mouse accidentally sweeps past the left edge.

          Comment

          Working...