diff --git a/navigator-html-injectables/package.json b/navigator-html-injectables/package.json index edb57dda..d8db4800 100644 --- a/navigator-html-injectables/package.json +++ b/navigator-html-injectables/package.json @@ -50,7 +50,7 @@ "devDependencies": { "@juggle/resize-observer": "^3.4.0", "@readium/shared": "workspace:*", - "css-selector-generator": "^3.6.4", + "css-selector-generator": "^3.6.9", "tslib": "^2.6.1", "typescript": "^5.4.5", "vite": "^4.5.5" diff --git a/navigator-html-injectables/src/helpers/dom.ts b/navigator-html-injectables/src/helpers/dom.ts index cb2a78ca..99b8eb37 100644 --- a/navigator-html-injectables/src/helpers/dom.ts +++ b/navigator-html-injectables/src/helpers/dom.ts @@ -68,13 +68,19 @@ export function nearestInteractiveElement(element: Element): Element | null { /// Returns the `Locator` object to the first block element that is visible on /// the screen. export function findFirstVisibleLocator(wnd: ReadiumWindow, scrolling: boolean) { - const element = findElement(wnd, wnd.document.body, scrolling); + const element = findElement(wnd, wnd.document.body, scrolling) as HTMLElement; + + // Use only the allowed selectors to generate the cssSelector and avoid a crash + const cssSelector = wnd._readium_cssSelectorGenerator.getCssSelector(element, { + selectors: ["tag", "id", "class", "nthchild", "nthoftype", "attribute"] + }); + return new Locator({ href: "#", type: "application/xhtml+xml", locations: new LocatorLocations({ otherLocations: new Map([ - ["cssSelector", wnd._readium_cssSelectorGenerator.getCssSelector(element)] + ["cssSelector", cssSelector] ]) }), text: new LocatorText({ @@ -87,6 +93,9 @@ function findElement(wnd: ReadiumWindow, rootElement: Element, scrolling: boolea for (var i = 0; i < rootElement.children.length; i++) { const child = rootElement.children[i]; if (!shouldIgnoreElement(child) && isElementVisible(wnd, child, scrolling)) { + // Once we get a fully visible element, return it + if (isElementFullyVisible(wnd, child)) return child; + // if the parent is not fully visible, search in the childs return findElement(wnd, child, scrolling); } } @@ -111,11 +120,29 @@ function isElementVisible(wnd: ReadiumWindow, element: Element, scrolling: boole } } +/** +* Check if an element is fully visible in the current viewport. +* @param wnd Window instance to operate on +* @param element Element to check visibility of +* @returns True if the element is fully visible, false otherwise +*/ +function isElementFullyVisible(wnd: ReadiumWindow, element: Element): boolean { + const rect = element.getBoundingClientRect(); + const isFullyVisible = + rect.top >= 0 && + rect.left >= 0 && + rect.bottom <= wnd.innerHeight && + rect.right <= wnd.innerWidth; + return isFullyVisible; +} + function shouldIgnoreElement(element: Element) { const elStyle = getComputedStyle(element); if (elStyle) { const display = elStyle.getPropertyValue("display"); - if (display != "block") { + // Added list-item as it is a common display property for list items + // TODO: Check if there are other display properties that should be ignored/considered + if (display != "block" && display != "list-item") { return true; } // Cannot be relied upon, because web browser engine reports invisible when out of view in diff --git a/navigator-html-injectables/src/modules/Peripherals.ts b/navigator-html-injectables/src/modules/Peripherals.ts index 23351339..3523899c 100644 --- a/navigator-html-injectables/src/modules/Peripherals.ts +++ b/navigator-html-injectables/src/modules/Peripherals.ts @@ -71,7 +71,7 @@ export class Peripherals extends Module { targetFrameSrc: this.wnd.location.href, targetElement: (event.target as Element).outerHTML, interactiveElement: nearestInteractiveElement(event.target as Element)?.outerHTML, - cssSelector: this.wnd._readium_cssSelectorGenerator.getCssSelector(event.target), + cssSelector: this.wnd._readium_cssSelectorGenerator.getCssSelector(event.target as Element), } as FrameClickEvent); this.pointerMoved = false; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ac5bd5c6..a6c84a27 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -63,8 +63,8 @@ importers: specifier: workspace:* version: link:../shared css-selector-generator: - specifier: ^3.6.4 - version: 3.6.4 + specifier: ^3.6.9 + version: 3.6.9 tslib: specifier: ^2.6.1 version: 2.6.1