feat: add readthedocs javascript search#158
Conversation
6989243 to
b2a87fc
Compare
This comment was marked as resolved.
This comment was marked as resolved.
jothepro
left a comment
There was a problem hiding this comment.
Cool! The readthedocs search seems to work a lot better indeed! I think adding this as an extension to the theme would be really valuable!
When starting the search with the readthedocs addon widget at the bottom right, a really nice popover with live search is opened. IMO that's a really nice user experience! Do you think it would be possible to use the searchbar in the upper right just as a link that opens this popover?
I'm not sure I fully understand the ask, but if you're just asking if the live search will work (search as you type)... it's possible, I just don't know how to implement it. |
6daf3f9 to
6bd490e
Compare
Introduce a live search dropdown and various robustness improvements for ReadTheDocs search integration. Changes: - Add CSS for live search dropdown (#RTDLiveResults) styling and item/snippet formatting. - Use globalThis.location.hostname and expose searchFor on globalThis to avoid scope issues. - Wrap global SearchBox to preserve behavior while preventing CloseResultsWindow from blurring an actively focused search field. - Add MutationObservers to re-attach listeners and re-focus the search input when the DOM is mutated (fixes mobile/OSK behavior where input is recreated). - Implement debounced live search (_attachLiveSearch, _runLiveSearch), AJAX-based live result rendering (_showLiveResults), positioning, keyboard navigation, and hide logic. - Refactor paginated search fetch into fetchResults with a ctx object and appendResultItem helper for consistent DOM updates. - Make getReadTheDocsDefaultVersion return a Promise and resolve to 'latest' on error (handles PR numeric versions). These changes improve UX for live searching, keyboard support, and resilience when menu code recreates the search field.
Migrate away from jQuery for DOM access and AJAX: use document.getElementById / querySelector and textContent/appendChild instead of jQuery selectors and methods; replace $.ajax calls with fetch + response.ok checks and promise handlers; replace $.each with Array.forEach; update search field assignment to use .value. Remove jquery from package.json devDependencies. This modernizes the code and removes an unnecessary runtime dependency.
|
Decided to revisit this PR with assistance from Claude.
|
|
@jothepro anything else needed to get this going? It's working pretty well for me. You can see an example of it with my docs for Sunshine at https://docs.lizardbyte.dev/projects/sunshine/latest/ An example to search for could be "vulkan" to show the live and standard search results. |
jothepro
left a comment
There was a problem hiding this comment.
This looks very promising already, thanks for the good work!
| dropdown.style.top = `${rect.bottom + scrollY}px`; | ||
| dropdown.style.left = ''; | ||
| dropdown.style.right = `${viewportWidth - rect.right}px`; | ||
| dropdown.style.minWidth = `${Math.max(rect.width, 260)}px`; |
There was a problem hiding this comment.
I assume that this will not work with the sidebar only variant of the theme, because the element is always aligned to the right edge of the search box. The sidebar theme would require a left-align though, otherwise the element will not be fully visible.
I've crafted some screenshots on how this would likely look like with the current alignment, and how the layout should be IMO:
Right Aligned
Left Aligned
I'm guessing an easy fix would be to add an init parameter that allows to configure the alignment?
DoxygenAwesomeReadtheDocsSearch.init('leftAlign')Pls correct me if my assumptions are not correct.
| const rect = anchor.getBoundingClientRect(); | ||
| const scrollY = window.scrollY; | ||
| const viewportWidth = document.documentElement.clientWidth; | ||
| dropdown.style.top = `${rect.bottom + scrollY}px`; |
There was a problem hiding this comment.
Very minor bug that I'd consider tolerable because I'd consider the input a rase edge case:
When resizing the viewport, the position of the dropdown is not recalculated. This occurs when switching from desktop to mobile layout and when the navigation-items move under the logo:
Bildschirmaufnahme.2026-05-31.um.15.25.00.mov
| field.addEventListener('keydown', function(e) { | ||
| if (e.key === 'Escape') { | ||
| DoxygenAwesomeReadtheDocsSearch._hideLiveResults(); | ||
| } else if (e.key === 'ArrowDown') { | ||
| e.preventDefault(); | ||
| DoxygenAwesomeReadtheDocsSearch._moveLiveResultFocus(1); | ||
| } else if (e.key === 'ArrowUp') { | ||
| e.preventDefault(); | ||
| DoxygenAwesomeReadtheDocsSearch._moveLiveResultFocus(-1); | ||
| } else if (e.key === 'Enter') { | ||
| const focused = document.querySelector('#RTDLiveResults .rtd-live-item:focus'); | ||
| if (focused) { | ||
| e.preventDefault(); | ||
| focused.click(); | ||
| } | ||
| } |
There was a problem hiding this comment.
I'm not sure why but the keyboard navigation is broken for me:
It is possible to focus the first item in the list pressing ↓ on the keyboard. But the focus is stuck then and cannot be moved to other items using either ↓ or ↑. It is also not possible to exit out of the search by pressing Esc.
I've tried to visualize the problem in this video (Keyboard inputs are displayed in the lower left)
Bildschirmaufnahme.2026-05-31.um.14.34.30.mov
Co-Authored-By: jothepro <21294002+jothepro@users.noreply.github.com>
Support left-aligned live search variant and improve live-results behavior. Docs: add example showing initialization with leftAlign for the sidebar-only theme. JS: add alignment state, allow init('leftAlign'), prevent duplicate live-search attachment, move keydown handling to a shared _handleLiveSearchKeydown, add dropdown keydown listener, restore focus on Escape, update positioning logic for left/right alignment and mobile, use requestAnimationFrame to schedule position updates, and listen for window resize to recompute placement. Overall fixes keyboard, focus and positioning edge cases and reduces redundant event attachment.
|
Thanks for the review! I applied your suggestions to the styling, and pushed some changes that I think will fix the 3 functional issues you mentioned. I have not tested the changes though... would it be possible for you to setup this project in readthedocs and enforce building pull requests? Then it will be super easy to view and test pull request modifications. I can add all the required readthedocs configuration in this PR, it just needs to be setup there first and receive webhooks from GitHub. Edit: I added the rtd config already. If you setup this project on readthedocs, I will force push to trigger a build there. And one more note, when setting up the project it will ask for the location of the |
This PR adds a custom readthedocs search extension using javascript. I found the default doxygen search is quite terrible, and doesn't seem to actually search any markdown files (or barely searches them)... which is not great when you have user documentation.
I have added this for my own projects (here: LizardByte/doxyconfig#4), and figured I could share it back here for others to benefit from. A live preview is available here: https://docs.lizardbyte.dev/projects/doxyconfig/latest/search.html?query=doxygen
This is what it looks like.

Old version
The styling is somewhat based on Furo, this is how that looks in one of my projects.

TODO: