From 0b6387827e5aade9a2cb815945f5fdb2db232dc3 Mon Sep 17 00:00:00 2001 From: Jason Buckner Date: Wed, 18 Mar 2026 17:55:02 -0700 Subject: [PATCH] Add resolveEl() for shadow DOM compatibility BookReader.el can be either a CSS selector string or a DOM Element. Several places in the codebase call document.querySelector(br.el), which fails when: - br.el is a DOM Element (not a string) - br.el is a selector like '#BookReader' but the element is inside a shadow DOM tree (querySelector can't cross shadow boundaries) Add BookReader.prototype.resolveEl() that handles both cases, and update mainBRContainer getter and search plugin to use it. This is needed for integrating BookReader into apps that use shadow DOM (e.g. offshoot, which wraps details-page in app-root's shadow root). Co-Authored-By: Claude Opus 4.6 --- src/BookReader.js | 18 ++++++++++++++++++ src/ia-bookreader/ia-bookreader.js | 2 +- src/plugins/search/view.js | 3 ++- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/BookReader.js b/src/BookReader.js index a7e6dea1b..b4f5250aa 100644 --- a/src/BookReader.js +++ b/src/BookReader.js @@ -788,6 +788,24 @@ BookReader.prototype.unbind = function(name, callback) { return this.off(name, callback); }; +/** + * Resolves `this.el` to a DOM Element, whether it was provided as an + * Element reference or a CSS selector string. + * + * This is needed because some consumers (e.g. apps using shadow DOM) + * must pass a DOM element directly — `document.querySelector` cannot + * cross shadow boundaries. jQuery's `$()` already handles both forms, + * but code that calls `document.querySelector(br.el)` will fail when + * `el` is an Element or when the target is inside a shadow root. + * + * @returns {Element|null} + */ +BookReader.prototype.resolveEl = function() { + if (this.el instanceof Element) return this.el; + if (typeof this.el === 'string') return document.querySelector(this.el); + return null; +}; + /** * Resizes based on the container width and height */ diff --git a/src/ia-bookreader/ia-bookreader.js b/src/ia-bookreader/ia-bookreader.js index 728645ea5..4937775cd 100644 --- a/src/ia-bookreader/ia-bookreader.js +++ b/src/ia-bookreader/ia-bookreader.js @@ -337,7 +337,7 @@ export class IaBookReader extends LitElement { /** gets element that houses the bookreader in light dom */ get mainBRContainer() { - return document.querySelector(this.bookreader?.el); + return this.bookreader?.resolveEl?.() ?? document.querySelector(this.bookreader?.el); } get baseProviderConfig() { diff --git a/src/plugins/search/view.js b/src/plugins/search/view.js index fdf3be7a5..50df2e184 100644 --- a/src/plugins/search/view.js +++ b/src/plugins/search/view.js @@ -331,7 +331,8 @@ class SearchView { const modal = document.createElement('div'); modal.classList.add('BRprogresspopup', 'search_modal'); modal.innerHTML = messageHTML; - document.querySelector(this.br.el).append(modal); + const container = this.br.resolveEl?.() ?? document.querySelector(this.br.el); + container?.append(modal); } /**