Semantic and Spatial Layouts Can't Share a Ruler: Page Snap with IntersectionObserver
TLDR
Synchronizing scroll positions between fixed physical PDF canvas pages (fixed $\sim 1100\text{px}$ height) and extracted reflowable HTML pages ($200\text{px}$ to $2500\text{px}$ dynamic height) via proportional ratio math (top / height) fails because spatial and semantic views lack vertical scale proportion. Replacing within-page ratio calculations with an IntersectionObserver page-snap coordinator anchored on matching data-page attributes provides precise synchronization across zoom levels.
| Synchronization Strategy | Metric Used | CSS Zoom Resiliency | Synchronization Result |
|---|---|---|---|
| Proportional Height Ratio | Pixel offsetTop & scrollTop | Defective (Chromium zoom mismatch) | Drifts & misaligns pages |
IntersectionObserver Page-Snap | Viewport intersection ratio | 100% Zoom Resilient | Precise Page Alignment |
Technical scroll synchronization engine
// IntersectionObserver Pane Observer for Page-Snap Synchronization
export function createPaneScrollObserver(containerPane, pageSelector, onActivePageChange) {
const intersectionRatios = new Map();
let currentActivePage = null;
const observer = new IntersectionObserver(entries => { for (const entry of entries) { intersectionRatios.set(entry.target, entry.intersectionRatio); }
let mostVisibleElement = null; let highestRatio = -1;
for (const [element, ratio] of intersectionRatios) { if (ratio > highestRatio) { highestRatio = ratio; mostVisibleElement = element; } }
if (!mostVisibleElement) return;
const pageId = mostVisibleElement.getAttribute('data-page'); if (pageId !== currentActivePage) { currentActivePage = pageId; onActivePageChange(pageId); } }, { root: containerPane, threshold: [0, 0.25, 0.5, 0.75, 1.0] });
const pageElements = containerPane.querySelectorAll(pageSelector); pageElements.forEach(pageEl => observer.observe(pageEl));
return observer; }
Rule of thumb: Synchronize multi-pane document editors using browser-native IntersectionObserver page anchors rather than proportional pixel scroll offsets.