Creating a simple "smooth scroll" (with javascript vanilla) - javascript

I have been trying to make a simple "smoothscroll" function using location.href that triggers on the mousewheel. The main problem is that the EventListener(wheel..) gets a bunch of inputs over the span of ca. 0,9 seconds which keeps triggering the function. "I only want the function to run once".
In the code below I have tried to remove the eventlistener as soon as the function runs, which actually kinda work, the problem is that I want it to be added again, hence the timed function at the bottom. This also kinda work but I dont want to wait a full second to be able to scroll and if I set it to anything lover the function will run multiple times.
I've also tried doing it with conditions "the commented out true or false variables" which works perfectly aslong as you are only scrolling up and down but you cant scroll twice or down twice.
window.addEventListener('wheel', scrolltest, true);
function scrolltest(event) {
window.removeEventListener('wheel', scrolltest, true);
i = event.deltaY;
console.log(i);
if (webstate == 0) {
if (i < 0 && !upexecuted) {
// upexecuted = true;
location.href = "#forside";
// downexecuted = false;
} else if (i > 0 && !downexecuted) {
// downexecuted = true;
location.href = "#underside";
// upexecuted = false;
}
}
setTimeout(function(){ window.addEventListener('wheel', scrolltest, true); }, 1000);
}
I had hoped there was a way to stop the wheel from constantly produce inputs over atleast 0.9 seconds.
"note: don't know if it can help in some way but when the browser is not clicked (the active window) the wheel will registre only one value a nice 100 for down and -100 for up"

What you're trying to do is called "debouncing" or "throttling". (Those aren't exactly the same thing, but you can look up the difference in case it's going to matter to you.) Functions for this are built into libraries like lodash, but if using a library like that is too non-vanilla for what you have in mind, you can always define your own debounce function: https://www.geeksforgeeks.org/debouncing-in-javascript/
You might also want to look into requestanimationframe.

a different approach
okey after fiddeling with this for just about 2 days i got fustrated and started over. no matter what i did the browsers integrated "glide-scroll" was messing up the event trigger. anyway i decided to animate the scrolling myself and honestly it works better than i had imagined: here is my code if anyone want to do this:
var body = document.getElementsByTagName("BODY")[0];
var p1 = document.getElementById('page1');
var p2 = document.getElementById('page2');
var p3 = document.getElementById('page3');
var p4 = document.getElementById('page4');
var p5 = document.getElementById('page5');
var whatpage = 1;
var snap = 50;
var i = 0;
// this part is really just to read what "page" you are on if you update the site. if you add more pages you should remember to add it here too.
window.onload = setcurrentpage;
function setcurrentpage() {
if (window.pageYOffset == p1.offsetTop) {
whatpage = 1;
} else if (window.pageYOffset == p2.offsetTop) {
whatpage = 2;
} else if (window.pageYOffset == p3.offsetTop) {
whatpage = 3;
} else if (window.pageYOffset == p4.offsetTop) {
whatpage = 4;
} else if (window.pageYOffset == p5.offsetTop) {
whatpage = 5;
}
}
// this code is designet to automaticly work with any "id" you have aslong as you give it a variable called p"number" fx p10 as seen above.
function smoothscroll() {
var whatpagenext = whatpage+1;
var whatpageprev = whatpage-1;
var currentpage = window['p'+whatpage];
var nextpage = window['p'+whatpagenext];
var prevpage = window['p'+whatpageprev];
console.log(currentpage);
if (window.pageYOffset > currentpage.offsetTop + snap && window.pageYOffset < nextpage.offsetTop - snap){
body.style.overflowY = "hidden";
i++
window.scrollTo(0, window.pageYOffset+i);
if (window.pageYOffset <= nextpage.offsetTop + snap && window.pageYOffset >= nextpage.offsetTop - snap) {
i=0;
window.scrollTo(0, nextpage.offsetTop);
whatpage += 1;
body.style.overflowY = "initial";
}
} else if (window.pageYOffset < currentpage.offsetTop - snap && window.pageYOffset > prevpage.offsetTop + snap){
body.style.overflowY = "hidden";
i--
window.scrollTo(0, window.pageYOffset+i);
if (window.pageYOffset >= prevpage.offsetTop - snap && window.pageYOffset <= prevpage.offsetTop + snap) {
i=0;
window.scrollTo(0, prevpage.offsetTop);
whatpage -= 1;
body.style.overflowY = "initial";
}
}
}
to remove the scrollbar completely just add this to your stylesheet:
::-webkit-scrollbar {
width: 0px;
background: transparent;
}

Related

Pause at top and bottom continuous vertical scroll HTML

I have an embedded HTML page containing a single table of event results. I have found the code below which perfectly gives a continuous scroll, however I which to be able to pause for say 10 seconds at both the top and bottom before continuing.
How do I intercept the top and bottom and insert the pause?
Any suggestions would be much appreciated.
/*
Advanced window scroller script-
By JavaScript Kit (www.javascriptkit.com)
Over 200+ free JavaScripts here!
*/
var currentpos = 0, alt = 1, curpos1 = 0, curpos2 = -1
function initialize() {
startit()
}
function scrollwindow() {
if (document.all)
temp = document.body.scrollTop
else
temp = window.pageYOffset
if (alt == 0)
alt = 1
else
alt = 0
if (alt == 0)
curpos1 = temp
else
curpos2 = temp
if (curpos1 != curpos2) {
if (document.all)
currentpos = document.body.scrollTop + 300
else
currentpos = window.pageYOffset + 1
window.scroll(0, currentpos)
}
else {
currentpos = 0
window.scroll(0, currentpos)
}
}
function startit() {
setInterval("scrollwindow()", 25)
}
window.onload = initialize
Since setInterval is a basic javascript function, which runs a function every n-seconds, you set a global variable to capture that "interval", then cancel it when the user performs some action (click on a button). Then resubmit the setInterval to start running in the future, or tie it back to another onClick event. An example would be to have a button that will toggle the setInterval on and off, which will act as a pause/run.
Below is an example of how you could set this up. I have not tested it, so there may be some use cases that could cause issues.
var myInterval = null;
function startit() {
myInterval = setInterval("scrollwindow()", 25)
}
Then to cancel the interval:
function stopInterval() {
clearInterval(myInterval);
myInterval = null;
}
Example of how to toggle the interval on and off:
function toggleInterval() {
if ( myInterval ) {
stopInterval();
}
else {
startit();
}
}
Then just add it to any html element, or a button. This example is assuming the css is locked at the top of the scrollable pane so the user could always have access to click it.
<span onClick="toggleInterval()">Click to pause/continue scrolling.</span>
<button onClick="toggleInterval()">Pause/Scroll</button>

How to remove jank when setting an element to a fixed position using JavaScript

I have a webpage that when scrolled down, the text freezes when it reaches the last paragraph of text but the images keep on scrolling. I've got the implementation working but there is a lot of jank when scrolling with a mouse wheel, not so much if I click and drag the scroll bar.
Are there any optimizations I can make to this code to make work as intended or is there a different way to accomplish the same task?
window.addEventListener('scroll', function (e) {
window.requestAnimationFrame(keepTextStationary);
//keepTextStationary(); // Less janky, but still horrible
});
function keepTextStationary() {
var textRect = writtenContent.getBoundingClientRect();
var imageRec = images.getBoundingClientRect();
if (textRect.bottom < window.innerHeight && document.documentElement.scrollTop > 0) {
writtenContent.style.position = 'relative';
writtenContent.style.bottom = (225 - document.documentElement.scrollTop) + 'px';
if (imagesTop === undefined) {
imagesTop = imageRec.y;
}
} else {
writtenContent.style.bottom = (225 - document.documentElement.scrollTop) + 'px';
}
if (imageRec.y >= imagesTop) {
writtenContent.style.position = '';
}
}
Here is the site so you can see the problem.
https://bowerbankninow.azurewebsites.net/exhibitions/oscar-perry-the-pheasant
You are causing layout trashing every time you call getBoundingClientRect. Try debouncing your scroll events:
var lastScrollY = 0;
var ticking = false;
function keepTextStationary() {
var textRect = writtenContent.getBoundingClientRect();
var imageRec = images.getBoundingClientRect();
if (textRect.bottom < window.innerHeight && lastScrollY > 0) {
writtenContent.style.position = 'relative';
writtenContent.style.bottom = (225 - lastScrollY) + 'px';
if (imagesTop === undefined) {
imagesTop = imageRec.y;
}
} else {
writtenContent.style.bottom = (225 - lastScrollY) + 'px';
}
if (imageRec.y >= imagesTop) {
writtenContent.style.position = '';
}
ticking = false;
}
function onScroll() {
lastScrollY = document.documentElement.scrollTop;
requestTick();
}
function requestTick() {
if (!ticking) {
requestAnimationFrame(keepTextStationary);
ticking = true;
}
}
window.addEventListener('scroll', onScroll );
See this article for in-depth explanation: https://www.html5rocks.com/en/tutorials/speed/animations/
You dont.
Relocations / styling in javascript take place after the CSS has been loaded. Bad practise. What you can do, is make it animated to make it look less horrible.
Why is pure CSS not an option ?

how to fix loop "go to bottom" when i scroll top

After you click Go To Bottom then roll the mouse over it just automatically roll down it? way still uses the setInterval without fail no summer?
link source code
http://jsfiddle.net/trananh/v7gdr4n0/
var process;
var delay = 50;
var scrollPixel = 20;
//Fix Undefine pageofset when using IE 8 below;
function getPapeYOfSet() {
var yOfSet = (typeof (window.pageYOffset) === "number") ? window.pageYOffset : document.documentElement.scrollTop;
return yOfSet;
}
function goToBottom() {
process = setInterval(function () {
var yOfSet = getPapeYOfSet();
if (yOfSet >= document.body.scrollHeight) {
clearInterval(process);
} else {
window.scrollBy(0, scrollPixel);
}
}, delay);
}
JAvascript function goToBottom

animate when window.location.hash changes

so i'm trying to get a site gallery-like, i have the articles that spreads all across the page and my idea is that when i get an action from the user the site jumps to the next article.
I have done a lot of work so far and it's a couple of days i'm behind javascript, i'm using jquery and the code is this
$('body').ready(function () {
var tpScroll = 0;
var SelArticles = $('#content').find('.bgjs');
var NumArticles = SelArticles.length;
window.location.hash = SelArticles.first().attr('id');
$(window).bind('mousewheel', function (event, delta, deltaX, deltaY) {
event.preventDefault();
console.log(delta, deltaX, deltaY);
console.log(tpScroll);
if (delta < 0) {
if (tpScroll >= (NumArticles - 1)) {
tpScroll = 0;
} else {
tpScroll = tpScroll + 1;
}
var pgid = SelArticles.eq(tpScroll).attr('id');
window.location.hash = pgid;
} else {
if (tpScroll <= 0) {
tpScroll = (NumArticles - 1);
} else {
tpScroll = tpScroll - 1;
}
var pgid = SelArticles.eq(tpScroll).attr('id');
window.location.hash = pgid;
}
});
});
i managed to handle the mousewheel event to make the hash change, but i want to prevent the default scroll to the content adding a smooth animation.
I'm not a monster in javascript (neither jquery) as you can see , but it's kind of working, i don't even know if it's possible to prevend this behavior, or to work around it... any suggestion?
I just completely changed approach, i used wiselinks to handle history apis, turned out to be a great choice

Javascript "For" Loop not working?

I have a Javascript file that I am using to try to animate a dropdown menu. I have the "Toggle" function in that file set to run when I click on a certain div. Here's the script I'm using:
var up = true;
function Toggle(x)
{
if (up)
{
for (var i = x.offsetTop; i <= 0; i++)
{
x.style.top = i;
if (i == 0)
{
up = false;
}
}
}
else if (up == false)
{
for (var i = x.offsetTop; i >= -50; i--)
{
x.style.top = i;
if (i == -50)
{
up = true;
}
}
}
}
In the HTML div I want to animate, I have the "onclick" property set to "onclick=Toggle(this)". The first for loop works as it should (it sets the div's top offset to 0). However, the second for loop doesn't set the offsetTop. I know that the for loop is activating because I've tested it and it gives me every integer between 0 and -50. Why isn't it setting the offset position?
1) You must specify a unit to the top ie: x.style.top = i +"px"
2) Your function won't animate instead of you use a setInterval or a setTimeout
Like you asked, an example. I wouldn't do it like this for one of my project, but i kept your function to make you more familiar with the code.
I Used setTimeout instead of setInterval because setInterval must be cleared when not needed and setTimeout is just launched one time :
var Toggle = (function() { // use scope to define up/down
var up = true;
return function(element) {
var top = parseInt(element.style.top, 10); // element.offsetTop ?
if ( !top ) {
top = 0;
}
if (up) {
if (element.offsetTop < 0) { // if we are not at 0 and want to get up
element.style.top = (top+1) + "px";
setTimeout(function() { Toggle(element); }, 10); // recall the function in 10 ms
} else { // we change up value
up = !up;
}
}
else {
if (element.offsetTop > -50) {
element.style.top = (top-1) + "px";
setTimeout(function() { Toggle(element); }, 10); // recall the function in 10 ms
} else {
up=!up;
}
}
}
})();
You'd have to use x.style.top = i + 'px' as top and similar css properties must define the type (px, em, %, etc.) unless they are 0, as this is 0 in any case.
But your script would actually snap the div directly to -50px, as you do not wait between those iteration steps.
I'd recommend to use a library like jQuery to use it's animate() method.
function Toggle(obj) {
$(obj).animate({
top: parseInt($(obj).css('top')) === 0 ? '-50px' : '0px'
})
}

Categories

Resources