Synchronize scrollbars between two elements - jQuery - javascript

How can you synchronize scrollbars between two elements without recursively calling each event?
Usually you would expect the following code:
$div1.scroll(function() {
$div2.scrollTop($div1.scrollTop());
});
$div2.scroll(function(){
$div1.scrollTop($div2.scrollTop());
});
But in this case, if you scroll $div1 1px, it will scroll $div2 1px as well, which will prompt the $div2 scroll event to fire and re-apply the scroll position to $div1. While this might not seem like an issue, when this code is applied to a page and you naturally scroll with a mouse, it scrolls in 1px increments because the handlers call eachother and don't allow the scroll to flow.
So, how would you solve this issue?
Example: https://jsfiddle.net/axtn/a91fsar3/2

Found out a good solution. Debouncing does the trick.
You can use a combination of timers and bools to make sure the element is being scrolled by the user. Thus, when the scroll event is rapidly and consecutively fired (like when a user scrolls down), it prevents the handlers from recursively calling eachother. The following code does the trick:
var userScroll1 = true;
var userScroll2 = true;
var timer;
$div1.scroll(function() {
if(userScroll2) {
userScroll1 = false;
clearTimeout(timer);
$div2.scrollTop($div1.scrollTop());
timer = setTimeout(function() {
userScroll1 = true;
}, 100);
}
});
$div2.scroll(function(){
if(userScroll1) {
userScroll2 = false;
clearTimeout(timer);
$div1.scrollTop($div2.scrollTop());
timer = setTimeout(function() {
userScroll2 = true;
}, 100);
}
});
Check out the properly functioning jsbin: https://jsfiddle.net/axtn/a91fsar3

Related

wait until scroll is done then execute

Hello I found a code that switches the content of your body when you click a button from this codepen and I would like to add a feature that it waits with switching the content until the page is done scrolling for cross browser support I use this script for my smooth scroll:
// add event listener on load
window.addEventListener('load', function() {
// scroll into view
var btns = document.querySelectorAll('.scrollwrap');
for (let i = 0; i < btns.length; i++) {
btns[i].addEventListener('click', function(e) {
e.preventDefault();
document.querySelector('.wrapper').scrollIntoView({
behavior: 'smooth'
});
document.getElementById("wrap").scrollTop -= 100;
});
}
I got this script from here.
How can I make it wait until the page is fully scrolled to the set destination?
I think you can try to catch Event
const wrap = document.getElementById("wrap");
wrap.addEventListener('scroll', function(e) {
const lastKnownScrollPosition = wrap.scrollTop;
Here you can do what you want to do =)
});
While the page scrolls, if the page has any loading icon or anything indicating new contents loading, you need to get the selector of that icon or whatever.
Then do something like this.
var checkExist = setInterval(function() {
if ($('#loadingIcon').length) {
console.log("Exists!");
clearInterval(checkExist);
}
}, 100); // check every 100ms
Refer to this for more information.
To get selector to your loading icon, you will have to pause the dom by pressing F8 and then inspecting the loading icon. Pausing the dom will prevent the icon from disappearing.

60fps: How to use requestAnimationFrame the right way?

On my website, a related content box should be animated into the viewport when it gets visible.
I’m trying to make my animation as efficient as possible through CSS and JavaScript, so that it doesn’t affects scroll performance negatively.
While the CSS part was simple (using transform, will-change, contain), I’m struggling a bit with when to use window.requestAnimationFrame.
Should I use it only when the class is added to the element or also when the function isScrolledIntoView is called or even inside isScrolledIntoView, when the elements position is measured?
var percentVisible = 0.25;
window.addEventListener('scroll', function(){
relatedContent(related, percentVisible);
}
)
function relatedContent(r, pV){
window.requestAnimationFrame(function() {
if(isScrolledIntoView(r, pV)){
window.requestAnimationFrame(function(){
r.classList.add("visible");
}, r)
}
}, r)
}
function isScrolledIntoView(el, percentV) {
var elemTop, elemBottom, elemHeight, overhang, isVisible;
/*window.requestAnimationFrame(
function(){*/
elemTop = el.getBoundingClientRect().top;
elemBottom = el.getBoundingClientRect().bottom;
elemHeight = el.getBoundingClientRect().height;
/*}
);*/
overhang = elemHeight * (1 - percentV);
isVisible = (elemTop >= -overhang) && (elemBottom <= window.innerHeight + overhang);
return isVisible;
}
requestAnimationFrame returns a non-zero long that can be used to cancel your request, so instead of writing your own throttle implementation, you can use the following simpler approach to prevent multiple handlers stacking up:
let currentRequest;
document.addEventListener('scroll', function () {
cancelAnimationFrame(currentRequest);
currentRequest = requestAnimationFrame(handleScroll);
});
No don't use it like that...
requestAnimationFrame (rAF) is a timing function that does synchronize with the screen refresh rate (generally 60fps).
Scroll event may fire more often than 60 events per second.
Each call to rAF will stack all the functions passed as its parameter in some kind of a big function called just before the next screen refresh.
Combine all of this and what you get is multiple calls to the same function in a stack, just before the next screen refresh.
Instead, what you seem to want is to prevent your scroll event to fire when not useful. This is called a throttle function, and you're a bit far from it.
Here is a simple throttle implementation using rAF :
var throttle = function(callback) {
var active = false; // a simple flag
var evt; // to keep track of the last event
var handler = function(){ // fired only when screen has refreshed
active = false; // release our flag
callback(evt);
}
return function handleEvent(e) { // the actual event handler
evt = e; // save our event at each call
if (!active) { // only if we weren't already doing it
active = true; // raise the flag
requestAnimationFrame(handler); // wait for next screen refresh
};
}
}
That you could use like this :
window.addEventListener('scroll', throttle(yourScrollCallback));

Only setTimeout executes function. JS

I am trying to implement kind of player on my website.
If press 'Play' button, the music starts and the page smoothly scrolls down.
But when you press 'Mute' button (function(){music.volume=0}) I am not sure why the page appears at the top again. window.scroll() doesn't do anything without delay. So i am using setTimeout function to scroll the page on the current place. The problem is that in Opera and IE setTimeout takes about 10 ms, so when i click 'Mute' button i see like ticks to top and back. In chrome it takes only 2 ms and there is no problems.
Now when i decide to create my own timeout function the window.scroll() does not work again.
Here is my code:
var isMuted = false;
muteButton.onclick = function() { ////This function works with big delay.
if (!isMuted) {
mainAudio.volume = 0;
isMuted = true;
} else {
mainAudio.volume = bgAudioTrackVolume;
isMuted = false;
}
setTimeout(function() {
window.scroll(0, offset); /// Works
}, 0)
};
Change setTimeout with:
i = 9542155.873; /// I have tried delay time from 1ms - 250ms by changing this value.
while (i > 0.00001) {
i = i / 1.0001234567;
if (i < 0.00001) {
window.scroll(0, offset); /// Does not do anything. Strange! Have tried to change variable with a number.
}
}
Every time i check offset value, it is always available before calling scroll function.
I know that my problem is not usual and i am realy need your help.
The reason that the page scrolls to the top is that you are using a link with the empty bookmark #, which represents the top of the page. The reason that the scroll method doesn't work without a timeout is that jumping to the bookmark happens after the event handler.
Instead of trying to scroll the page back to where it was, just stop the default action of the link by returning false from the event handler:
var isMuted = false;
muteButton.onclick = function() {
if (!isMuted) {
mainAudio.volume = 0;
isMuted = true;
} else {
mainAudio.volume = bgAudioTrackVolume;
isMuted = false;
}
return false;
};
Alternatively, use some other element than a link.

Differentiating between mouseup mousedown and click

I know that mousedown happens when a user depresses the mouse button, mouseup happens when the release the mouse and click is of course two events mousedown and mouseup. I have three different events each dealing with these three events mouseup down and click. My question is how to differentiate between the three, now my mouse down has a timer, so I was thinking of adding a boolean in that timer and testing it within the click I tried this and it didn't work to my standards.
Mousedown- timer checks for certain classes then if none of these classes exist within the targeted element proceed
Mouseup- clear the timer
Click- open a module
I may have not made the boolean a global variable that each can read or not, or I am missing something completely. Here is an example quick code of my full code:
var isDown = false;
ee[i].addEventListener('click',function(){
if(isDown===false){
openModule();
}
},false);
ee[i].addEventListener('mousedown',function(){
var timer;
var $this = this;
timer = setTimeout(function(){
if($this.className == "class"){
isDown=true;
createActive();
}
},500);
},true);
ee[i].addEventListener('mouseup',function(){
clearTimeout(timer);
},false);
That is just a quick example. I may have missed some coding but I hope you catch my drift in the code above. Anyone know of a good way to differentiate between the three events?
I've rewritten your code utilizing jQuery...
var isDown = false;
var timer;
$('.class').mousedown(function(){
isDown = false;
timer = setTimeout(function(){
isDown = true;
//createActive();
console.log('MOUSE DOWN');
}, 500);
}).mouseup(function(){
if(isDown === false){
//openModule();
console.log('CLICK');
}else{
console.log('MOUSE UP');
}
clearTimeout(timer);
});
If you simply add jQuery to your page, my code will automatically attach itself to any element in your document with a class of 'class'.
I've commented out your createActive(); and openModule(); calls so that you can play around with it (viewing your javascript console at runtime will show you the script in action - remove the console.log() stuff when you're done playing). This code could be optimised a bit more but it will give you the general idea.
Your timer variable needed to be created globally (I moved it out of the function).
In this case (declaring a mousedown time barrier) the click function will be rendered useless so I've improvised it into the mouseup function.
It's good to know core javascript, but jQuery is just too easy and powerful to ignore.
Try this:
const isDown = ref(false)
const timer = ref(0)
const mouseDown = () => {
isDown.value = true
timer.value = setTimeout(() => {
isDown.value = false
}, 120)
}
const mouseUp = () => {
if (isDown.value === true) {
hideModal()
} else {
return
}
clearTimeout(timer.value)
}

How to detect end of scrolling

i have some problems with my script. So, i want to detect end of my scrolling action. I have my alert when i'm scrolling but not if i'm ending it. Can you help me? This is my code:
var animatable = $('body, html');
var animating = false;
animatable.animate( {scrollTop: $('#foo').offset()})
$(window).scroll(function(e) {
if(!animating){
animatable.stop(false, true);
alert('stop scrolling');
}
animating = false;
});​
and some fiddle: http://jsfiddle.net/yhnKR/
is this what you're trying to achieve:
$('body').animate( {scrollTop: $('#foo').offset().top},1000,function(){
alert('stop scrolling');
});
http://jsfiddle.net/yhnKR/2/
You don't have to watch the scroll event if you animate the scroll with jquery.
Ok, if you want to detect when the user stopped scrolling, you'll have to use a timeout to check if the user stopped. Otherwise you'll get the event for each scroll step.
Like this:
var delay = 1000;
var timeout = null;
$(window).bind('scroll',function(){
clearTimeout(timeout);
timeout = setTimeout(function(){
alert('scrolling stopped');
},delay);
});​​​​​​​​​​
http://jsfiddle.net/yhnKR/4/
maybe adding new events like this:
http://james.padolsey.com/javascript/special-scroll-events-for-jquery/
would help

Categories

Resources