JavaScript手工编写滚动条组件
0 前言
上周的一个练习,由于没来得及编写笔记,这里补充一下~
虽然CSS3中提供了overflow:scroll; 来实现滚动条,但是这里可以使用原生JS来编写一个,以达到练习组件编写的效果。
练习要点包括以下几点:
dom对象操作
鼠标点击事件
鼠标滑轮事件
滚动条显示比例设计逻辑
感兴趣的朋友欢迎点击屏幕左上角的小猫咪进入我的github,或猛戳这里下载源码~
1 需求分析
需要支持鼠标点击响应:点击上下箭头时,能上下滚动文章内容。
需要支持鼠标拖拽响应:拖拽滚动条并上下滚动文章内容。此外应支持拖拽过快的状况,即鼠标超出滚动条区域范围依然能够上下滚动。
边界限制:当滚动条到达范围的边界的时候,必须能够停止,不得超出屏幕显示范围。
对于不在显示区域的文字部分需要隐藏。
- 这里我们只关心其实现逻辑,而不关注其展现形式上的美化。
效果如下:

2 实现细节
2-1 HTML结构
结构上非常简单,两栏布局,内容文本区和滚动条区。
滚动条区采用ul-li结构实现,分别加载图片和使用一个div。
代码如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Scroll</title>
<link rel="stylesheet" href="index.css">
</head>
<body>
<div class="container">
<div class="content">
区域文本内容。
</div>
<ul class="scroll">
<li class="up"><img id="up_img" src="img/up.png" alt=""></li>
<li class="duration"><div class="bar"></div></li>
<li class="down"><img id="down_img" src="img/down.png" alt=""></li>
</ul>
</div>
<script src="index.js"></script>
</body>
</html>
2-2 CSS样式
几个要点:
在外层container上使用overflow:hidden隐藏超出区域范围的文本内容。
选择器的使用cursor:-webkit-grab;设置一个鼠标移入的形状控制。
- 若根据父子选择器的选择逻辑,则需要在父子标签之间添加空格,即表示选中子元素。
- 若不添加空格,则表示的是选中同一级标签,通过多个标签来缩小选中范围。
代码如下:
*{
margin:;
padding:;
list-style: none;
}
body{
overflow: hidden;
}
.container{
width: 380px;
height: 400px;
margin: 100px auto;
border: 1px solid red;
position: relative;
overflow: hidden;
}
.container .content{
width: 350px;
position: absolute;
left:;
top:;
}
.container .scroll{
width: 28px;
height: 398px;
position: absolute;
border: 1px solid black;
right:;
top:;
}
.container .scroll li{
width: 100%;
}
.container .scroll li.up{
height: 28px;
cursor: pointer;
}
.container .scroll li.down{
height: 28px;
cursor: pointer;
}
.container .scroll li img{
height: 100%;
width: 100%;
}
.container .scroll li.duration{
height: 342px;
width: 100%;
position: relative;
}
.container .scroll .duration .bar{
position: absolute;
right:;
top:;
height: 27px;
width: 100%;
background-color: red;
cursor: -webkit-grab;
}
2-3 Javascript实现
2-3-1 模块调用入口
在html文档引入了JS代码以后,需要一个函数调用的入口,这里我采用的是以init()函数作为主入口。最外层是一个立即执行函数,保证这一组件内部的变量只可以被内部成员访问,避免变量污染的发生。
在入口函数init()中调用了三个函数,分别是控制拖拽的scrollDrage(ele_bar);控制点击事件的scrollClick(ele_bar);和控制滚轮事件的scrollWheel(ele_container , ele_bar);
(function(){
function init(){
scrollDrage(ele_bar);
scrollClick(ele_bar);
scrollWheel(ele_container , ele_bar);
}
init();
});
2-3-2 获取dom对象
由于需要对dom元素进行操作,因此首先需要先获取到与滚轮操作有关的对象:页面容器container、文本content、滚动条中间滑动部分duration、滑块bar
可以在初始时设置滑块bar的高度,这里采用相似变换的原理,计算其值并进行赋值即可。
滑块高度的计算公式为:
Math.floor((container.offsetHeight / content.offsetHeight) * duration.offsetHeight)
offsetHeight属性为获取还dom元素的height属性。
var ele_container = document.getElementsByClassName('container')[0];
var ele_content = document.getElementsByClassName('content')[0];
var ele_duration = document.getElementsByClassName('duration')[0];
var ele_bar = document.getElementsByClassName('bar')[0];
var percentH = Math.floor((ele_container.offsetHeight / ele_content.offsetHeight) * ele_duration.offsetHeight);
ele_bar.style.height = percentH + 'px
2-3-3 文本移动控制
文本移动控制的方法同样用到了相似变换的原理,并根据计算的高度,修改文本context的top值。
传入的参数为滑块bar对象,在鼠标事件中调用该方法。
注意:由于向下查看内容时,文本是向上移动的,因此这个top值需要设置为负值。
function contentMove(item){
var percentHeight = item.offsetTop / (ele_duration.offsetHeight - item.offsetHeight);
var moveH = percentHeight * Math.floor(ele_content.offsetHeight - ele_container.offsetHeight);
ele_content.style.top = -moveH + 'px';
}
2-3-4 鼠标拖拽事件
在这一部分有以下几点注意:
e = e||window.event 一般在获取事件对象e的时候要如此给一个初值,大体上是为了给firefox做一个兼容,细节上的讨论可以参考这篇文章。
e.preventDefault()这里是为了阻止事件的默认动作,比如提交表单等。
通过e.pageY来获取点击位置的y坐标。关于使用事件对象e来获取点击位置坐标的方法有screen,offset,page和client四种。
event.clientX、event.clientY
鼠标相对于浏览器窗口可视区域的X,Y坐标(窗口坐标),可视区域不包括工具栏和滚动条。IE事件和标准事件都定义了这2个属性
event.pageX、event.pageY
类似于event.clientX、event.clientY,但它们使用的是文档坐标而非窗口坐标。这2个属性不是标准属性,但得到了广泛支持。IE事件中没有这2个属性
event.offsetX、event.offsetY
鼠标相对于事件源元素(srcElement)的X,Y坐标,只有IE事件有这2个属性,标准事件没有对应的属性
event.screenX、event.screenY
鼠标相对于用户显示器屏幕左上角的X,Y坐标。标准事件和IE事件都定义了这2个属性
用一张图可以表示成下图所示

鼠标事件绑定的对象:需要确定鼠标事件的作用范围,因此必须明确鼠标事件所绑定的对象。对于mousedown事件,就需要绑定到bar滑块对象上;而mousemove和mouseup事件,则需要绑定在document对象上(即整个窗口)。这是为了避免鼠标移动过快,而导致鼠标移出滑块区域导致鼠标事件响应失效。
解除绑定:当mouseup时,需要解除mousemove的绑定,只需为mousemove赋值为null即可。
获取当前对象相对其父元素的top值:offsetTop,进而根据此值,来更新鼠标拖拽的滑块位置。
function scrollDrage(item){
item.onmousedown = function(e){
e = e || window.event;
e.preventDefault();
var e_y = e.pageY;
document.onmousemove = function(e){
var chay = e.pageY - e_y;
item.style.top = item.offsetTop + chay + 'px';
e_y = e.pageY;
if(item.offsetTop <= 0){
item.style.top = 0 + 'px';
}
else if(item.offsetTop + item.offsetHeight >= ele_duration.offsetHeight){
item.style.top = ele_duration.offsetHeight - item.offsetHeight + 'px';
}
contentMove(item);
}
}
document.onmouseup = function(){
document.onmousemove = null;
}
}
2-3-5 鼠标点击上下箭头移动事件
这里的要点有:
- e.target:
- target定义:
target 事件属性可返回事件的目标节点(触发该事件的节点),如生成事件的元素、文档或窗口。
- 语法:
event.target
event.target.nodeName //获取事件触发元素标签name(li,p...)
event.target.id //获取事件触发元素id
event.target.className //获取事件触发元素classname
event.target.innerHTML //获取事件触发元素的内容(li)
等等。
- 这里我用e.target.id来判断是上翻还是下翻页面。
- 边界判断:
这里仍要进行边界判断,比如offsetTop <= 0的情况和bar.offsetTop + bar.offsetHeight >= scroll.offsetHeight的情况需要直接赋予边界值,保证滑块不会从临界值超出范围。
function scrollClick(item){
var ele_scroll = document.getElementsByClassName('scroll')[0];
var speed = 5;
ele_scroll.onclick = function(e){
// console.log(e.target.id);
if(e.target.id == 'up_img'){
item.style.top = item.offsetTop - speed + 'px';
if(item.offsetTop <= 0){
item.style.top = 0 + 'px';
}
}
else if(e.target.id == 'down_img'){
item.style.top = item.offsetTop + speed + 'px';
if(item.offsetTop + item.offsetHeight >= ele_duration.offsetHeight){
item.style.top = ele_duration.offsetHeight - item.offsetHeight + 'px';
}
}
contentMove(item);
}
}
2-3-6 鼠标滚轮控制
通过onmousewheel绑定滚轮事件,e.wheelDelta表示滚轮方向,为正向上,负向下。
同样要施加一个边界判断。其他流程与2-3-5完全类似。
function scrollWheel(container , item){
var speed = 5;
container.onmousewheel = function(e){
// console.log(e.wheelDelta); //上下滑动滑轮
if(e.wheelDelta > 0){ //向上滑动滑轮
item.style.top = item.offsetTop - speed + 'px';
if(item.offsetTop <= 0){
item.style.top = 0 + 'px';
}
}
else{
item.style.top = item.offsetTop + speed + 'px';
if(item.offsetTop + item.offsetHeight >= ele_duration.offsetHeight){
item.style.top = ele_duration.offsetHeight - item.offsetHeight + 'px';
}
}
contentMove(item);
}
}
3 总结
通过练习基础组件的写法,达到基础巩固的目的。但还是不要忘记使用overflow:scroll;这种自动实现滚动条的方法。
JavaScript手工编写滚动条组件的更多相关文章
- 【Java例题】8.2 手工编写字符串统计的可视化程序
2. 手工编写字符串统计的可视化程序. 一个Frame窗体容器,布局为null,两个TextField组件,一个Button组件. Button组件上添加ActionEvent事件监听器Actio ...
- 【Java例题】8.1手工编写加法器的可视化程序
1. 手工编写加法器的可视化程序. 一个Frame窗体容器,布局为null,三个TextField组件,一个Button组件. Button组件上添加ActionEvent事件监听器ActionLis ...
- 使用Javascript来编写贪食蛇(零基础)
引用的东西都很基础,注释也很多,这里就不多说了. <head> <meta http-equiv="Content-Type" content="t ...
- Servlet基础-手工编写第一个servlet
[手工编写第一个servlet] [步骤] 1.继承HttpServlet 2.重写doGet()或者doPost()方法 //这个doGet或者doPost方法取决用户提交的方式 3.在web.x ...
- struts_19_对Action中所有方法、某一个方法进行输入校验(手工编写代码实现输入校验)
对所有方法进行校验1.通过手工编写代码的形式实现 需求:用户名:不能为空手机号:不能为空,并且要符合手机号的格式1,3/5/8,后面是9个数字 第01步:导包 第02步:配置web.xml <? ...
- struts2视频学习笔记 19-20(手工编写代码实现所有方法和指定方法校验)
课时19 对Action中所有方法进行输入校验 1.手工编写代码实现对action中所有方法输入校验 通过重写validate() 方法实现, validate()方法会校验action中所有与exe ...
- iOS应用日志:开始编写日志组件与异常日志
应用日志(一):开始编写日志组件 对于那些做后端开发的工程师来说,看 LOG解Bug应该是理所当然的事,但我接触到的移动应用开发的工程师里面,很多人并没有这个意识,查Bug时总是一遍一遍的试图重现,试 ...
- 纯javascript代码编写计算器程序
今天来分享一下用纯javascript代码编写的一个计算器程序,很多行业都能用到这个程序,例如做装修预算.贷款利率等等. 首先来看一下完成后的效果: 具体代码如下:(关注我的博客,及时获取最新WEB前 ...
- 第2篇:用as3.0制作一个滚动条组件
本实例演示了实现一个滚动条基本功能的制作方法,没有添加改变皮肤,修改滚动条视框大小等功能,有兴趣的朋友可根据自己要求自行添加.使用时只需要通过以下一行代码创建滚动条组件: var myScrollba ...
随机推荐
- 解释器模式 Interpreter 行为型 设计模式(十九)
解释器模式(Interpreter) 考虑上图中计算器的例子 设计可以用于计算加减运算(简单起见,省略乘除),你会怎么做? 你可能会定义一个工具类,工具类中有N多静态方法 比如定义了两个 ...
- java集合(1)
java集合类存放于java.util包里,只能存放对象,存放的是对象的引用,可以是不同类型,不限数量的数据类型. 顶层接口:Iterator(迭代器),Map Iterator:核心方法hasNex ...
- 超级有爱的五款APP共享 可以让你神操作
随着科技的不断发展,手机功能的不断完善,让我们更加依赖手机,不得不说手机给我们带来很多的乐趣和方便. 今天就主要给大家分享五款超级有爱的APP软件,感兴趣的小伙伴已经迫不及待了吧! 荔枝 荔枝是一款声 ...
- 《.NET 进阶指南》读书笔记2------定义不可改变类型
不可改变对象的定义 一个类型的对象在创建后,它的状态就不能再改变,知道它死亡,它的状态一直维持与创建时相同.这时候称该对象具有不可改变性.这样的类型为不可改变类型. 不可改变对象在创建的时候,必须完全 ...
- rocketmq延时消息
rocketmq提供一种延时消息的解决方案,就是在特定的时间到了,消息才会被投递出去供consumer消费. 总体来是简单的场景是满足了,但是需要注意的是延时的时间是需要按照默认配置的延时级别去配置的 ...
- Hive分桶
1.简介 分桶表是对列值取哈希值的方式将不同数据放到不同文件中进行存储.对于hive中每一个表,分区都可以进一步进行分桶.由列的哈希值除以桶的个数来决定数据划分到哪个桶里. 2.适用场景 1.数据抽样 ...
- 如何删除Windows10操作系统资源管理器中的下载、图片、音乐、文档、视频、桌面、3D对象这7个文件夹
通过注册表删除,步骤如下: 1.按下win+R,输入regedit,打开注册表 2.找到位置:计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Cur ...
- ASP.NET Core 下的依赖注入(一)
本文介绍利用 Microsoft.Extensions.Configuration.Binder.dll 来实现超级简单的注入. 1. appsettings.json 中定义配置 假设我们有如下配置 ...
- ASP.NET Core 使用 Google 验证码(Google reCAPTCHA)
关心最多的问题,不FQ能不能用,答案是能.Google官方提供额外的域名来提供服务,国内可以正常使用. 一. 前言 验证码在我们实际的生活场景中非常常见,可以防止恶意破解密码.刷票.论坛灌水.刷注册等 ...
- 【深色模式】macOS Mojave+Visual Studio for Mac+FineUICore多图赏析!
全面开启深色模式,今早成功升级到 macOS Mojave,下面就来欣赏一下吧. 点击图片,查看大图 1. 下载 macOS Mojave 2. 安装成功,开启深色模式 3. 来一张桌面截图 4. 开 ...