在一些支持用 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’,于是上述监听代码变成了: