@@ -47,6 +47,19 @@ window.document.addEventListener("DOMContentLoaded", function (_event) {
4747 // perform any highlighting
4848 highlight ( query , mainEl ) ;
4949
50+ // Activate tabs on pageshow — after tabsets.js restores localStorage state.
51+ // tabsets.js registers its pageshow handler during module execution (before
52+ // DOMContentLoaded). By registering ours during DOMContentLoaded, listener
53+ // ordering guarantees we run after tabsets.js — so search activation wins.
54+ window . addEventListener ( "pageshow" , function ( event ) {
55+ if ( ! event . persisted ) {
56+ for ( const mark of mainEl . querySelectorAll ( "mark" ) ) {
57+ openAllTabsetsContainingEl ( mark ) ;
58+ }
59+ requestAnimationFrame ( ( ) => scrollToFirstVisibleMatch ( mainEl ) ) ;
60+ }
61+ } , { once : true } ) ;
62+
5063 // fix up the URL to remove the q query param
5164 const replacementUrl = new URL ( window . location ) ;
5265 replacementUrl . searchParams . delete ( kQueryArg ) ;
@@ -1170,7 +1183,7 @@ function searchMatches(inSearch, el) {
11701183 /** @type {{i:number; els:Map<HTMLElement,{lo:number,hi:number}>}[] } */
11711184 let curMatchContext = initMatch ( )
11721185
1173- for ( leaf of leafNodes ) {
1186+ for ( const leaf of leafNodes ) {
11741187 const leafStr = leaf . textContent . toLowerCase ( )
11751188 // for each character in this leaf's text:
11761189 for ( let leafi = 0 ; leafi < leafStr . length ; leafi ++ ) {
@@ -1231,18 +1244,37 @@ function markMatches(node, lohis) {
12311244 return parent
12321245}
12331246
1247+ // Activate ancestor tabs so a search match inside an inactive pane becomes visible.
1248+ // When multiple panes in the same tabset contain matches, avoid switching away from
1249+ // the currently active pane — the user already sees a match there.
12341250function openAllTabsetsContainingEl ( el ) {
1235- for ( const tab of matchAncestors ( el , '.tab-pane' ) ) {
1236- const tabButton = document . querySelector ( `[data-bs-target="#${ tab . id } "]` ) ;
1251+ for ( const pane of matchAncestors ( el , '.tab-pane' ) ) {
1252+ const tabContent = pane . closest ( '.tab-content' ) ;
1253+ if ( ! tabContent ) continue ;
1254+ const activePane = tabContent . querySelector ( ':scope > .tab-pane.active' ) ;
1255+ if ( activePane ?. querySelector ( 'mark' ) ) continue ;
1256+ const tabButton = document . querySelector ( `[data-bs-target="#${ pane . id } "]` ) ;
12371257 if ( tabButton ) new bootstrap . Tab ( tabButton ) . show ( ) ;
12381258 }
12391259}
12401260
1261+ function scrollToFirstVisibleMatch ( mainEl ) {
1262+ for ( const mark of mainEl . querySelectorAll ( "mark" ) ) {
1263+ const isMarkVisible = matchAncestors ( mark , '.tab-pane' ) . every ( markTabPane =>
1264+ markTabPane . classList . contains ( "active" )
1265+ )
1266+ if ( isMarkVisible ) {
1267+ mark . scrollIntoView ( { behavior : "smooth" , block : "center" } ) ;
1268+ return ;
1269+ }
1270+ }
1271+ }
1272+
12411273/**
1242- * e.g.
1274+ * e.g.
12431275 * ```js
12441276 * const m = new Map()
1245- *
1277+ *
12461278 * arrayMapPush(m, 'dog', 'Max')
12471279 * console.log(m) // Map { dog->['Max'] }
12481280 *
@@ -1270,16 +1302,9 @@ function highlight(searchStr, el) {
12701302 }
12711303 }
12721304
1273- const matchNodes = [ ...matchesGroupedByNode ] . map ( ( [ node , lohis ] ) => {
1274- const matchNode = markMatches ( node , lohis )
1275- openAllTabsetsContainingEl ( matchNode )
1276- return matchNode
1277- } )
1278- // let things settle before scrolling
1279- setTimeout ( ( ) =>
1280- matchNodes [ 0 ] ?. scrollIntoView ( { behavior : 'smooth' , block : 'center' } ) ,
1281- 400
1282- )
1305+ for ( const [ node , lohis ] of matchesGroupedByNode ) {
1306+ markMatches ( node , lohis )
1307+ }
12831308}
12841309
12851310/* Link Handling */
0 commit comments