How to scroll MULTIPLE scrollbars at the same time - javascript

When I scroll the wrapper div the 2 images should also scroll together with it but it seems only the 2 images sync together but not the wrapper
The 2 images should sync with the body's scrollbar
$(function(){
$('.linked').scroll(function(){
$('.linked').scrollTop($(this).scrollTop());
})
})
#left { width: 300px; height: 400px; overflow: scroll; float: left; }
#right { width: 300px; height: 400px; overflow: scroll; float: left; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.5.1/jquery.min.js"></script>
<div id="left" class="linked">
<img src="http://upload.wikimedia.org/wikipedia/commons/5/51/Eiffel_Tower_(72_names).jpg">
</div>
<div id="right" class="linked">
<img src="http://upload.wikimedia.org/wikipedia/commons/5/51/Eiffel_Tower_(72_names).jpg">
</div>

This might help
let l = document.getElementById(".left");
let r = document.getElementById(".right");
l.addEventListener("scroll", function () {
r.scrollTop = l.scrollTop;
});
l.addEventListener("scroll", function () {
r.scrollTop = l.scrollTop;
});

If I understood correctly you're question is how to sync a set of scroll bars including the window scroll bar? This is possible and I will demonstrate for education purposes only however this is not something I would personally recommend.
Manipulating the browser window scroll bar can create a poor user experience as it interferes with the expected behavior of the scroll bar. Additionally, scrollbar manipulation can cause unexpected behavior and may not work consistently across different browsers and devices, leading to a fragmented user experience.
This example does not take into consideration the dimensions of the browser window and elements with scroll bars so be warned that it might not work as expected.
// Pseudo code
// 1. Query the scroll bars you want to sync
// 2. Add event listener to each of them
// 3. In the event listener, set the scroll position of the other scroll bar(s) to the same value
// Step 1
const scrollBarContainers = [
...document.querySelectorAll('.scrollable'),
document // Include document for window scrolling
];
// Step 2
scrollBarContainers.forEach(container => {
container.addEventListener('scroll', syncScrolls);
});
// Step 3
function syncScrolls(e) {
scrollBarContainers.forEach(container => {
if (container !== e.target) {
// The document element doesn't directly have a scrollLeft property.
// It's on the scrollingElement. The other elements have it directly.
if (container.scrollingElement) {
container.scrollingElement.scrollLeft = e.target.scrollLeft;
} else if (e.target.scrollingElement) {
container.scrollLeft = e.target.scrollingElement.scrollLeft;
} else {
container.scrollLeft = e.target.scrollLeft;
}
}
});
}
.container {
height: 100px;
border: 1px solid gray;
width: 900px;
margin-bottom: 1em;
overflow: auto;
}
span {
display: inline-block;
width: 1200px;
height: 50px;
background-color: red;
}
<div class="container scrollable"><span></span></div>
<div class="container scrollable"><span></span></div>

Related

How to modify element upon it exiting "sticky" state?

I've got an element stickied to the bottom of the viewport. I am trying to have it change text upon reaching its "destination", i.e. when it stops sticking.
I found a somewhat relevant solution elsewhere online, but I can't quite get it to work... It almost does the trick, but it only goes into effect once the element comes in contact with the top of the viewport. Whereas I want it to turn pink once it stops sticking, immediately when it touches the blue element.
I've looked around for about a week now and I still can't find a way to make this happen, nor have I had any success through pure trial and error...
Hopefully some of you wizards here know how I can achieve this.
Link to codepen: https://codepen.io/darkwing/pen/WNwVXKx
Please ignore that the element starts out pink below if you don't use the link above.
const el = document.querySelector(".myElement")
const observer = new IntersectionObserver(
([e]) => e.target.classList.toggle("is-pinned", e.intersectionRatio < 1),
{ threshold: [1] }
);
observer.observe(el);
#parent {
height: 2000px;
}
.filler {
background-color: green;
height: 500px;
}
.myElement {
width: 300px;
height: 200px;
background-color: red;
position: sticky;
bottom: 0;
}
.otherElement {
width: 300px;
height: 200px;
background-color: blue;
}
/* styles for when the header is in sticky mode */
.myElement.is-pinned {
background-color: pink;
}
<div id="parent">
<div class="filler"></div>
<div class="myElement"></div>
<div class="otherElement"></div>
</div>

How to control the scroll behaviour when there is a long event attached to it

I have a div with a scroll event on it. The scroll event is a sync function that takes some time to be computed. I see that the navigators might react to this situation with 2 different behaviours.
1- The scroll is freezed until the scroll event ends and then it will trigger the next scroll event.
2- I can scroll fluidly, but the scroll event is triggered once the previous event is done.
I would like to know how can I control and decide which one of the two scenarios the user will face.
codepen: https://codepen.io/xmorelll/pen/wvdmbYq
const container = document.getElementById("container");
const text = document.getElementById("text");
container.addEventListener('scroll', (event) => {
//sleep 1Second to simulate that the program is busy.
let a = new Date().getTime();
while(new Date().getTime() < a + 1000){};
text.style.top = container.scrollTop + "px";
});
#container {
height: 300px;
width: 300px;
border: solid 1px black;
overflow: auto;
}
#content {
height: 1000px;
width: 100%;
background-image: linear-gradient(red, yellow);
position: relative;
}
#text {
position: absolute;
left: 0;
}
<div id="container">
<div id="content">
<span id="text">Hola</span>
</div>
</div>

Why adding an invisible uninteractive overlay increases performance of scroll synchronization? [Chrome]

I need to synchronise the scroll of two elements: A content area and a "header".
But when I used the scroll event to change the position of the other element with a CSS transform, there was a notable delay between the user scrolling and the header moving, which is distracting, specially on Chrome.
However, when comparing to other applications that use the same method for synchronising the scroll position, they didn't have it. After many hours trying to find why, I've finally found it: they have an invisible overlay that covers the whole scroll area, which is also not interactive (CSS pointer-events: none)
I've prepared a playground below, where the overlay is visible and doesn't cover the whole area so it can be compared easily.
At least this behaviour is happening in my machine, a Mac with Chrome, and I scroll through the trackpad gesture. There also needs to be some load, so this snippet also adds a 30ms blocking loop every 100ms to simulate one
const content = document.getElementById("content");
const header = document.getElementById("header");
// Add content
const helloes = new Array(100).fill("Hello!").join("<br /><br />");
header.innerHTML = content.innerHTML = helloes;
// Synchronize scroll of content to header by adding `transform`
content.addEventListener("scroll", (evt) => {
header.style.transform = `translateY(${-content.scrollTop}px)`;
});
// Simulates performance hit, either low-end computer or a component doing extra load
setInterval(() => {
let start = Date.now();
while (Date.now() < start + 30);
}, 120);
.main {
position: relative;
display: flex;
}
.main>div {
overflow: auto;
height: 200px;
padding: 10px;
border: 1px solid black;
border-radius: 5px;
white-space: nowrap;
}
#content {
padding-right: 200px;
}
#overlay {
position: absolute;
left: 160px;
top: 50px;
width: 100px;
height: 100px;
box-sizing: border-box;
background: rgba(0, 0, 0, 0.3);
pointer-events: none;
}
<div class="main">
<div>
<div id="header">
</div>
</div>
<div id="content">
</div>
<div id="overlay"></div>
</div>
As it can be quite tricky to reproduce, I've recorded this behaviour in this image
It's quite subtle, but as you can see, when I'm scrolling with the mouse over the scroll area, there's a visible lag. If I'm scrolling instead on top of the uninteractive overlay, the scroll synchronization is perfect.
However, I totally can't understand why this happens - What is the reason adding an overlay and scrolling with the mouse on top of it improves the performance?

synchronise scrolling between 2 divs in native javascript (2020)

I'm trying to make a fake/duplicated scroll element that is synced with the actual x-scroll of a div in native javaScript. The use case for me is on a long table to have the x-scroll be present on screen when you're not at the bottom of the table.
This solves the situation of having a really wide table with a min-width that exceeds the current page/view-port width, causing the table to side/x-scroll. However, if the table is very long, the scroll can only be set on top or bottom of the table. That means if people are mid-way down the table and want to scroll across to see all of the columns, they would have difficulty in doing it there.
See image:
Yep, it's been done to death IN JQUERY. According to my research, no one on SO knows or has been interested in doing this in native javaScript (esp 2020). My version for reference is also in jQuery, it needs to be converted.
$dupScroll.scroll(function () {
if (scrolling) {
scrolling = false;
return true;
}
scrolling = true;
$tableParent.scrollLeft($dupScroll.scrollLeft());
});
$tableParent.scroll(function () {
if (scrolling) {
scrolling = false;
return true;
}
scrolling = true;
$dupScroll.scrollLeft($tableParent.scrollLeft());
});
All the jQuery solutions:
How to Scroll two div's at the same time?
synchronizing scrolling between 2 divs
synchronizing scrolling between 2 divs with different text size
How to sync the scroll of two divs by ID
synchronise scrolling of two divs
Help is appreciated and will be useful for all the people needing to do the same post-jQuery. I'm currently working on this myself but running into snags here and there, the 1st being attaching a scroll event onto an element. If I make something that works, I'll post it here. Thanks.
Here's the simple way to keep two divs aligned. Javascript doesn't dispatch event on actions from scripts by default, so there's no need to keep track of which div is being scrolled.
const divs = document.querySelectorAll( 'div' );
divs.forEach(div => div.addEventListener( 'scroll', e => {
divs.forEach(d => {
d.scrollTop = div.scrollTop;
d.scrollLeft = div.scrollLeft;
});
}) );
html, body {
height: 100%;
}
body {
display: flex;
padding: 0;
margin: 0;
}
div {
width: 50%;
height: 100%;
overflow: scroll;
}
span {
width: 200vw;
height: 300vh;
display: block;
background: linear-gradient(90deg, transparent, yellow), linear-gradient( 0deg, red, blue, green );
}
#div2 {
margin-top: 30px;
}
<div id="div1"><span></span></div>
<div id="div2"><span></span></div>
With Relative Scroll in Different Sized Containers
If you want to accomplish this with differently sized containers and relative scroll, just normalize the scroll value and multiply it again:
const divs = document.querySelectorAll( 'div' );
divs.forEach(div => div.addEventListener( 'scroll', e => {
const offsetTop = div.scrollTop / (div.scrollHeight - div.clientHeight);
const offsetLeft = div.scrollLeft / (div.scrollWidth - div.clientWidth);
divs.forEach(d => {
d.scrollTop = offsetTop * (d.scrollHeight - d.clientHeight);
d.scrollLeft = offsetLeft * (d.scrollWidth - d.clientWidth);
});
}) );
html, body {
height: 100%;
}
body {
display: flex;
padding: 0;
margin: 0;
}
div {
width: 50%;
height: 100%;
overflow: scroll;
}
span {
width: 200vw;
height: 300vh;
display: block;
background: linear-gradient(90deg, transparent, yellow), linear-gradient( 0deg, red, blue, green );
}
#div2 span {
height: 500vh;
width: 500vw;
}
<div id="div1"><span></span></div>
<div id="div2"><span></span></div>

Scroll a div when focused on an internal div

I need to make a scrollable div, scroll even if the mouse is upon the content (inside the scrollable div), and not just beside it (Where it is blank). This is what I have so far:
var main = document.getElementById('main-site');
var maxTop = main.parentNode.scrollHeight-main.offsetHeight;
main.parentNode.parentNode.onscroll = function() {
main.style.top = Math.min(this.scrollTop,maxTop) + "px";
}
In Chrome is ok
In IE8+ is ok (i know a hack)
In Safari the content shakes a lot when i scroll, can i fix that? (I want fix this)
Working fiddle -> https://jsfiddle.net/8oj0sge4/6/
var main = document.getElementById('main-site');
var maxTop = main.parentNode.scrollHeight - main.offsetHeight;
main.parentNode.parentNode.onscroll = function() {
main.style.top = Math.min(this.scrollTop, maxTop) + "px";
}
#wrapper {
width: 100%;
height: 1500px;
border: 1px solid red;
padding-top: 380px;
}
#wrapper .container {
border: 1px solid green;
width: 100%;
height: 500px;
overflow: scroll;
}
#wrapper .container-scroll {
height: 1500px;
width: 100%;
border: 1px solid yellow;
position: relative;
}
#wrapper .main {
width: 200px;
height: 500px;
background: black;
overflow: scroll;
position: absolute;
color: white;
left: 50%;
margin-left: -100px;
margin-top: 10px;
}
<div id="wrapper">
<div class="container">
<div class="container-scroll">
<div id="main-site" class="main">
My goals is to make the div container scroll also when the mouse is hover this div in safari, in Google and IE8 i already know how to make work, but safari is shaking a lot!
</div>
</div>
</div>
</div>
Thank you guys.
I hope this demo helps you out to make the div content scroll when mouse hover and when mouse out of the div.
<html>
</head>
<style>
.mydiv
{height: 50px;width: 100px; overflow-y: scroll; }
</style>
<script>
function loadpage()
{ document.getElementById('marquee1').stop(); }
function marqueenow()
{ document.getElementById('marquee1').start(); }
</script>
</head>
<body onload="loadpage()">
<marquee id="marquee1" class="mydiv" onmouseover="marqueenow()" onmouseout="loadpage()" behavior="scroll" direction="up" scrollamount="10">
This is my test content This is my test content This is my test content This is my test content This is my test content This is my test content This is my test
content This is my test content This is my test content This is my test content This is my test content This is my test content This is my test content This is my test content This is my test content This is my test content
</marquee>
</body>
</html>
you just add this js file to get a smooth scrolling effect.
https://github.com/nathco/jQuery.scrollSpeed
live deomo
http://code.nath.co/scrollSpeed
Not 100% sure what you are up to but you can get the fixed position with css "fixed". It will stay where you put it. The following css fixes to the bottom of the page.
.fixed {
bottom: 0;
left: 0;
position: fixed;
right: 0;
top: auto;
}
There is already an answer on scroll position:
How to get scrollbar position with Javascript?
I don't know important is that content, and by this I mean if it needs to stay selectable.
If not a pretty good solution would be to use #wrapper .main{ pointer-events: none; }, meaning that the content will not get any events from mouse and it would go through it to the next element behind it - in your case the scroll would go dirrectly to #wrapper.
Safari does this because every browser has its own scrolling. If you have a fixed header on a phone it acts bouncy and if you do this on a PC it acts normal. Explorer scrolls smooth and Chrome scrolls right to the place without a smooth transition.
The reason why your #main-site is "jiggling" is because the browser keep "repaint" the position of this element.
One Trick to solve this is called Debounce Function, (you may also google it to see other variations.) The basic idea is to delay the scroll event handler to clear out those untriggered callbacks.
In your case, you may do something like this:
main.parentNode.parentNode.onscroll = function(event) {
debounce(offsetting, 10);
}
function offsetting() {
main.style.top = Math.min(main.parentNode.parentNode.scrollTop,maxTop) + "px";
}
function debounce(method, delay) {
clearTimeout(method._tId);
method._tId= setTimeout(function(){
method();
}, delay);
}
If you keep seeing the jiggling issue, you can simply edit the delay parameter (i.e, change 10 to 50). The downside for that is your #main-site element will be 'cut off the top` for a while, depending on your delay settings.
Since your code works perfectly on Chrome and IE, there might be a bug on scrollHeight or offsetHeight attribute on Safari. I recommend you to use getBoundingClientRect for calculating element position since this method is more reliable and accurate.
var maxTop = main.parentNode.getBoundingClientRect().height - main.getBoundingCLientRect().height;

Categories