Skip to content

Commit

Permalink
fix(viewer): selection highlighting problems on iOS
Browse files Browse the repository at this point in the history
This fixes the followintg problems that only occur on iOS (iPhone and iPad).

- marker highlighting position is wrongly shifted.
- highlighting of found text is not shown.
  This is due to the [WebKit Bug 199211 - Selection highlight not shown on programmatic focus after initial load](https://bugs.webkit.org/show_bug.cgi?id=199211)
- Find Next/Previous button clears the previous found position and cannot continue same text searching
  • Loading branch information
MurakamiShinyu committed Sep 18, 2023
1 parent 1a31dbf commit abb3ee2
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 2 deletions.
50 changes: 50 additions & 0 deletions packages/viewer/src/viewmodels/find-box.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import Navigation from "./navigation";
import urlParameters from "../stores/url-parameters";
import keyUtil from "../utils/key-util";
import stringUtil from "../utils/string-util";
import { scaleRect, applyTransformToRect } from "../utils/scale-util";

const { Keys } = keyUtil;

Expand Down Expand Up @@ -52,6 +53,7 @@ class FindBox {
status: Observable<string>;
cancel = false;
intervalID = 0;
foundRange: Range | null = null;

constructor(public viewer: Viewer, public navigation: Navigation) {
this.opened = ko.observable();
Expand Down Expand Up @@ -98,6 +100,8 @@ class FindBox {
this.cancel = true;
}
this.status(FindStatus.Default);
this.foundRange = null;
this.fixHighlight();
return true;
};

Expand Down Expand Up @@ -193,6 +197,13 @@ class FindBox {
const windowF = window as WindowWithFind;
const findBoxElem = document.getElementById("vivliostyle-menu-find-box");
findBoxElem.style.visibility = "hidden";
if (this.foundRange && selection.rangeCount === 0) {
// Restore selection. This is needed because on iOS the selection is
// cleared by touching the Find Next/Previous button in the Find box.
selection.addRange(this.foundRange);
this.foundRange = null;
this.fixHighlight();
}
const found = windowF.find(text, false, backwards);
findBoxElem.style.visibility = "";

Expand All @@ -202,6 +213,8 @@ class FindBox {
"[data-vivliostyle-page-container]",
)
) {
this.foundRange = selection.getRangeAt(0);
this.fixHighlight();
return true;
} else {
selection.removeAllRanges();
Expand All @@ -210,6 +223,43 @@ class FindBox {
return false;
}

private fixHighlight(): void {
// Workaround for the WebKit bug on iOS:
// Selection highlight not shown on programmatic focus after initial load
// https://bugs.webkit.org/show_bug.cgi?id=199211

if (!/iPad|iPhone|iPod/.test(navigator.userAgent)) {
// No problem on non-iOS.
return;
}

const oldHighlightElements = document.querySelectorAll(
"[data-viv-found-highlight='true']",
);
for (const oldHighlightElement of oldHighlightElements) {
oldHighlightElement.remove();
}
if (!this.foundRange) {
return;
}
const parent = this.foundRange.startContainer.parentElement.closest(
"[data-vivliostyle-page-container='true']",
);
const parentRect = scaleRect(parent.getBoundingClientRect());
for (const clientRect of this.foundRange.getClientRects()) {
const rect = applyTransformToRect(scaleRect(clientRect), parentRect);
const highlightElement = document.createElement("div");
highlightElement.setAttribute("data-viv-found-highlight", "true");
highlightElement.style.position = "absolute";
highlightElement.style.left = `${rect.left}px`;
highlightElement.style.top = `${rect.top}px`;
highlightElement.style.width = `${rect.width}px`;
highlightElement.style.height = `${rect.height}px`;
highlightElement.style.backgroundColor = "#0080ff33";
parent.appendChild(highlightElement);
}
}

/**
* @returns true if the key remains unconsumed
*/
Expand Down
4 changes: 2 additions & 2 deletions packages/viewer/src/viewmodels/marks-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,8 +195,8 @@ const highlight = (
div.style.position = "absolute";
div.style.margin = "0";
div.style.padding = "0";
div.style.top = `${rect.top + window.scrollY}px`;
div.style.left = `${rect.left + window.scrollX}px`;
div.style.top = `${rect.top}px`;
div.style.left = `${rect.left}px`;
div.style.width = `${rect.width}px`;
div.style.height = `${rect.height}px`;
applyStyle(div, style);
Expand Down

0 comments on commit abb3ee2

Please sign in to comment.