I don't know what this scrolling effect is called so don't know how to search for my answer, and I can't find it in the source code. What is the code responsible for the scrolling effect at Paragon where scrolling will cause the page to scroll down to the next div I assume. Or down a certain height.
I've thrown together a CodePen. It hase no animations but explains the general mechanics (the Paragon site does it differently but to start the following might be better suited).
The core part is this:
window.onwheel = function ( e ) {
e.preventDefault();
var wDelta = e.deltaY > 0 ? 'down' : 'up';
if (wDelta === "down") {
// scroll Down
} else {
// scroll Up
}
}
To know where to scroll we'll of course need to know where we are. There are several ways to do that. What I did is check the current top of the viewport against it's height.
var offset = window.pageYOffset,
viewportHeight = document.documentElement.clientHeight;
switch (parseInt(offset/viewportHeight,0)) {
case 0:
// we are in the first viewport
break;
case 1:
// we are in the second viewport
break;
...
}
I just checked it for functionality. Performance wise it can be improved upon. You should also bind keydown (to catch pageUp, pageDown, space and so on) but it can be done in a similar fashion. To ensure backwards compatibility you'll need to expand the code (e.g. a binding to the onmousewheel event). But this will give you a place to start.
PS
Also consider what behaviour you want when reloading the page (if the user reloads while between viewports it will stay between them until another scroll occurs).
This answer could also interest you.
Related
I have a site where I have each section as 100vh so it fills the height of the screen perfectly. The next step I wanted to implement was disabling the regular scrolling, and on scroll force the screen to jump smoothly to the top of the next 100vh section. Here is the example of this animation / feature:
https://www.quay.com.au/
I was having a hard time finding any answers for this as most things just deal with smooth scrolling when clicking on anchors, not actually forcing div relocation when the user scrolls up / down.
I just wanted to know what code I would need do this...
Thanks, been using stack overflow for a while but first post, let me know if there is anything I can do to make this more clear.
disclaimer: this solution needs some testing and probably a bit of improvements, but works for me
if you don't want to use a plugin and prefer a vanilla JavaScript solution I hacked together a small example how this can be achieved with JS features in the following codepen:
https://codepen.io/lehnerchristian/pen/QYPBbX
but the main part is:
function(e) {
console.log(e);
const delta = e.deltaY;
// check which direction we should scroll
if (delta > 0 && currentlyVisible.nextElementSibling) {
// scroll downwards
currentlyVisible = currentlyVisible.nextElementSibling;
} else if (delta < 0 && currentlyVisible.previousElementSibling) {
// scroll upwards
currentlyVisible = currentlyVisible.previousElementSibling;
} else {
return false;
}
// perform scroll
currentlyVisible.scrollIntoView({ behavior: 'smooth' });
e.preventDefault();
e.stopPropagation();
}
what it does is that it listens for the wheel event and then calls the callback, which intercepts the scroll event. inside the callback the direction is determined and then Element.scrollIntoView() is called to let the browser do the actual scrolling
check https://caniuse.com/#search=scrollintoview for browser support, if you're going for this solution
I have built my own web page based on the same concept as this demo: https://ihatetomatoes.net/demos/full-screen-layout-with-skrollr/
The problem is when inexperienced users browse on their mobile units. They immediately try to scroll horizontally mid way through the page.
My intention is to enable horizontal scrolling as it were vertical as well. That would be to interpret "scrolling" right on an iPad would be equal to vertical scrolling downward. In addition to normal vertical scrolling.
Is there any jquery function to enable this ?
I doubt your usability design is the best, because the behaviour of 'inexperienced' users might just be the way normal users are used to interact with an app/webpage. Fancy animations might look cool, but could distract from the content and confuse people.
In case you want to try it anyway, here is what i came up with:
$(window).scroll(function() {
var currPos = $(document).scrollLeft();
var callback = function() {
animationComplete = true;
}
if (lastPos < currPos && animationComplete) {
animationComplete = false;
console.log('scroll right');
$('body, html').animate({scrollTop: 200}, callback);
}
});
If a scrolling event happens, it checks if it was horizontal. If that is the case, a downward animation is triggered. Callback function is there so that no further animations are triggered, while still animating (otherwise it would block the vertical scrolling).
http://codepen.io/TobiObeck/pen/jrKBQw
Situation:
Suppose we are reading the content somewhere down the page that is built to be responsive. Suppose also that we resize the browser window to a smaller size and that some content above get extended down due to the thinner width, hence making the whole page longer. Then as we resize, whatever content we are looking at will get pushed down the page accordingly.
Example:
Suppose we were to look at the Helper classes section in this page. Then shrinking/expanding the window a sufficient amount moves the bit we were reading down/up the current view.
Prompt:
Is there any way we can fix this? I.e. maintain our current view of the page regardless of what happens to the contents above it when we resize the window.
Thoughts:
I am thinking that we could at least start with javascript and put an event on window resize. Then automatically scroll the page to the top-most element that was in our view on event fire. I don't know how this will affect the performance, however, especially in bigger pages.
There's also the problem of refering to the top-most element in current view. The top of our current view might be cutting off the top portion of some elements, not to mention that there's usually more than 1 element layered on top of one another at any point within the page. The notion of top-most element I've mentioned is not very well-defined :(
Also rather than a problem of responsive design in general, instead it seems to me like this is a problem with the default scrolling behaviour of web browsers? Or perhaps I am missing some circumstances where the current behaviour is desirable.
Edit 2 4
Updated fiddle (see fullscreen result) based on Rick Hitchcock's solution's solution.
With jQuery:
//onresize:
var scrollAmount;
if (topNode.getBoundingClientRect().top >= 0) {
scrollAmount = $(topNode).offset().top - topNode.getBoundingClientRect().top;
} else {
scrollAmount = $(topNode.offset().bottom - topNode.getBoundingClientRect().bottom;
}
$(window).scrollTop(scrollAmount);
The fiddle is acting a bit weird even in the same browsers, I've uploaded the same script using a free hosting here.
Still need to incorporate the IE, Opera and Safari fix for elementFromPoint.
Edit 3
Thanks for all the help, Rick Hitchcock. Welcome to stackoverflow, by the way :)
The discussion is turning into cross-browser compatibility issues so I've accepted your answer since we've pretty much got the answer to the original question. I'll still be fixing up my implementation though. The focus being cross-browser issues, topNode criteria, and topNode cut-off handling.
An edge case
While playing around with it, I noticed that when we were at the bottom of the page in a small viewport, then switch to a larger viewport (let us assume now that some more elements that were originally above the element we saw now came into view due to shorter container from wider viewport) the window cannot always lock the topNode to the top of the viewport in such a case since we've reached the scroll bottom. But then switching back to the small viewport now uses a new topNode that got into the viewport during the switch.
Although this should be expected from the behaviour being implemented, it is still a weird side-effect on scroll bottom.
I will also be looking into this in due course. Initially, I am thinking of simply adding a check for scroll bottom before we update topNode. I.e. to keep the old topNode when we've reached scroll bottom until we've scrolled up again. Not sure how this will turn out yet. I'll make sure to see how Opera handle this as well.
Here's what I've come up with:
(function(){
var topNode;
window.onscroll=function() {
var timer;
(function(){
clearTimeout(timer);
timer= setTimeout(
function() {
var testNode;
topNode= null;
for(var x = 0 ; x < document.body.offsetWidth ; x++) {
testNode= document.elementFromPoint(x,2);
if(!topNode || testNode.offsetTop>topNode.offsetTop) {
topNode = testNode;
}
}
},
100
)
}
)();
}
window.onresize=function() {
var timer;
(function(){
clearTimeout(timer);
if(topNode) {
timer= setTimeout(function(){topNode.scrollIntoView(true)},10);
}
}
)();
}
}
)();
If there were a window.onbeforeresize() function, this would be more straightforward.
Note that this doesn't take into account the scrolled position of the element's textNode. We could handle that if only the height of the window were resized. But resizing the width would generally cause reformatting.
This works in Chrome, Firefox, IE, and Safari.
Edit
How it works
The code's closures make variables private, and the timers prevent the code from running constantly during scrolling/resizing. But both tend to obfuscate the code, so here's another version, which may aid in understanding. Note that the onscroll timer is required in IE, because elementFromPoint returns null when it used in onscroll event.
var topNode;
window.onscroll=function() {
setTimeout(
function() {
var testNode;
topNode= null;
for(var x = 0 ; x < document.body.offsetWidth ; x++) {
testNode= document.elementFromPoint(x,2);
if(!topNode || testNode.offsetTop>topNode.offsetTop) {
topNode = testNode;
}
}
},
100
)
}
window.onresize=function() {
if(topNode) {
topNode.scrollIntoView(true)
}
}
topNode maintains the screen's top-most element as the window scrolls.
The function scans the screen left to right, along the 3rd row: document.elementFromPoint(x,2)*
It doesn't scan along the 1st row, because when IE does scrollIntoView, it pushes the element down a couple pixels, making the top-most screen element the previous element. (Figured this out through trial and error.)
When the window is resized, it simply positions topNode at the top of the screen.
[*Originally, onscroll scanned left to right along the 11th row (in pixels) until it found an element with just one child. The child would often be a textNode, but that wouldn't always be the case. Example:
<div><ul><li>...<li>...<li>...</ul></div>
The div has only one child – the ul. If the window were scrolled to the 50th li, scanning left to right would incorrectly return the div due to the inherent padding of lis.
The original code has been updated.
]
I've been struggling with this for a while, and I'm surprised that doing this isn't more straightforward...
I need to detect when the user scrolls a page, either with the mouse, scrollbar or by touch on mobile devices. jQuery has their scroll() function which works alright, but it requires that the page is actually scrolling. I want to detect the scrolling wether the page is scrolling or not (say I reach the end of the page, and there is nowhere left to scroll too.. I still want to know if the user is trying to scroll)
I found another question that had asked something similar, along the lines of detecting scroll input even when the page isn't scrolling, and I got this chunk of code:
if (document.addEventListener) {
document.addEventListener("mousewheel", MouseWheelHandler(), false);
document.addEventListener("DOMMouseScroll", MouseWheelHandler(), false);
} else {
sq.attachEvent("onmousewheel", MouseWheelHandler());
}
function MouseWheelHandler() {
return function (e) {
var e = window.event || e;
var delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)));
if (delta < 0) {
// increase scroll amount
} else {
// decrease scroll amount
}
}
return false;
}
At first, this seemed to do the trick, but I'm finding it doesn't really return balanced results with different types of mice, and didn't work too smoothly with touch events, which is the core aspect of this question.
I'm using this in a project that does a lot of fancy events on scroll, with the actual page not actually scrolling at all... But I'm running into the problem of it being incredibly slow with all my standard mice, but incredibly fast on my Apple Magic Mouse. I know that there will naturally be some difference here, as the magic mice do scroll quicker, but the difference is far more off balance than it is between the mice normally.
I'm hoping there is a way to improve upon this to get a more reliable result, with all sorts of different inputs. Any suggestions?
Edit:
To clarify, in order for an answer to work for me, it needs to work on an element which is not scrollable. I have a page which does not scroll at all, but which has other events that fire when the user scrolls. This means that I cannot use properties that are based on the window's scroll position (such as scrollTop()).
You should use window.onscroll most usage and then create a new listener to deal specifically with top and bottom scroll conditions I would suggest using a mousewheel event for desktop browsers and a specifically coded touch responder like below to detect if they are trying to scroll, what direction and if that is possible at the current window.scrollY value.
var isOverScroll = function isOverScroll ( touchStartY, touchEndY ) {
if ( Math.abs( touchStartY - touchEndY ) < 5 ) &&
( ( window.scrollY = window.innerHeight && touchStartY - touchEndY > 0 ) ||
( window.scrollY = 0 && touchStartY - touchEndY < 0 ) ) ) {
return false;
}
return true;
}
There is no way to detect scrollbar events, combine this with your current code and only trigger mouse wheel and touch events if the scrollY position is at either 0 or max.
On a side note if you are trying to get rid of the scroll bar completely that is a very bad idea as it is both a wonderful tool for users as well as something that is a standard part of the ui. If you trying to do a scrollable fullpage app and don't want a scroll bar try using slides. Either way don't continue setting the scroll value thats slow instead just move the whole body using css:
transition: transform3d( 0, YOURSCROLLVALUE , 0);
One possible solution is using a plugin for scrolling like
ISCROLL
in this example here :
Example
they use the feature pull to refresh , which will fire upon reaching the maximum scroll available , here by you can use any custom function (even if your item is not scrollable ).
I have a small div box that has a vertical scroll bar and sits within an html page that also has a vertical scroll bar.
My problem is when the user reaches the end of the small DIV box scrolling, the ENTIRE html page that contains the div box then begins to scroll (assuming the user is scrolling via the mouse scroll and NOT by actually clicking the DIV box scroll buttons themselves)
is there a way to prevent the entire html page from scrolling once a user reaches in end of my small DIV box scroll? Any help would be much appreciated! Thank you!
I have tried this (but it cancels scrolling for even the div box):
if (window.addEventListener)
/** DOMMouseScroll is for mozilla. */
window.addEventListener('DOMMouseScroll', handleWheelEvent, false);
/** IE/Opera. */
window.onmousewheel = document.onmousewheel = handleWheelEvent;
function handleWheelEvent(e){
e.preventDefault();
}
I didn't look too much into your code and the problem, but I wanted to throw out a suggestion before I move on :P.
window.addEventListener
and
document.onmousewheel = handleWheelEvent;
are normally good ways to apply what you want to do the ENTIRE document, whereas if you want to apply a specific value (in this case scroll = false) to a specific element, then you need to set the reference to that specific reference (i.e. getElementById() and then it applies only to the element of the document).
Idk - maybe that helps, maybe it doesn't :P good luck.
-J
You would need to modify the handleWheelEvent function and check the srcElement property of the e event and call preventDefault() when it's not scrolling the DIV box. Here's a link with some code examples:
http://www.webdeveloper.com/forum/archive/index.php/t-158824.html
I had a similar problem. Google led me here. Over 1700 views, in 4 years, of an incomplete answer. I figured once I had coded a solution, I'd pop it in a JSFiddle and share it. Better late than never.
Tested on MacOSX / Chrome.
http://jsfiddle.net/mF8Pr/
My problem involved being able to scroll inside a textarea, within a lightbox, and disabling scrolling on the rest of the page beneath the overlay.
bind mouse wheel event to document
when event fires (optional: test to make sure overlay is visible)
check target is obj we want to have scrolling enabled
make sure 0 < obj.scrollTop < (obj.scrollHeight - obj.clientHeight)
check direction of attempted scroll event.originalEvent.deltaY
UP == negative
DOWN == positive
event.preventDefault()
$(document).bind('mousewheel', function(e){
//if($overlay.is(':visible'))
{
if(e.target != null && e.target.type != 'textarea')
{
e.preventDefault();
}
else
{
if(e.originalEvent.deltaY < 0 && e.target.scrollTop == 0)
{
e.preventDefault(); // already at top
}
else if(e.originalEvent.deltaY > 0 && e.target.scrollTop >=
(e.target.scrollHeight - e.target.clientHeight))
{
// must use greater than because sometimes
// the math is wrong by 1px
e.preventDefault(); // already at bottom
}
}
}
});
-Amanda