原生JS控制多个滚动条同步跟随滚动

在一些支持用 markdown写文章的网站,例如 掘金 或者 CSDN等,后台写作页面,一般都是支持 markdown即时预览的,也就是将整个页面分成两部分,左半部分是你输入的 markdown文字,右半部分则即时输出对应的预览页面,例如下面就是 CSDN后台写作页面的 markdown即时预览效果:

这里写图片描述

本文不是阐述如何从 0实现这种效果的(后续 很可能 会单出文章,),抛开其他,单看页面主体中左右两个容器元素,即 markdown输入框元素和预览显示框元素

本文要探讨的是,当这两个容器元素的内容都超出了容器高度,即都出现了滚动框的时候,如何在其中一个容器元素滚动时,让另外一个元素也随之滚动。


DOM结构

既然是与滚动条有关,那么首先想到 js中控制滚动条高度的一个属性: scrollTop,只要能控制这个属性的值,自然也就能控制滚动条的滚动了。

对于以下 DOM结构:

<div id="container">
  <div class="left"></div>
  <div class="right"></div>
</div>

其中,.left元素是左半部分输入框容器元素,.right元素是右半部分显示框容器元素,.container是它们共同的父元素。

由于需要溢出滚动,所以还需要设置一下对应的样式(只是关键样式,非全部):

#container {
  display: flex;
  border: 1px solid #bbb;
}
.left, .right {
  flex: 1;
  height: 100%;
  word-wrap: break-word;
  overflow-y: scroll;
}

再向 .left.right元素中塞入足够的内容,让二者出现滚动条,就是下面这种效果:

这里写图片描述

样式是出来个大概了,下面就可以在这些 DOM上进行一系列的操作了。


初次尝试

大致思路,监听两个容器元素的滚动事件,在其中一个元素滚动的时候,获取这个元素的 scrollTop属性的值,同时将此值设置为另外一个滚动元素的 scrollTop值即可。

例如:

var l=document.querySelector('.left')
var r=document.querySelector('.right')
l.addEventListener('scroll',function(){
   
  r.scrollTop = l.scrollTop
})

效果如下:

这里写图片描述

似乎很不错,但是现在是不仅想让右边跟随左边滚动,还想左边跟随右边滚动,于是再加以下代码:

l.addEventListener('scroll',function(){
   
  r.scrollTop = l.scrollTop
})

看上去很不错,然而,哪有那么简单的事情。

这个时候你再用鼠标滚轮进行滚动的时候,却发现滚动得有点吃力,两个容器元素的滚动似乎被什么阻碍住了,很难滚动。

仔细分析,原因很简单,当你在左边滚动的时候,触发了左边的滚动事件,于是右边跟随滚动,但是与此同时右边的跟随滚动也是滚动,于是也触发了右边的滚动,于是左边也要跟随右边滚动…然后就进入了一个类似于相互触发的情况,所以就会发现滚动得很吃力。


解决scroll事件同时触发的问题

想要解决上述问题,暂时有以下两种方案。

scroll事件换成 mousewheel事件

由于 scroll事件不仅会被鼠标主动滚动触发,同时改变容器元素的 scrollTop也会触发,元素的主动滚动其实就是鼠标滚轮触发的,所以可以将scroll事件换成一个对鼠标滚动敏感而不是元素滚动敏感的事件:’mousewheel’,于是上述监听代码变成了:


                
  • 11
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
好的,下面是一个简单的原生JS实现滚动尺子的代码: HTML代码: ``` <div class="slider-wrapper"> <div class="slider-track"></div> <div class="slider-handle"></div> </div> ``` CSS代码: ``` .slider-wrapper { width: 300px; height: 30px; border: 1px solid #ccc; position: relative; } .slider-track { width: 100%; height: 5px; background-color: #ccc; position: absolute; top: 50%; transform: translateY(-50%); } .slider-handle { width: 20px; height: 20px; background-color: #fff; border: 1px solid #ccc; border-radius: 50%; position: absolute; top: 50%; transform: translate(-50%, -50%); cursor: pointer; } ``` JS代码: ``` var sliderWrapper = document.querySelector('.slider-wrapper'); var sliderHandle = sliderWrapper.querySelector('.slider-handle'); var sliderTrack = sliderWrapper.querySelector('.slider-track'); var isDragging = false; sliderHandle.addEventListener('mousedown', function(e) { isDragging = true; }); document.addEventListener('mousemove', function(e) { if (isDragging) { var x = e.clientX - sliderWrapper.offsetLeft; if (x < 0) { x = 0; } else if (x > sliderWrapper.offsetWidth - sliderHandle.offsetWidth) { x = sliderWrapper.offsetWidth - sliderHandle.offsetWidth; } var percent = x / (sliderWrapper.offsetWidth - sliderHandle.offsetWidth); sliderHandle.style.left = percent * 100 + '%'; sliderTrack.style.width = percent * 100 + '%'; } }); document.addEventListener('mouseup', function(e) { isDragging = false; }); ``` 说明:该代码使用了部分ES6语法,需要在支持ES6的浏览器中运行。该滚动尺子可拖动滑块调节滑块位置。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值