Skip to content

Search highlights (<mark>) cleared within milliseconds, invisible to users #14047

@cderv

Description

@cderv

When navigating to a page via search (with ?q= parameter), the highlight() function adds <mark> elements around matching text. These marks are then cleared almost immediately (~24ms on quarto.org) by quarto-hrChanged events, making them invisible to users.

Steps to Reproduce

  1. Go to any Quarto website page with ?q=someterm (e.g. html-basics.html?q=format)
  2. Observe that no highlighted text is visible

Instrumented testing on quarto.org shows marks appear and disappear within a single frame:

404ms: 17 <mark> elements added by highlight()
428ms: 0 <mark> elements — all cleared by resetHighlighting()

Root Cause

The clearing chain:

  1. quarto-search.js registers a one-shot listener for quarto-hrChanged that calls clearHighlight():
    https://github.com/quarto-dev/quarto-cli/blob/aaaba63c2/src/resources/projects/website/search/quarto-search.js#L67-L76

  2. quarto-nav.js dispatches quarto-hrChanged from updateDocumentOffset():
    https://github.com/quarto-dev/quarto-cli/blob/aaaba63c2/src/resources/projects/website/navigation/quarto-nav.js#L196-L197

  3. updateDocumentOffset() is triggered during initial layout settling by a ResizeObserver on the header element and a setTimeout(250):
    https://github.com/quarto-dev/quarto-cli/blob/aaaba63c2/src/resources/projects/website/navigation/quarto-nav.js#L254-L269

The comment says "Clear search highlighting when the user scrolls sufficiently", but the event fires from layout recalculation, not user interaction.

Context

PR #14003 added text fragments (:~:text=) to search result URLs, which provides browser-native scroll-to-text highlighting that persists independently. This effectively works around the broken <mark> behavior for visible text, but text fragments cannot highlight content inside hidden elements (e.g. inactive Bootstrap tab panes).

Possible Fix

Delay the quarto-hrChanged/quarto-sectionChanged listener registration to skip initial layout settling events, or gate the clearing on actual user interaction (scroll, click).

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions