refactor(theme): improve robustness and readability of outline component #3368
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
fixes #3378
1. Enhance Data Consistency
The original code uses different data paths for generating and retrieving "anchors":
Upon page content update, getHeaders() will be invoked.
It will use
querySelectorAll('.VPDoc :where(h1,h2,h3,h4,h5,h6)')
to locate all header elements and then filter them using resolveHeaders().The processed list is then passed to VPDocAsideOutline.vue for rendering the outline.
However, when handling scroll event,
setActiveLink()
uses two querySelectorAll statements to retrieve the list of anchor elements. It is assuming every header MUST have an associated anchor element inside or next to it, which is not always true!The code provided by this PR fixes this issue by reusing the same data used for rendering the outline.
A global variable
resolvedHeaders
is provided and will be updated upon each call toresolveHeaders()
. This variable is an array keeping track of all header elements and their assigned hash links that will be used in the outline.When handling scroll events, instead of invoking querySelectors,
setActiveLink()
will directly use the global variableresolvedHeaders
for the links currently displayed in the outline.In this way, activeLink handlers will always be strictly aligned with the displayed outline. And will cover a much wider senario.
2. Improve Robustness
The original getAnchorTop() function could fail at some special conditions. For example, when header element is nested in a positioned element:
An alternative function
getAbsoluteTop()
in this PR will replacegetAnchorTop()
. The alternative solution is supposed to be more robust. It recursively accumulates relative offset to get a more reliable result. In addition, it returnsNaN
for unexpected items, so the handler may safely skip them.3. Improve Code Readability
Function
isAnchorActive()
is only used bysetActiveLink()
to calculate the appropriate link to highlight. However, it was stripped out of context and the meanings of its return values[boolean, string | null]
are not clear, especially for first-time readers.In addition, the logic of checking
isBottom
is located insidesetActiveLink()
, but the logic to check if the page is on top is somehow nested insideisAnchorActive()
, making it more difficult to understand.In the pr code, I moved the logic back into
isAnchorActive()
and added comments to make things more clear.