Skip to content

Commit 5aa56eb

Browse files
authored
Merge pull request #14089 from quarto-dev/fix/search-tabset-activation-regression
Fix tab activation regression in search highlighting
2 parents ed9f952 + e220960 commit 5aa56eb

1 file changed

Lines changed: 40 additions & 15 deletions

File tree

src/resources/projects/website/search/quarto-search.js

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
12341250
function 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

Comments
 (0)