Disclaimer: I am not a JavaScript developer, I'm a web designer. HTML and CSS, I handle all day, JS, not so much. That's why I'm reaching out for help.
The following script allows for a smooth scroll to the top of the page:
function scrollToTop() {
var position =
document.body.scrollTop || document.documentElement.scrollTop;
if (position) {
window.scrollBy(0, -Math.max(1, Math.floor(position / 10)));
scrollAnimation = setTimeout("scrollToTop()", 30);
} else clearTimeout(scrollAnimation);
}
Is there a way to "stop" the script from executing if the user decides to scroll back down the moment the script is running and taking the user back to the top of the page?
Here's a demo for reference: https://codepen.io/ricardozea/pen/ewBzyO
Thank you.
To specifically detect scrolling back down the page, you could check the old postion against the current position and ensure the scroll is moving in the intended direction:
function scrollToTop(prevPosition) {
// first time round, prevPosition is undefined
var position =
document.body.scrollTop || document.documentElement.scrollTop;
// did page move in non-expected direction? If so, bail-out
if (prevPosition <= position) {
return;
}
var scrollAnimation; //declare this so it doesn't leak onto global scope
if (position) {
var scrollAmt = -Math.max(1, Math.floor(position / 10));
window.scrollBy(0, scrollAmt);
// After timeout, re-call the function with current position.
// Becomes prevPosition for the next time round
scrollAnimation = setTimeout(() => scrollToTop(position), 30);
} else clearTimeout(scrollAnimation);
}
See https://codepen.io/spender/pen/eYvRyox
Why not listen to wheel events? This won't detect dragging the scrollbar with the mouse.
Thanks a lot for your answers!
After consulting with a friend of mine, he provided me with a much succinct way to accomplish the overall behavior of smooth scrolling to the top while solving the potential case of a user wanting to scroll back down during the animation.
Just add this script to a <button> element in the onclick: attribute:
window.scrollTo({top: 0, behavior: "smooth"});
It looks like this:
<button onclick='window.scrollTo({top: 0, behavior: "smooth"});'>Back to Top ↑</button>
Here's a new demo: https://codepen.io/ricardozea/pen/NWpgyjL
Related
I'm working on an application that renders articles, books and long texts.
The application has 2 buttons, one "start" and "stop"
"Start" makes the page scroll slowly, when you press it again, the button icon changes to a faster scroll and so 3 steps so that the user can read the text without using the scroll.
When the user clicks on the "stop" button or the page reaches the end, the scrolling stops.
I understand how to change the buttons from the state of the current scroll, but how to make the page scroll and stop it when the button is pressed does not fit in my head.
Please help with this problem. Making the screen move uncontrollably to the desired section is not as difficult as what I described above.
Thank you.
P.S. If the answer is adopted for typescript, I will be even more grateful.
In order for the page to stop scrolling at the bottom, you need to add the following function.
This task will be completed.
I hope it will be useful for someone.
window.onscroll = () => {
if ((window.innerHeight + window.pageYOffset) >= document.body.offsetHeight) {
clearInterval(myInterval);
}
};
You can use the window.scrollBy() method to scroll the page
I just share sample code which will help for your work.. I'm not sure how to stop the scroll when the scroll reaches to end
var myInterval = setInterval(()=>onBtnClick('stop'), 1000000);
const scroll = (speed) => {
window.scrollBy({
top: speed,
left: 0,
behavior: 'smooth'
})
}
const onBtnClick = (type) => {
switch(type) {
case 'start1':
myInterval = setInterval(()=>scroll(10), 100)
break
case 'start2':
myInterval = setInterval(()=>scroll(20), 100)
break
case 'stop':
clearInterval(myInterval)
break
case 'reverse':
myInterval = setInterval(()=>scroll(-10), 100)
break
}
}
onBtnClick("start1") // slow scroll
onBtnClick("start2") // fast scroll
onBtnClick("stop") // stop
I'm trying to smoothly scroll a page using setInterval() and window.scrollBy()
I would use jQuery's animate function, but the animation needs to be continuous and loop infinitely (the page contents will be infinite).
The idea is fairly simple:
var x = 1;
var y = 1;
setInterval(function() {
window.scrollBy(0, x);
}, y);
How can I increase the scroll speed without making the animation appear jumpy?
I'm experience two problems:
setInterval() can't take a Y value less than 1 (or probably closer to 30, depending on browser limits)
Increasing the value of X causes the animation to be jumpy (due to pixels being skipped altogether)
Here's a fiddle to experiment with:
http://jsfiddle.net/eoojrqh6/2/
Thanks!
Use behavior option instead of setInterval its more simple and it's the right way to do this,
var x = 1;
var y = 1;
window.scrollBy({
top: x,
left: y,
behavior : "smooth"
})
Rather than window.scrollBy you can use window.scroll.
http://jsfiddle.net/peterdotjs/f7yzLzyx/1/
var x = 1; //y-axis pixel displacement
var y = 1; //delay in milliseconds
setInterval(function() {
window.scroll(0, x);
x = x + 5; //if you want to increase speed simply increase increment interval
}, y);
As you currently have y value set very low, you can adjust the values of y and the incremental value of x to find the desired scroll speed.
You can also scroll other elements:
document.querySelector('.el-class').scrollBy({
top: 100,
behavior: 'smooth',
});
An alternative method to just auto-scrolling on any page with a click, I created a bookmark that I placed on my bookmark toolbar so it's always visible and accessible with a single click of the mouse. Then I just edited it's properties with this code that simply auto-scrolls the page when clicked, if the page isn't auto-scrolling. If it is already auto-scrolling, then it simply stops it so you can turn on auto-scrolling and turn off auto-scrolling by just clicking on it. Here is the code I saved in the bookmark as the location, just copy the following code and then open up any browser, either right click on any existing bookmarks on your toolbar and goto properties, then paste the code or create new bookmark and paste the code as the Location if using Firefox
javascript:var%20isScrolling;%20var%20scrolldelay;%20function%20pageScroll()%20{%20window.scrollBy(2,1);%20scrolldelay%20=%20setTimeout('pageScroll()',.1);%20isScrolling%20=%20true;%20}%20if(isScrolling%20!=%20true)%20{%20pageScroll();%20}%20else%20{%20isScrolling%20=%20false;%20clearTimeout(scrolldelay);%20}
For Internet Explorer & Chrome, use this code that doesn't contain the %20 for spaces
javascript:var isScrolling; var scrolldelay; function pageScroll() { window.scrollBy(0,1); scrolldelay = setTimeout('pageScroll()',15); isScrolling = true; } if(isScrolling != true) { pageScroll(); } else { isScrolling = false; clearTimeout(scrolldelay); }
Just edit the values to change the speed according to your own preference for what looks best on your computer since the effect won't be the same unanymously for every display
Solutuon for 2020
you can use the scrollTo function which has the behavior property of smooth, so your code be as follows.
//Excluding event listener functions.
//one liner solutions for scrolling to the bottom and top of a document.
const footer = document.body.scrollHeight;
const scrollDown = (footer)=>{
return window.scrollTo({top: footer, behavior: 'smooth'});
}
Scroll to the top by simply reducing the top properties value to zero.
const nav = 0;
const scrollUp = ()=>{
return window.scrollTo({top: nav, behavior: 'smooth'});
}
I try to make a mousewheel event script, but getting some issues since I'm using an Apple Magic Mouse and its continue-on-scroll function.
I want to do this http://jsfiddle.net/Sg8JQ/ (from jQuery Tools Scrollable with Mousewheel - scroll ONE position and stop, using http://brandonaaron.net/code/mousewheel/demos), but I want a short animation (like 250ms) when scrolling to boxes, AND ability to go throught multiple boxes when scrolling multiple times during one animation. (If I scroll, animation start scrolling to second box, but if I scroll again, I want to go to the third one, and if I scroll two times, to the forth, etc.)
I first thought stopPropagation / preventDefault / return false; could "stop" the mousewheel velocity (and the var delta) – so I can count the number of new scroll events (maybe with a timer) –, but none of them does.
Ideas?
EDIT : If you try to scroll in Google Calendars with these mouses, several calendars are switched, not only one. It seems they can't fix that neither.
EDIT 2 : I thought unbind mousewheel and bind it again after could stop the mousewheel listener (and don't listen to the end of inertia). It did not.
EDIT 3 : tried to work out with Dates (thanks to this post), not optimal but better than nothing http://jsfiddle.net/eZ6KE/
Best way is to use a timeout and check inside the listener if the timeout is still active:
var timeout = null;
var speed = 100; //ms
var canScroll = true;
$(element).on('DOMMouseScroll mousewheel wheel', function(event) {
// Timeout active? do nothing
if (timeout !== null) {
event.preventDefault();
return false;
}
// Get scroll delta, check for the different kind of event indexes regarding delta/scrolls
var delta = event.originalEvent.detail ? event.originalEvent.detail * (-120) : (
event.originalEvent.wheelDelta ? event.originalEvent.wheelDelta : (
event.originalEvent.deltaY ? (event.originalEvent.deltaY * 1) * (-120) : 0
));
// Get direction
var scrollDown = delta < 0;
// This is where you do something with scrolling and reset the timeout
// If the container can be scrolling, be sure to prevent the default mouse action
// otherwise the parent container can scroll too
if (canScroll) {
timeout = setTimeout(function(){timeout = null;}, speed);
event.preventDefault();
return false;
}
// Container couldn't scroll, so let the parent scroll
return true;
});
You can apply this to any scrollable element and in my case, I used the jQuery tools scrollable library but ended up heavily customizing it to improve browser support as well as adding in custom functionality specific to my use case.
One thing you want to be careful of is ensuring that the timeout is sufficiently long enough to prevent multiple events from triggering seamlessly. My solution is effective only if you want to control the scrolling speed of elements and how many should be scrolled at once. If you add console.log(event) to the top of the listener function and scroll using a continuous scrolling peripheral, you will see many mousewheel events being triggered.
Annoyingly the Firefox scroll DOMMouseScroll does not trigger on magic mouse or continuous scroll devices, but for normal scroll devices that have a scroll and stop through the clicking cycle of the mouse wheel.
I had a similar problem on my website and after many failed attempts, I wrote a function, which calculated total offset of selected box and started the animation over. It looked like this:
function getOffset() {
var offset = 0;
$("#bio-content").children(".active").prevAll().each(function (i) {
offset += $(this)[0].scrollHeight;
});
offset += $("#bio-content").children(".active")[0].scrollHeight;
return offset;
}
var offset = getOffset();
$('#bio-content').stop().animate( {
scrollTop: offset
}, animationTime);
I hope it gives you an idea of how to achieve what you want.
you can try detecting when wheel stops moving, but it would add a delay to your response time
$(document).mousewheel(function() {
clearTimeout($.data(this, 'timer'));
$.data(this, 'timer', setTimeout(function() {
alert("Haven't scrolled in 250ms!");
//do something
}, 250));
});
source:
jquery mousewheel: detecting when the wheel stops?
or implement flags avoiding the start of a new animation
var isAnimating=false;
$(document).bind("mousewheel DOMMouseScroll MozMousePixelScroll", function(event, delta) {
event.preventDefault();
if (isAnimating) return;
navigateTo(destination);
});
function navigateTo(destination){
isAnimating = true;
$('html,body').stop().animate({scrollTop: destination},{complete:function(){isAnimating=false;}});
}
I am using the excellent jQuery Reel plugin (http://jquery.vostrel.cz/reel) for a project. I would like to bind to the window scroll event, so when the user scrolls down the page the plugin advances 1 frame for say every 10px scrolled, if the user scrolls up the animation is reversed.
The plugin has methods I can pass the values to no problem and I know how to bind to the window scroll event. What I am struggling with is the last.
How can I use jQuery/JavaScript to say for every 10 pixels scrolled in any vertical direction advance 1 frame in the animation? I know I can store the window scroll in a variable but I'm unsure how to say every time it hits a multiple of 10 advance one frame.
Many thanks in advance.
EDIT
Thanks to help of the users below I worked out a solution. As follows:
$(window).scroll(function()
{
windowScrollCount = $(this).scrollTop();
animationFrame = Math.round(windowScrollCount / 100);
});
So here I am getting the scrolled distance in windowScrollCount, translating it into frames in animationFrame and setting it back with .reel("frame", animationFrame); I am actually doing this for every 100 frames as every 10 was to quick.
Thanks to help of codef0rmer and noShowP I worked out a solution. As follows:
$(window).scroll(function()
{
windowScrollCount = $(this).scrollTop();
animationFrame = Math.round(windowScrollCount / 100);
});
So here I am getting the scrolled distance in windowScrollCount, translating it into frames in animationFrame and setting it back with .reel("frame", animationFrame); I am actually doing this for every 100 frames as every 10 was to quick.
If I'm wrong then you might want this:
var jump = 500; // consider this is your 10px
window.scrollHeight = 0;
$(window).scroll(function () {
console.log($(this).scrollTop());
var diff = $(this).scrollTop() - window.scrollHeight;
if (diff >= jump) {
window.scrollHeight = $(this).scrollTop();
console.log('reload frame');
}
});
Demo : http://jsfiddle.net/Dyd6h/
You could possible have a sticky element to the top of your page,
position: fixed; top 0; left: 0;
(hidden if you like).
And then when you are scrolling you can monitor its offset:
$('element').offset().top
You can then see how far down the page you have scrolled, so every time they scroll see what its top value is and trigger events appropiately?
EDIT:
I've set up a little JSfiddle with a start of what I think you need.
http://jsfiddle.net/qJhRz/3/
Im just calculating the frame you need to be on and storing that in a variable. Is it anything like what you're looking for?
Simple, I just would like to have it so when a user is dragging an item and they reach the very bottom or top of the viewport (10px or so), the page (about 3000px long) gently scrolls down or up, until they move their cursor (and thus the item being dragged) out of the region.
An item is an li tag which uses jquery to make the list items draggable. To be specific:
../jquery-ui-1.8.14.custom.min.js
http://code.jquery.com/jquery-1.6.2.min.js
I currently use window.scrollBy(x=0,y=3) to scroll the page and have the variables of:
e.pageY ... provides absolute Y-coordinates of cursor on page (not relative to screen)
$.scrollTop() ... provides offset from top of page (when scroll bar is all the way up, it is 0)
$.height()... provides the height of viewable area in the user's browser/viewport
body.offsetHeight ... height of the entire page
How can I achieve this and which event best accommodates this (currently its in mouseover)?
My ideas:
use a an if/else to check if it is in top region or bottom, scroll up if e.pageY is showing it is in the top, down if e.page& is in bottom, and then calling the $('li').mouseover() event to iterate through...
Use a do while loop... this has worked moderately well actually, but is hard to stop from scrolling to far. But I am not sure how to control the iterations....
My latest attempt:
('li').mouseover(function(e) {
totalHeight = document.body.offsetHeight;
cursor.y = e.pageY;
var papaWindow = window;
var $pxFromTop = $(papaWindow).scrollTop();
var $userScreenHeight = $(papaWindow).height();
var iterate = 0;
do {
papaWindow.scrollBy(0, 2);
iterate++;
console.log(cursor.y, $pxFromTop, $userScreenHeight);
}
while (iterate < 20);
});
Works pretty well now, user just needs to "jiggle" the mouse when dragging items sometimes to keep scrolling, but for scrolling just with mouse position its pretty solid. Here is what I finally ended up using:
$("li").mouseover(function(e) {
e = e || window.event; var cursor = { y: 0 }; cursor.y = e.pageY; //Cursor YPos
var papaWindow = parent.window;
var $pxFromTop = $(papaWindow).scrollTop();
var $userScreenHeight = $(papaWindow).height();
if (cursor.y > (($userScreenHeight + $pxFromTop) / 1.25)) {
if ($pxFromTop < ($userScreenHeight * 3.2)) {
papaWindow.scrollBy(0, ($userScreenHeight / 30));
}
}
else if (cursor.y < (($userScreenHeight + $pxFromTop) * .75)) {
papaWindow.scrollBy(0, -($userScreenHeight / 30));
}
}); //End mouseover()
This won't work as the event only fires while you're mouse is over the li.
('li').mouseover(function(e) { });
You need to be able to tell the position of the mouse relative to the viewport when an item is being dragged. When the users starts to drag an item attach an 'mousemove' event to the body and then in that check the mouse position and scroll when necessary.
$("body").on("mousemove", function(event) {
// Check mouse position - scroll if near bottom or top
});
Dont forget to remove your event when the user stops dragging.
$("body").off("mousemove", function(event) {
// Check mouse position - scroll if near bottom or top
});
This may not be exactly what you want, but it might help. It will auto-scroll when the mouse is over the 'border of the screen' (a user defined region). Say you have a 40px wide bar on the right of the screen, if the mouse reaches the first 1px, it will start scrolling. Each px you move into it, the speed will increase. It even has a nice easing animation.
http://www.smoothdivscroll.com/v1-2.htm
I get a weekly newsletter (email) from CodeProject, and it had some stuff that certainly looks like it will solve my problem... hopefully this can help others:
http://johnpolacek.github.com/scrollorama/ -- JQuery based and animates the scroll
https://github.com/IanLunn/jQuery-Parallax -- JQuery based, similar to above
http:// remysharp. com/2009/01/26/element-in-view-event-plugin/ -- JQuery, detects whether an element is currently in view of the user (super helpful for this issue!)
Also the site in #2 had some interesting code:
var windowHeight = $window.height();
var navHeight = $('#nav').height() / 2;
var windowCenter = (windowHeight / 2);
var newtop = windowCenter - navHeight;
//ToDo: Find a way to use these vars and my original ones to determine scroll regions