Created
April 13, 2025 13:00
-
-
Save jan-swiecki/db2866ddd06e8c701b16f076239ab266 to your computer and use it in GitHub Desktop.
side-by-side-iframe-sync-scroll.html
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<title>Side-by-Side HTML Viewer</title> | |
<style> | |
/* Ensure the body takes the full height */ | |
html, body { | |
margin: 0; | |
padding: 0; | |
height: 100%; | |
} | |
/* Use Flexbox for a side-by-side layout */ | |
.container { | |
display: flex; | |
height: 100%; | |
} | |
/* Each iframe will take half the width of the container */ | |
.container iframe { | |
flex: 1; /* Each iframe takes equal space */ | |
border: none; /* Remove default border */ | |
height: 100%; | |
overflow: auto; /* Enable scroll if needed */ | |
} | |
</style> | |
</head> | |
<body> | |
<div id="container" class="container"> | |
<iframe class="scrollable" src="file1.html" title="HTML File 1"></iframe> | |
<iframe class="scrollable" src="file2.html" title="HTML File 2"></iframe> | |
</div> | |
<script> | |
// Sync scroll / sync scrollbars / sync scroll bars (for later grepping this solution) | |
// Source: | |
// - https://phuoc.ng/collection/html-dom/synchronize-scroll-positions-between-two-elements/ | |
// - https://chatgpt.com/share/67fafd76-2ac0-8001-9575-b377be903e05 | |
const randomInteger = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min; | |
document.addEventListener("DOMContentLoaded", async () => { | |
const container = document.getElementById("container"); | |
const elements_ = [...container.querySelectorAll(".scrollable")]; | |
let elements = []; | |
for(const ele_ of elements_) { | |
const ele = await new Promise((resolve) => { | |
ele_.addEventListener("load", () => { | |
const iframeDoc = ele_.contentDocument || ele_.contentWindow.document; | |
resolve(iframeDoc); | |
}); | |
}); | |
elements.push(ele); | |
} | |
const syncScroll = (scrolledEle, ele) => { | |
if(scrolledEle.documentElement) scrolledEle = scrolledEle.documentElement; | |
if(ele.documentElement) ele = ele.documentElement; | |
const scrolledPercent = scrolledEle.scrollTop / (scrolledEle.scrollHeight - scrolledEle.clientHeight); | |
const top = scrolledPercent * (ele.scrollHeight - ele.clientHeight); | |
const scrolledWidthPercent = scrolledEle.scrollLeft / (scrolledEle.scrollWidth - scrolledEle.clientWidth); | |
const left = scrolledWidthPercent * (ele.scrollWidth - ele.clientWidth); | |
ele.scrollTo({ | |
behavior: "instant", | |
top, | |
left, | |
}); | |
}; | |
const handleScroll = (e) => { | |
new Promise((resolve) => { | |
requestAnimationFrame(() => resolve()); | |
}); | |
const scrolledEle = e.target; | |
elements.filter((item) => item !== scrolledEle).forEach((ele) => { | |
ele.removeEventListener("scroll", handleScroll); | |
syncScroll(scrolledEle, ele); | |
window.requestAnimationFrame(() => { | |
ele.addEventListener("scroll", handleScroll); | |
}); | |
}); | |
}; | |
elements.forEach((ele) => { | |
ele.addEventListener("scroll", handleScroll); | |
}); | |
}); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment