iOS: -webkit-overflow-scrolling:touch and scrollTop - javascript

I have some trouble when trying to back to the top of a list.
I put -webkit-overflow-scrolling:touch on the list to get momentum scroll.
However when I'm using jQuery scrollTop(); while the momentum scroll is still going, it jumps to the top but it doesn't stop the momentum. So it keeps going down again until the momentum ends.
Is there an easy way to stop momentum scroll?

Set your list's -webkit-overflow-scrolling style to 'auto' and then back to 'touch' after a short timeout.
i.e.
let scrollContainer = document.getElementById('yourListId');
scrollContainer.style['-webkit-overflow-scrolling'] = 'auto'; // stop scroll
setTimeout(function() {
scrollContainer.scrollTop = 0; // or jQuery scrollTop()
scrollContainer.style['-webkit-overflow-scrolling'] = 'touch'; // re-enable
}, 2);
Reference: this SO answer. (modified their implementation to get rid of scrollbar redraw jerkiness on desktop browsers)

Related

`window.ScrollTo(...)` in a `mousemove` event gives horrible janky scrolling

I've got a custom scrollbar solution (view on CodePen).
The obvious idea is dragging the custom scrollbar should scroll the page.
Try it and see what happens... it's bizarrely janky, and the scrollbar and page scrolling will suddenly snap between points.
The scrolling process is currently in a mousemove handler:
update the scrollbar position visually
window.scrollTo(...) the new position, calculated as viewport top relative to the new scrollbar position
If I comment out the window.scrollTo(...) line, the scrollbar itself then moves perfectly smoothly and sticks with the cursor.
Pertinent code
mousemove(e) {
if (!this.active) return;
this.update(this.getScrollDeltaPositional(e.pageY));
window.scrollTo({top: this.getWindowScrollTop()});
}
update(position, show=true, timer=true, time=0) {
let track = this.getTrackHeight();
this.trackPosition = Math.min(Math.max(position, 0), track);
this.track.style.transform = `translateY(${this.trackPosition}px)`;
}
getWindowScrollTop() {
let scroll = this.getDocumentScroll();
let position = (this.trackPosition / this.root.clientHeight);
return Math.round(scroll * position);
}
(Recommended you view the full source on CodePen)
I presume the scrolling each mousemove is blocking the mousemove events, resulting in the sudden snaps being observed.
How to achieve a smooth scrolling effect on window using a custom scrollbar?
I finally found the answer
After far too many hours of trying everything conceivable to remedy this, I stumbled upon this identical problem: https://css-tricks.com/forums/topic/scrolltop-inexplicably-going-haywire/.
As that user eventually discovered, MouseEvent.pageY (which is what I was using to get scroll position) is
relative to the top edge of the viewport, including scroll offsets.
Therefore, the scroll movement effectively amplifies the mousemove events, causing the scrolling to accelerate exponentially as seen in the demo.
So after half a day of hacking about with this, the fix is a simple Ctrl+H... use MouseEvent.clientY instead.

Unexpected jump while changing position from absolute to fixed, why?

first of all I want to say that it's not about content jumping!
I have a navbar and a sidebar which both have absolute position. after user scrolls 100 pixels I change both of them to fixed. but an odd action happens (not always!). wrappers of navbar and sidebar flush for a second. I tested it with different browsers and it does not depend on browser. I tried to reproduce the situation in this fiddle:
https://jsfiddle.net/addxmkgj/
(resize the screen as large as possible it happens in large screens)
-- Edit --
https://codepen.io/anon/pen/dJKBPe
codepen link added too.
Causes
Scrolling can generate scroll events quickly and handlers may need to either throttle scroll events to some extent (e.g. perform code action after scrolling has stopped) or be fairly lightweight functions that can execute quickly.
In addition scroll event handling is not synchronized with page update: if the mouse wheel initiates downward scrolling, scrolling can continue after the wheel is released (and similarly with touch event scrolling). The browser can scroll below a top position of 100px before scroll event handling has had a chance to catch up and change the positioning.
The result is the header jumps down from being partially off-screen to occupy a fixed position at top of screen. The faster the scroll action (or the busier the browser is) the more likely it is that jumping will be noticeable.
A secondary effect in desktop browsing is that when the side bar panel scrolls upwards past top of screen and moves down again, a visible patch of white screen "flashes" momentarily below the side bar before fixed positioning takes effect.
Experimental Remedies
Flashing of the side bar can be reduced but not necessarily fully eliminated, by increasing the height of the container. Changing the height to 150% with visible overflow met with some success:
.side-bar {
position: absolute;
height: 150%;
... /* more declarations */
This may or may not conflict with application requirements.
Some mitigation of navbar jumping can be achieved by using requestAnimationFrame call backs to monitor scrollTop values and change positioning as necessary. This does not use scroll event handling as such:
$(document).ready(function() {
$(window).resize(function() {
if( $(window).width() > 850) {
$('.navbar').css('display', 'block');
} else {
$('.navbar').css('display', 'none');
}
});
scrollTo(0, 0);
var num = 100;
var bAbsolute = true;
function checkScroll() {
var newTop = $(window).scrollTop();
if( bAbsolute && newTop >= num) {
$('.navbar').css('position', 'fixed');
$('.navbar').css('top', '0');
$('.side-bar').css('position', 'fixed');
$('.side-bar').css('top', '0');
bAbsolute = false;
}
if( !bAbsolute && newTop < num) {
$('.navbar').css('position', 'absolute');
$('.side-bar').css('position', 'absolute');
$('.navbar').css('top', '100px');
$('.side-bar').css('top', '100px');
bAbsolute = true;
}
requestAnimationFrame( checkScroll);
}
requestAnimationFrame( checkScroll)
});
This code showed an improvement in jump reduction but was not perfect. It is not particularly a JQuery solution and calls requestAnimationFrame directly.
One option, of course, is to do nothing given browser timing constraints.
Update
This MDN guide for Scroll linked effects explains the root cause problem better than I was able to:
most browsers now support some sort of asynchronous scrolling .... the visual scroll position is updated in the compositor thread and is visible to the user before the scroll event is updated in the DOM and fired on the main thread ... This can cause the effect to be laggy, janky, or jittery — in short, something we want to avoid.
So the absolutely positioned elements can scroll off screen (to some extent) before scroll handlers are notified of a new scroll position.
The solution going forward is to use sticky positioning (see the scroll effects guide above or the CSS position guide. However position:sticky swaps between relative and fixed position so the HTML would need redesigning to accommodate this.
Sticky positioning is also leading edge technology at January 2018, and not yet recommended for production use on MDN. A web search for "JQuery support sticky position" revealed a choice of JQuery plugin support.
Recommendation
Potentially the best-case compromise may be to redesign the HTML to use sticky positioning and include a JQuery plugin that uses native support when available or a polyfill when not - site visitors with supporting browsers will get the best experience, those with older browsers will get functional support.

Halt scrolling momentum in iScroll 5

When scrolling with momentum in iScroll 5, is there a way to interrupt the scroll, stopping it in its tracks?
I have attempted toggling the momentum option, and forcing another scrollTo but haven't quite found the behaviour I need.
Here are my failed attempts: http://jsfiddle.net/chvtmwz5/4/
// only comes into effect once scroll is finished:
myScroll.options.momentum = false;
// halts scroll, but forces it back to the start unless we know the current scroll
myScroll.scrollTo(0,0);
// halts div scroll but allows scrollbar scroll animation to continue
var currentX = myScroll.x;
myScroll.scrollTo(currentX + 1, 0); // (scrolling to the current value of x has no effect)

window scroll method flickers in IE

This may come as a huge surprise to some people but I am having an issue with the IE browser when I am using the $(window).scroll method.
My goal:
I would like to have the menu located on the left retain it's position until the scroll reaches > y value. It will then fix itself to the top of the page until the scroll returns to a < y value.
My error:
Everything seems just fine in Chrome and Firefox but when I go to Internet Explorer it would seem the browser is moving #scroller every time the scroll value changes, this is causing a moving/flickering event.
If someone could point me to a resource or give me a workaround for this I would be very grateful!
Here is a fiddle:
http://jsfiddle.net/CampbeII/nLK7j/
Here is a link to the site in dev:
http://squ4reone.com/domains/ottawakaraoke/Squ4reone/responsive/index.php
My script:
$(window).scroll(function () {
var navigation = $(window).scrollTop();
if (navigation > 400) {
$('#scroller').css('top',navigation - 220);
} else {
$('#scroller').css('top',183);
$('#scroller').css('position','relative');
}
});
You might want to take a look at the jQuery Waypoints plugin, it lets you do sticky elements like this and a lot more.
If you want to stick with your current method, like the other answers have indicated you should toggle fixed positioning instead of updating the .top attribute in every scroll event. However, I would also introduce a flag to track whether or not it is currently stuck, this way you are only updating the position and top attributes when it actually make the transition instead of every scroll event. Interacting with the DOM is computationally expensive, this will take a lot of load off of the layout engine and should make things even smoother.
http://jsfiddle.net/WYNcj/6/
$(function () {
var stuck = false,
stickAt = $('#scroller').offset().top;
$(window).scroll(function () {
var scrollTop = $(window).scrollTop();
if (!stuck && scrollTop > stickAt) {
$('#scroller').css('top', 0);
$('#scroller').css('position','fixed');
stuck = true;
} else if (stuck && scrollTop < stickAt) {
$('#scroller').css('top', stickAt);
$('#scroller').css('position','absolute');
stuck = false;
}
});
});
Update
Switching the #scroller from relative to fixed removes it from the normal flow of the page, this can have unintended consequences for the layout as it re-flows without the missing block. If you change #scroller to use an absolute position it will be removed from the normal flow and will no longer cause these side-effects. I've updated the above example and the linked jsfiddle to reflect the changes to the JS/CSS.
I also changed the way that stickAt is calculated as well, it uses .offset() to find the exact position of the top of #scoller instead of relying on the CSS top value.
Instead of setting the top distance at each scroll event, please consider only switching between a fixed position and an absolute or relative position.All browsers will appreciate and Especially IE.
So you still listen to scroll but you now keep a state flag out of the scroll handler and simply evaluate if it has to switch between display types.
That is so much more optimized and IE likes it.
I can get flickers in Chrome as well if I scroll very quickly. Instead of updating the top position on scroll, instead used the fixed position for your element once the page has scrolled below the threshold. Take a look at the updated fiddle: http://jsfiddle.net/nLK7j/2/

How do a add to scrollTop based on the intended scroll distance of another element

fiddle: http://jsfiddle.net/DerNalia/88N4d/9/
The behavior I'm trying to accomplish:
The Sidebar(s) should be fixed on the page if there is enough room
for them
The Sidebar(s) should scroll if there is ever not enough room for them
When scrolling down, and the bottom of the sidebar(s) is reached, it should stop scrolling
When scrolling down, the sidebar(s) should also scroll down, until
the bottom of the sidebar(s) is/are reached
When scrolling up, and the top of the sidebar(s) is reached, it should stop scrolling
If at any point during the scrolling of the content, the user switches directions of scrolling, the sidebar(s) shall also move in the same direction as the rest of the page / content
When scrolling up, the sidebar(s) should also scroll up, until the
top of the sidebar(s) is/are reached
If the content is shorter than the sidebar(s), the sidebar(s) should
still be able to scroll This is the one that I'm having trouble
with
How do I make it so that I can detect the intended scroll distance desired by the user, rather than use the actual scrolled distance of the content body? There may be another solution, but this is all I can think of for right now.
I'm currently using Chrome on Mac.
UPDATE:
something I've noticed: using the track pad on macs does the stretching / bouncy scrolling shenanigans on the edges.. which messes up this javascript hard core. It's possible to scroll the sidebar completely off the screen if you bounce up enough times. Mouse Wheel scrolling does not have this issue.
I think you’d be much better off positioning the columns absolute and then check positions onscroll and toggle the positions.
It gets quite complicated, since the scroll will jump if both columns are fixed and the content has regular flow.
I created a solution for you using a simpler logic that goes:
var $win = $(window);
var $containers = $(".container").css('position','absolute');
// we need to force a height to the body for fixed positioning
$('body').css('minHeight', Math.max.apply( Math, $containers.map(function() {
return $(this).height();
})));
$win.bind("resize scroll", function() {
var scrolled = $win.scrollTop(),
winheight = $win.height(),
elheight = 0;
$containers.each(function() {
elheight = $(this).height();
$(this).css('position', function() {
if ( elheight > (winheight+scrolled) ) {
$(this).css('top',0);
return 'absolute';
}
$(this).css('top', Math.min(0, winheight-elheight));
return 'fixed';
});
});
});
It should fill your requirements. The fixed positioning kicks in if the columns are shorter than the window height, or if the scrollTop is enough.
A demo in all it’s glory: http://jsfiddle.net/mb9qC/

Categories

Resources