How do I synchronize the scroll position of two divs?
$('#bottom').on('scroll', function () {
$('#top').scrollTop($(this).scrollTop());
});
Here we are using .scrollTop()
for all it's worth, getting the scrollTop
value from the element with scroll-bars, and setting the scrollTop
for the other element to sync their scroll positions: http://api.jquery.com/scrollTop
This assumes that your bottom element has an ID of bottom
and your top element has an ID of top
.
You can hide the scroll-bars for the top
element using CSS:
#top {
overflow : hidden;
}
Here is a demo: http://jsfiddle.net/sgcer/1884/
I suppose I've never really had a reason to do this, but it looks pretty cool in action.
How to make Syncing two Divs scroll positions smoother
relying on the scroll events (OPs method 1) is fine for a desktop implementation. the scroll event fires before the screen is updated. on mobile devices, especially iOS this is not the case. due to limited resources the scroll event only fires after the user completed (lifted his finger) the scroll operation.
implementing manual scrollingto have a scroll event while the user scrolls on iOS requires to implement the scrolling manually.
register the
touchstart
event. and get the first touch:var element1 = document.getElementById('content1');
var element2 = document.getElementById('content2');
var activeTouch = null;
var touchStartY = 0;
var element1StartScrollTop = 0;
var element2scrollSyncFactor = 0;
document.addEventListener('touchstart', function(event) {
event.preventDefault();
var touch = event.changedTouches[0];
if ( activeTouch == null ) {
// implement check if touch started on an element you want to be scrollable
// save a reference to the scrolling element for the other functions
activeTouch = touch;
touchStartY = touch.screenY;
// if scroll content does not change do this calculation only once to safe compute and dom access time while animating
calcSyncFactor();
}
});
function calcSyncFactor()
{
// calculate a factor for scroll areas with different height
element2scrollSyncFactor = (element2.scrollHeight - element2.clientHeight) / (element1.scrollHeight - element1.clientHeight);
}update your scroll position on finger movement:
document.addEventListener('touchmove', function() {
for ( var i = 0; i < event.changedTouches.length; i++ ) {
var touch = event.changedTouches[i];
if ( touch === activeTouch ) {
var yOffset = touch.screenY - touchStartY;
element1.scrollTop = element1StartScrollTop + (0 - yOffset);
syncScroll();
break;
}
}
});
function syncScroll()
{
element2.scrollTop = Math.round(element1.scrollTop * element2scrollSyncFactor);
}it is possible to add a check that starts the scrolling only after the user has moved his finger some pixels. this way if the user clicks an element the document will not scroll some pixels.
cleanup after the user lifts the finger:
document.addEventListener('touchend', touchEnd);
document.addEventListener('touchcancel', touchEnd);
function touchEnd(event)
{
for ( var i = 0; i < event.changedTouches.length; i++ ) {
var touch = event.changedTouches[i];
if ( touch === activeTouch ) {
// calculate inertia and apply animation
activeTouch = null;
break;
}
}
}to have the scrolling feel more natuaral apply the iOS rubber band effect and inertia. calculate the velocity of the scroll by comparing the last
touchMove
yOffset with the one before. from this velocity apply an animation (for example css transition) that slowly stops the scrolling
see FIDDLE. see result on iOS. the fiddle only implements the solution for touch devices. for desktop devices use OP's method 1. implement a condition which checks which method to use depending on device.
how to apply inertia with css transitionsit would be possible to animate in javascript with requestAnimationFrame
. a probably more performant way on mobile might be the use of css transformations or css animations. although an elements scroll position can not be animated with css.
change the structure of the html to.
div: container with
overflow: hidden
div: content with
position: absolute
depending on content size use css property
-webkit-transform: translateZ(0)
on content div. this will create a new layer with its own backing surface, which will be composited on the gpu.
implement the functions described above so that they animate the content's
top
position instend ofscrollTop
var element1 = document.getElementById('content1');
var element2 = document.getElementById('content2');
var activeTouch = null;
var touchStartY = 0;
var element1StartScrollTop = 0;
var element2scrollSyncFactor = 0;
var offsetY = 0;
var lastOffsetY = 0;
document.addEventListener('touchstart', function(event) {
event.preventDefault();
var touch = event.changedTouches[0];
if ( activeTouch == null ) {
activeTouch = touch;
touchStartY = touch.screenY;
// use offsetTop instead of scrollTop
element1StartScrollTop = element1.offsetTop;
// if scroll content does not change do this calculation only once to safe compute time while animating
calcSyncFactor();
// cancel inertia animations
element1.style.webkitTransition = 'none';
element2.style.webkitTransition = 'none';
}
});
function calcSyncFactor()
{
// calculate a factor for scroll areas with different height
// use the div's sizes instead of scrollTop
element2scrollSyncFactor = (element2.clientHeight - element2.parentNode.clientHeight) / (element1.clientHeight - element1.parentNode.clientHeight);
}
document.addEventListener('touchmove', function() {
for ( var i = 0; i < event.changedTouches.length; i++ ) {
var touch = event.changedTouches[i];
if ( touch === activeTouch ) {
lastOffsetY = offsetY;
offsetY = touch.screenY - touchStartY;
// use offsetTop instead of scrollTop
element1.style.top = (element1StartScrollTop + offsetY) + 'px';
syncScroll();
break;
}
}
});
function syncScroll()
{
element2.style.top = Math.round(element1.offsetTop * element2scrollSyncFactor) + 'px';
}
document.addEventListener('touchend', touchEnd);
document.addEventListener('touchcancel', touchEnd);
function touchEnd(event)
{
for ( var i = 0; i < event.changedTouches.length; i++ ) {
var touch = event.changedTouches[i];
if ( touch === activeTouch ) {
applyInertia();
activeTouch = null;
break;
}
}
}when the user finishes scrolling and lifts his finger apply the inertia
function applyInertia()
{
var velocity = offsetY - lastOffsetY;
var time = Math.abs(velocity) / 150;
var element1EndPosition = element1.offsetTop + velocity;
element1.style.webkitTransition = 'top ' + time + 's ease-out';
element1.style.top = element1EndPosition + 'px';
element2.style.webkitTransition = 'top ' + time + 's ease-out';
element2.style.top = Math.round(element1EndPosition * element2scrollSyncFactor) + 'px';
}the inertia is calculated from the velocity when the user lifted the finger. fiddle around with the values to get desired results. a rubberband effect could be implemented in this function aswell. to have no javascript involved applying css animations might be the trick. another way would be to register events for when the transitions finish. if the transition finishes and the scroll position is outside the container apply a new transition that animates the content back.
see FIDDLE. see result on iOS.
synchronise scrolling between 2 divs in native javascript (2020)
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>
Synchronize scrolls on 2 divs
So the problem was that when scrolling the first list it updated the second one which also reacted to scroll event and updated the first one. I'm not sure that's what you described in the question, but anyway my idea of fixing it is to check where's cursor right now and update the opposite list. It can be easily done with extra property in the class and updating it on mouseenter
event.
This is my solution: https://stackblitz.com/edit/angular-9-0-0-rc-1-sync-scroll-m9jqr7?file=src/app/app.component.ts
How do I synchronize the scroll position of two divs using AngularJS?
Here is my example. It was initially written to combine vertical scrolls for two elements with the same size which are positioned in different blocks on page. I've rewritten it for horizontal scroll.
But it needs elements to be the same width. And it synchronizes the scroll - they are both master and slave at one time. If your elements have different sizes, you can set scroll using a proportion, something like el.scrollLeft = scrollLeft*(el.width/otherEl.width)
.
synchronizing scrolling between 2 divs
Always check the console - that will cause errors because you are attempting to use jQuery methods on native elements (since you retrieved them via [0]
). If you were doing this purely for the sake of the if
condition, there's no need - to check the selectors found elements, you can just query the length
property.
$(function() {
var bmrDetailDiv = $("div[id$='_bmrDetailDataDiv']");
var residentDetailDiv = $("div[id$='_residentDetailDataDiv']");
if (bmrDetailDiv.length && residentDetailDiv.length) {
bmrDetailDiv.on('scroll', function () {
residentDetailDiv.scrollTop($(this).scrollTop());
});
residentDetailDiv.on('scroll', function () {
bmrDetailDiv.scrollTop($(this).scrollTop());
});
}
});
Other changes:
1) Document ready handler instead of window.onload
2) Use of $(this)
inside event callback
How can I synchronize the "scrollLeft" property of 2 divs while scrolling horizontally within a react component?
Eventually, I found the solution myself. It works perfectly if useRef
is used instead of useState
. When the scroll event fires, the scrollLeft
property of div1
is set to the value of the scrollLeft
property of div2
using the references created with useRef
.
const { useRef } = require('react');
const MainComponent = () => {
const div1 = useRef(null);
const div2 = useRef(null);
const onScroll = () => {
div1.current.scrollLeft = div2.current.scrollLeft;
}
return (
<>
<div ref={div1} style={{ height:'200pt', width:'800pt', overflow:'scroll'}} onScroll={onScroll}>
<div style={{ height:'600pt', width:'1600pt', backgroundColor:'lightgray' }}>
...
</div>
</div>
<div ref={div2} style={{ height:'200pt', width:'800pt', overflow:'scroll'}} onScroll={onScroll}>
<div style={{ height:'600pt', width:'1600pt', backgroundColor:'lightgray' }}>
...
</div>
</div>
</>
)
};
export default MainComponent;
Related Topics
How to Get the Ajax Response from Success and Assign It in a Variable Using Jquery
Scrollable Table With Fixed Headers and Fixed First Column
How to Get Line Break Within String Interpolation in Angularjs
How to Save an Image to Localstorage and Display It on the Next Page
Javascript Generate Random Numbers Without Repeating
Angular 8 Loose Data After Refresh Page
Reload Table After Deleting an Item from It With React
How to Assign Empty Value to Select Dropdown
Jquery - Get Id Dynamically from Generated Element
How to Redirect to Another Component Onsubmit Search
Stop Just One Dropdown Toggle from Closing on Click
Angular Load Async Data Before Component Initialization
Formdata Created from an Existing Form Seems Empty When I Log It
Download Image as File in Typescript
Javascript Submit Multiple Forms With One Button
Parsing String as Json With Single Quotes
How to Completely Remove Ionic and Cordova Installation from Mac
How to Redirect to Another Page in Reactjs When a If Condition Is Executed