Reputation: 5005
I am using ScrollIntoView()
to scroll the highlighted item in a list into view.
When I scroll downwards ScrollIntoView(false)
works perfectly.
But when I scroll upwards, ScrollIntoView(true)
is causing the whole page to move a little which I think is intended.
Is there a way to avoid the whole page move when using ScrollIntoView(true)
?
Here is the structure of my page
#listOfDivs {
position:fixed;
top:100px;
width: 300px;
height: 300px;
overflow-y: scroll;
}
<div id="container">
<div id="content">
<div id="listOfDivs">
<div id="item1"> </div>
<div id="item2"> </div>
<div id="itemn"> </div>
</div>
</div>
</div>
listOfDivs
is coming from ajax call. Using mobile safari.
Upvotes: 263
Views: 239057
Reputation: 482
I wrote this and solved my problem.
function scrollSmoothTargetElmToCenterOfparentElm(parentElm, targetElm) {
const parentElmHalfOfHeight = parentElm.offsetHeight / 2
const activeElmHalfOfHeight = targetElm.offsetHeight / 2
const finalScrollPosition =
targetElm.offsetTop - parentElm.offsetTop - parentElmHalfOfHeight + activeElmHalfOfHeight
let currentScrollPosition = parentElm.scrollTop
const INT = setInterval(function () {
currentScrollPosition++
parentElm.scrollTop = currentScrollPosition
if (currentScrollPosition + 1 >= finalScrollPosition) clearInterval(INT)
})
}
// sample of usage
const parentElm = document.getElementById("parent")
const targetElm = document.getElementById("target")
scrollSmoothTargetElmToCenterOfparentElm(parentElm, targetElm)
Upvotes: 0
Reputation: 939
Spent too many hours on a similar issue, the only thing that worked was using
overflow: clip
(instead of hidden
)
on the container element that was scrolling too far.
Quoting from the MDN docs
clip: Overflow content is clipped at the element's overflow clip edge that is defined using the overflow-clip-margin property. As a result, content overflows the element's padding box by the value of overflow-clip-margin or by 0px if not set. Overflow content outside the clipped region is not visible, user agents do not add a scroll bar, and programmatic scrolling is also not supported. No new formatting context is created. To establish a formatting context, use overflow: clip along with display: flow-root. The element box is not a scroll container.
HT to this comment above for suggesting it.
Upvotes: 2
Reputation: 616
FWIW: I found (in Chrome 95, and Firefox 92 (all Mac)) that using:
.scrollIntoView({ behavior:'smooth', block:'center'});
on a scrollable list of options would scroll the body element a little, so I opted to use:
.scrollIntoView({ behavior:'smooth', block:'nearest'});
and select an option past the one I wanted centered (e.g. in a scrollable elem with 5 lines/options viewable, I selected the 2nd option past the one I wanted centered, thereby centering the desired element.
Upvotes: 5
Reputation: 2495
I found (in Chrome) I could more reliably scroll my element to the top of my parent div (without moving the page) if I scrolled from the bottom up to my element rather than from the top down to my element. Otherwise while my element would scroll into view, it would sometimes still be lower than desired within the div.
To achieve this, I am scrolling in two steps:
myScrollableDiv.scrollTop = myScrollableDiv.scrollHeight
which instantly scrolls to the bottom of my scrollable divmyElementWithinTheScrollingDiv.scrollIntoView({
behavior: 'smooth',
block: 'nearest',
})
Upvotes: 4
Reputation: 153
I've added a way to display the imporper behavior of the ScrollIntoView - http://jsfiddle.net/LEqjm/258/ [it should be a comment but I don't have enough reputation]
$("ul").click(function() {
var target = document.getElementById("target");
if ($('#scrollTop').attr('checked')) {
target.parentNode.scrollTop = target.offsetTop;
} else {
target.scrollIntoView(!0);
}
});
Upvotes: 10
Reputation: 91
ScrollIntoView() causes page movement. But the following code works fine for me and move the screen to the top of the element:
window.scroll({
top: document.getElementById('your-element')?.offsetParent.offsetTop,
behavior: 'smooth',
block: 'start',
})
Upvotes: 2
Reputation: 1406
Just to add an answer as per my latest experience and working on VueJs. I found below piece of code ad best, which does not impact your application in anyways.
const el = this.$el.getElementsByClassName('your_element_class')[0];
if (el) {
scrollIntoView(el,
{
block: 'nearest',
inline: 'start',
behavior: 'smooth',
boundary: document.getElementsByClassName('main_app_class')[0]
});
}
main_app_class
is the root class
your_element_class
is the element/view where you can to scroll into
And for browser which does not support ScrollIntoView() just use below library its awesome https://www.npmjs.com/package/scroll-into-view-if-needed
Upvotes: 10
Reputation: 461
in my context, he would push the sticky toolbar off the screen, or enter next to a fab button with absolute.
using the nearest solved.
const element = this.element.nativeElement;
const table = element.querySelector('.table-container');
table.scrollIntoView({
behavior: 'smooth', block: 'nearest'
});
Upvotes: 16
Reputation: 9568
Adding more information to @Jesco
post.
Element.scrollIntoViewIfNeeded()
non-standard WebKit method
for Chrome, Opera, Safari browsers.
Element.scrollIntoView()
method scrolls the element on which it's called into the visible area of the browser window.Try the below code in mozilla.org
scrollIntoView()
link. Post to identify Browser
var xpath = '//*[@id="Notes"]';
ScrollToElement(xpath);
function ScrollToElement(xpath) {
var ele = $x(xpath)[0];
console.log( ele );
var isChrome = !!window.chrome && (!!window.chrome.webstore || !!window.chrome.runtime);
if (isChrome) { // Chrome
ele.scrollIntoViewIfNeeded();
} else {
var inlineCenter = { behavior: 'smooth', block: 'center', inline: 'start' };
ele.scrollIntoView(inlineCenter);
}
}
Upvotes: 8
Reputation: 1440
I had this problem too, and spent many hours trying to deal with it. I hope my resolution may still help some people.
My fix ended up being:
.scrollIntoView()
to .scrollIntoView({block: 'nearest'})
(thanks to @jfrohn).overflow: -moz-hidden-unscrollable;
on the container element that shifts.Upvotes: 23
Reputation: 3649
Fixed it with:
element.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'start' })
see: https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView
Upvotes: 364
Reputation: 5854
var el = document.querySelector("yourElement");
window.scroll({top: el.offsetTop, behavior: 'smooth'});
Upvotes: 24
Reputation: 774
Using Brilliant's idea, here's a solution that only (vertically) scrolls if the element is NOT currently visible. The idea is to get the bounding box of the viewport and the element to be displayed in browser-window coordinate space. Check if it's visible and if not, scroll by the required distance so the element is shown at the top or bottom of the viewport.
function ensure_visible(element_id)
{
// adjust these two to match your HTML hierarchy
var element_to_show = document.getElementById(element_id);
var scrolling_parent = element_to_show.parentElement;
var top = parseInt(scrolling_parent.getBoundingClientRect().top);
var bot = parseInt(scrolling_parent.getBoundingClientRect().bottom);
var now_top = parseInt(element_to_show.getBoundingClientRect().top);
var now_bot = parseInt(element_to_show.getBoundingClientRect().bottom);
// console.log("Element: "+now_top+";"+(now_bot)+" Viewport:"+top+";"+(bot) );
var scroll_by = 0;
if(now_top < top)
scroll_by = -(top - now_top);
else if(now_bot > bot)
scroll_by = now_bot - bot;
if(scroll_by != 0)
{
scrolling_parent.scrollTop += scroll_by; // tr.offsetTop;
}
}
Upvotes: 2
Reputation: 183
i had the same problem, i fixed it by removing the transform:translateY
CSS i placed on the footer
of the page.
Upvotes: 1
Reputation: 151
Play around with scrollIntoViewIfNeeded()
... make sure it's supported by the browser.
Upvotes: 15
Reputation: 13714
You could use scrollTop
instead of scrollIntoView()
:
var target = document.getElementById("target");
target.parentNode.scrollTop = target.offsetTop;
jsFiddle: http://jsfiddle.net/LEqjm/
If there's more than one scrollable element that you want to scroll, you'll need to change the scrollTop
of each one individually, based on the offsetTop
s of the intervening elements. This should give you the fine-grained control to avoid the problem you're having.
EDIT: offsetTop isn't necessarily relative to the parent element - it's relative to the first positioned ancestor. If the parent element isn't positioned (relative, absolute or fixed), you may need to change the second line to:
target.parentNode.scrollTop = target.offsetTop - target.parentNode.offsetTop;
Upvotes: 188
Reputation: 105029
scrollintoview()
increases usabilityInstead of default DOM implementation you can use a plugin that animates movement and doesn't have any unwanted effects. Here's the simplest way of using it with defaults:
$("yourTargetLiSelector").scrollintoview();
Anyway head over to this blog post where you can read all the details and will eventually get you to GitHub source codeof the plugin.
This plugin automatically searches for the closest scrollable ancestor element and scrolls it so that selected element is inside its visible view port. If the element is already in the view port it doesn't do anything of course.
Upvotes: 8