Building off of 0stone0's answer (with some added stuff to make the scroll more smooth and testing all the pointer events).
// scroll bar functionality for fixed element
// https://stackoverflow.com/questions/41592349/allow-pointer-click-events-to-pass-through-element-whilst-maintaining-scroll-f#
function scroller(event) {
scrollable = document.getElementById("scrollable");
switch (event.deltaMode) {
case 0: //DOM_DELTA_PIXEL Chrome
scrollable.scrollTop += event.deltaY
scrollable.scrollLeft += event.deltaX
break;
case 1: //DOM_DELTA_LINE Firefox
scrollable.scrollTop += 15 * event.deltaY
scrollable.scrollLeft += 15 * event.deltaX
break;
case 2: //DOM_DELTA_PAGE
scrollable.scrollTop += 0.03 * event.deltaY
scrollable.scrollLeft += 0.03 * event.deltaX
break;
}
event.stopPropagation();
event.preventDefault()
}
document.onwheel = scroller;
// scroll to an item that inside a div
// https://stackoverflow.com/questions/45408920/plain-javascript-scrollintoview-inside-div
function scrollParentToChild(parent, child, threshold = 0) {
// Where is the parent on page
const parentRect = parent.getBoundingClientRect();
// What can you see?
const parentViewableArea = {
height: parent.clientHeight,
width: parent.clientWidth,
};
// Where is the child
const childRect = child.getBoundingClientRect();
// Is the child viewable?
const isViewableVertically = childRect.top >= parentRect.top &&
childRect.bottom <= parentRect.top + parentViewableArea.height;
const isViewableHorizontally = childRect.left >= parentRect.left &&
childRect.right <= parentRect.left + parentViewableArea.width;
// if you can't see the child try to scroll parent
if (!isViewableVertically || !isViewableHorizontally) {
// Should we scroll using top or bottom? Find the smaller ABS adjustment
const scrollTop = childRect.top - parentRect.top;
const scrollBot = childRect.bottom - parentRect.bottom;
const scrollLeft = childRect.left - parentRect.left;
const scrollRight = childRect.right - parentRect.right;
if (Math.abs(scrollTop) < Math.abs(scrollBot) && Math.abs(scrollLeft) < Math.abs(scrollRight)) {
// we're nearer to the top and left of the list
parent.scrollTo({
top: parent.scrollTop + scrollTop - threshold,
left: parent.scrollLeft + scrollLeft - threshold,
behavior: 'smooth',
});
} else {
// we're nearer to the bottom and right of the list
parent.scrollTo({
top: parent.scrollTop + scrollBot + threshold,
left: parent.scrollLeft + scrollRight + threshold,
behavior: 'smooth',
});
}
}
}
html,
body {
overflow: hidden;
margin: 0;
padding: 0;
background-color: pink;
}
.container {
position: relative;
width: 100%;
height: 100vh;
margin: 0;
padding: 0;
box-sizing: border-box;
}
#stage-layer {
position: absolute;
width: 100vw;
height: 100vh;
background-color: yellow;
margin: 0;
padding: 0;
}
#application-layer {
position: relative;
height: 100%;
margin: 0;
padding: 0;
}
rect:hover {
fill: blue;
}
#scrollable {
position: relative;
overflow: auto;
color: hotpink;
height: 100%;
width: 100%;
background-color: blue;
padding-left: 0;
}
p:hover {
color: white;
transform: translateX(20px);
transition: 0.3s;
}
.menu {
position: fixed;
background-color: red;
width: 100px;
z-index: 100;
}
.hover:hover {
background-color: orange;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="styles.css">
<title>Blank HTML</title>
</head>
<body>
<div class="container">
<svg id="stage-layer">
<rect></rect>
</svg>
<div id="application-layer">
<div class="menu">
<p onClick="scrollParentToChild(document.getElementById('scrollable'), document.getElementById('first'));">
go to top of page</p>
<p onClick="scrollParentToChild(document.getElementById('scrollable'), document.getElementById('last'));">
go to bottom of page</p>
</div>
<div id="scrollable">
<li id="first">_________</li>
<li>_________</li>
<li>_________</li>
<li>_________</li>
<li>_________</li>
<li>_________</li>
<li>_________</li>
<li>_________</li>
<li>_________</li>
<li>_________</li>
<li>_________</li>
<li>_________</li>
<li>_________</li>
<li>_________</li>
<li>_________</li>
<li>_________</li>
<li>_________</li>
<li>_________</li>
<li class="hover">HOVER TEST</li>
<li>_________</li>
<li>_________</li>
<li><a class="link" href="https://www.google.com/">LINK TEST</a></li>
<li>_________</li>
<li>_________</li>
<li>_________</li>
<li>_________</li>
<li>_________</li>
<li>_________</li>
<li>_________</li>
<li>_________</li>
<li>_________</li>
<li>_________</li>
<li>_________</li>
<li>_________</li>
<li>_________</li>
<li>_________</li>
<li>_________</li>
<li>_________</li>
<li>_________</li>
<li>_________</li>
<li>_________</li>
<li id="last">_________</li>
</div>
</div>
</div>
<script src="/website/test/scripts.js"></script>
</body>
</html>