160d10a30597d7435c30409878e2ba11c3e7c660
mspeir
  Wed Sep 9 14:19:40 2026 -0700
trackDb docs: stop the shared script from aborting on the pages without a search box, refs #38062

Two JavaScript faults on the trackDb doc pages, both of which trackDbHub.v3.html
had already avoided while the other pages drifted.

trackDbDoc.html, changes.html and trackDbHub.v0/v1/v2.html load /js/utils.js in
their head, and the hgMenubar include further down brings in the same file by a
different path, so it ran twice. utils.js declares mouseoverContainer with let
at top level, so the second run threw a redeclaration error. v3 does not load
utils.js in the head, and the menubar copy is in place well before anything
needs it, so drop the head copy from the other five.

documentLoad() then called addEventListener on the result of
getElementById("tdbSearch") with no null check, and only v3 has that input, so
the function threw partway through on the other four pages and never reached
its end. The search box and jump-to-top button do not exist on those pages, so
nothing visible was lost, but the exception aborted the rest of documentLoad
and would have swallowed anything added after it. Guard the two listeners.

All six pages now load with no console errors, checked in a browser.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

diff --git src/hg/htdocs/goldenPath/help/trackDb/trackDbDoc.js src/hg/htdocs/goldenPath/help/trackDb/trackDbDoc.js
index c98ccf5cf2b..266e58215a4 100644
--- src/hg/htdocs/goldenPath/help/trackDb/trackDbDoc.js
+++ src/hg/htdocs/goldenPath/help/trackDb/trackDbDoc.js
@@ -495,80 +495,85 @@
             tdbDoc.toggleAll($('img.toggle.all')[0],true);
 
         document.addEventListener("keydown", e => {
             if (e.key === "F3") {
                 e.preventDefault();
                 jumpToResult(e.shiftKey ? -1 : 1);
             }
         });
 
 
         // add the tdb setting to the img toggle classList
         let toggles = document.querySelectorAll("IMG.toggle.detail");
         toggles.forEach( (toggle) => {
             toggle.classList.add(toggle.parentNode.className + "_imgToggle");
         });
+        // Only the current hub spec page carries the search box; the other doc pages
+        // share this script and have no such input.
         let inp = document.getElementById("tdbSearch");
+        if (inp) {
             inp.addEventListener("input", (e) => {
                 let term = inp.value.trim();
                 if (term.length >= 2) {
                     runSearch(term);
                 } else if (term.length === 1) {
                     // Show helpful message for single character
                     runSearch(term);
                 } else if (term.length === 0) {
                     // Clear search when input is empty
                     clearSearchHighlights();
                     hideSearchStatus();
                     currentResults = [];
                     currentIndex = -1;
                 }
             });
 
             // Add keyboard shortcuts for search navigation
             inp.addEventListener("keydown", (e) => {
                 if (e.key === "Enter") {
                     e.preventDefault();
                     if (currentResults.length > 0) {
                         jumpToResult(e.shiftKey ? -1 : 1);
                     }
                 } else if (e.key === "Escape") {
                     e.preventDefault();
                     clearSearch();
                 }
             });
+        }
 
         // Add clear search button functionality
         let clearButton = document.getElementById("clearSearch");
         if (clearButton) {
             clearButton.addEventListener("click", () => {
                 clearSearch();
             });
         }
 
         // Add jump to top functionality with smooth scrolling
         let jumpToTopButton = document.getElementById("jumpToTop");
         if (jumpToTopButton) {
             jumpToTopButton.addEventListener("click", (e) => {
                 e.preventDefault();
                 // Smooth scroll to top
                 window.scrollTo({
                     top: 0,
                     behavior: "smooth"
                 });
                 // Also focus the search input for easy continued searching
                 setTimeout(() => {
+                    if (inp)
                         inp.focus();
                 }, 500);
             });
         }
     }
 }
 
 function searchHidden(term) {
     // Reset previous search
     clearSearchHighlights();
 
     // track elements already processed to prevent repeated <mark>ing
     if (!window.__searchedElements) {
         window.__searchedElements = new WeakSet();
     }