I have a parent div whose height can vary based on window vertical resize. That parent div needs to employ overflow-y:scroll
for sought functionality. The parent div contains 8 child divs of various fixed heights positioned vertically, with first and last child having a CSS height of 70vh
in order to be able to scroll the 6 vertical middle-positioned child divs (6pack) all the way to the top or bottom of parent div.
Now, I want the parent div to dynamically scroll the 6pack to the exact location where my mouse enters the parent div via mouseenter
event (not mousemove
), making certain that the bottom of the 6pack doesn't go lower than the bottom of parent div, and the top of the 6pack doesn't go higher than the parent top.
The code below is as close as I could get, one problem, among others, is the current vertical scroll direction being opposite of what it should be:
const parentElement =
document.getElementById('parentContainer');
parentElement.addEventListener('mouseenter', (event) => {
var mouseYRelativeToParent =
parentElement.getBoundingClientRect().y + event.clientY;
parentElement.scrollTop = mouseYRelativeToParent;
});
A better working version in JavaScript or jquery, with detailed comments on how parts of code work, would be most helpful.
Here is javascript code that gets the job done, based on kikon's jsfiddle provided in the comments to my question which in turn was inspired by parts of masoudiofficial answer.
Code below is the javascript part taken from an amended jsfiddle that can be found here.
const parentElement = document.getElementById('parentElement'),
sixPackFirstElement = document.querySelector('#parentElement > div:nth-of-type(2)'),
sixPackLastElement = document.querySelector('#parentElement > div:nth-of-type(7)'),
sixPackTop = sixPackFirstElement.offsetTop,
sixPackBottom = sixPackLastElement.offsetTop + sixPackLastElement.offsetHeight,
// yScrollOffset = sixPackTop, // to attempt to position the top of the 6pack at the cursor
yScrollOffset = (sixPackTop + sixPackBottom) / 2, // to attempt to position the center of the 6pack at the cursor
yScrollMin = sixPackBottom - parentElement.offsetHeight;
parentElement.addEventListener('mouseenter', (event) => {
const yScrollSixPackTop = yScrollOffset - event.offsetY;
// or, using event.clientY:
//const yScrollSixPackTop = yScrollOffset + parentElement.getBoundingClientRect().y - event.clientY;
parentElement.scrollTop = Math.min(Math.max(yScrollMin, yScrollSixPackTop), sixPackTop);
});
Requirement:
The goal is for the "parent div to dynamically scroll the (child divs) to the exact location where my mouse enters the parent div via mouseenter
event". Let's expose the shared logic and result:
Logic:
const parentElement
comprises retrieval of a specific HTML element from the Document Object Model (DOM), assigned as a constant variable, which becomes the parent container in this case;
const sixPackFirstElement
, const sixPackLastElement
, const sixPackTop
, const sixPackBottom
, const yScrollOffset
and const yScrollMin
respectively, comprise two child elements and their combined calculated offsets used to fulfill logic requirement;
parentElement.addEventListener('mouseenter', (event) => {
is the standard JavaScript method used to register an event handler to a specific element. In our case, mouseenter
is the event we are listening for, with the help of an arrow function ((event) => { ... }
) that serves as the event handler;
const yScrollSixPackTop
serves to use children middle-height based on the combination of children heights divided by 2 (yScrollOffset
), minus the mouse pointer offset which is "the Y coordinate of the mouse pointer between that event and the padding edge of the target node." (See MDN for specifics);
Result:
parentElement.scrollTop
equals the math leading to expected result. Math.min(...)
"is often used to clip a value so that it is always less than or equal to a boundary." (See MDN for specifics), while Math.max(...)
"returns the largest of the numbers given as input parameters." (See MDN for specifics).
Therefore, the expression parentElement.scrollTop = Math.min(Math.max(yScrollMin, yScrollSixPackTop), sixPackTop)
ensures that the targeted children will be scrolled and positioned - according to the middle-height of their combined heights - vis-à-vis the position of mouse pointer once it enters the parent container via mouseenter
event, while not exceeding boundary limits imposed upon them (no top or bottom child overflow).
Thanks for all who have contributed and keep contributing to SO.