用 HTML 元素实现自定义的滚动条
有时,浏览器默认的滚动条不能满足需求,我们要实现自定义的滚动条。借助于鼠标移动事件和滚轮事件,以及内容元素的滚动相关属性,可以很容易地实现这样的需求。下面就来试一试。
我们这次要实现的滚动条需要有以下功能或要素:
- 可拖动的滑块;
- 滚动条两端有可以小幅度滚动的按钮;
- 滑块与两端按钮之间的区域可点击以进行大幅度滚动,这点与常见的滚动条一致;
- 在内容区域上滚动鼠标滚轮时可滚动内容;
- 内容区域滚动到上下两端时继续滚动鼠标滚轮,应滚动整个页面,这点与大页面中包含小的可滚动区域时的行为一致。
滚动区域包含上下两头高度固定的、点击之后进行小幅滚动的按钮,固定高度的滑块,以及它们之间的、点击之后进行大幅滚动的区域。我们让下侧大幅滚动点击区域占满其他元素余下的空间,改变上侧大幅滚动点击区域的高度,就能达到滑块滑动的效果。
下面来看代码,代码的说明包含在注释里。
HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>自定义滚动条</title>
<link href="styles.css" rel="stylesheet" />
</head>
<body>
<!--滚动视图-->
<div id="scrollView">
<!--滚动内容-->
<div id="scrollContent"></div>
<!--滚动条区域-->
<div id="scrollTrack">
<!--小幅度向上滚动按钮-->
<div id="btnUp"></div>
<!--大幅度向上滚动点击区域-->
<div id="trackUp"></div>
<!--滚动条滑块-->
<div id="scrollBar"></div>
<!--大幅度向下滚动点击区域-->
<div id="trackDown"></div>
<!--小幅度向下滚动按钮-->
<div id="btnDown"></div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
CSS:
#scrollView {
width: 320px;
height: 300px;
/*上下边距是为了演示当滚动到顶部或底部时,继续滚动鼠标滚轮会滚动整个页面*/
margin: 100px 0;
display: flex;
}
#scrollContent {
width: 300px;
height: 300px;
padding-left: 8px;
background-color: antiquewhite;
/*隐藏超出滚动内容区域的元素*/
overflow: hidden;
}
#scrollTrack {
width: 20px;
height: 300px;
background-color: cadetblue;
display: flex;
/*竖着排列滚动条区域内的上下按钮、滑块等元素*/
flex-direction: column;
}
#btnUp, #btnDown {
height: 20px;
background-color: brown;
}
#scrollBar {
height: 50px;
background-color: darkblue;
}
#trackDown {
/*让大幅度向下滚动点击区域占据排列完其他元素后剩下的所有区域*/
flex-grow:;
}
/*当拖动滑块时,给body元素加上该类,防止鼠标的拖动导致网页内容的选择操作*/
.unselectable {
/*当前版本的火狐(53)和Edge(15)不支持user-select标准属性,需要使用浏览器厂商前缀*/
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
JavaScript:
var scrollContent = document.getElementById("scrollContent");
//给滚动内容区域填充大量元素
for (var i = 1; i <= 300; i++) {
var row = document.createElement("div");
row.innerText = "第" + i + "行";
scrollContent.appendChild(row);
}
//指示鼠标左键是否处于按下状态的变量,在滑块上按下鼠标左键时设为true,在页面上任意位置松开时设回false
//由于鼠标拖动滑块时可能会离开滑块,所以mouseup和mousemove事件是注册在window上的
//在mousemove事件处理程序中,会检查该变量,以确定当前是否在拖动滑块
var mouseHeld = false;
//记录上一次mousemove事件发生时,鼠标的Y轴位置,每次发生mousemove事件时,跟上一次作比较,确定需要滚动多少距离
var previousClientY = 0;
//滑块可滑动的距离,计算方式为整个滚动条高度度减去上下按钮的高度,再减去滑块本身的高度
var barMoveLength = 300 - 20 * 2 - 50;
//内容区域可滚动的距离,计算方式为内容区域的总高度减去内容区域本身的高度
var contentMoveLength = scrollContent.scrollHeight - 300;
//为上下按钮注册事件处理程序
document.getElementById("btnUp").addEventListener("click", function () {
scrollToRelative(-30);
});
document.getElementById("btnDown").addEventListener("click", function () {
scrollToRelative(30);
});
//为大幅度上下滚动点击区域注册事件处理程序
//保存trackUp元素变量,因为每次滚动时,都要改变它的高度,以达到移动滑块的效果
var trackUp = document.getElementById("trackUp");
trackUp.addEventListener("click", function () {
scrollToRelative(-300);
});
document.getElementById("trackDown").addEventListener("click", function () {
scrollToRelative(300);
});
//为滑块注册鼠标按下事件处理程序,因为只有在滑块上按下鼠标左键时,才算开始拖动滑块
document.getElementById("scrollBar").addEventListener("mousedown", function (e) {
mouseHeld = true;
previousClientY = e.clientY;
//防止页面因为鼠标的拖动而选择上了文本或其他元素
document.body.classList.add("unselectable");
});
//鼠标左键松开时可能不在滑块上,所以mouseup事件注册在document上
document.addEventListener("mouseup", function (e) {
mouseHeld = false;
//让页面恢复可选择
document.body.classList.remove("unselectable");
});
//鼠标拖动时可能离开滑块,所以mousemove事件也注册在document上
document.addEventListener("mousemove", function (e) {
if (mouseHeld) {
//相对滑动距离计算依据为滑块滑动距离占总可滑动距离的比应与内容滚动距离占总可滚动距离的比相等
scrollToRelative((e.clientY - previousClientY) * contentMoveLength / barMoveLength);
previousClientY = e.clientY;
}
});
//为内容区域注册鼠标滚轮事件处理程序
//火狐浏览器使用和其他浏览器不同的滚轮事件和事件参数属性
if (navigator.userAgent.indexOf("Firefox") < 0) {
scrollContent.addEventListener("mousewheel", function (e) {
handleMouseWheel(-e.wheelDelta, e);
});
} else {
scrollContent.addEventListener("DOMMouseScroll", function (e) {
handleMouseWheel(e.detail * 30, e);
});
}
//确定内容区域当前是否在顶部或底部
function isOnTopOrBottom() {
//判断是否在底部时,用了向上取整函数,因为在chrome下,滚动到底时,scrollTop常为小数,与contentMoveLength不等,向上取整之后一般相等
return scrollContent.scrollTop == 0 || Math.ceil(scrollContent.scrollTop) == contentMoveLength;
}
//鼠标滚轮事件的处理程序,relative为相对滚动距离,e为事件参数
function handleMouseWheel(relative, e) {
//记录下滚动之前内容区域是否在两端
var previousOnTopOrBottom = isOnTopOrBottom();
scrollToRelative(relative);
//如果现在不在两端,或者现在在两端而滚动之前不在,则屏蔽默认滚轮行为————滚动整个页面
//反过来说,只有当“滚动”(实际上内容区域未滚动)前后内容区域都在某一端时,即已经到两端之后继续滚动时,才让滚动整个页面
if (!isOnTopOrBottom() || (isOnTopOrBottom() && !previousOnTopOrBottom)) {
e.preventDefault();
}
}
//将内容区域滚动到某一绝对位置
function scrollTo(top) {
if (top < 0) {
scrollContent.scrollTop = 0;
} else if (top > contentMoveLength) {
scrollContent.scrollTop = contentMoveLength;
} else {
scrollContent.scrollTop = top;
}
//设置滑块的位置,这是通过设置滑块上面的大幅度向上滚动点击区域的高度实现的
//滑块位置计算依据为滑块距顶部距离占总可滑动距离的比应与内容区域距顶部距离占总可滚动距离的比相等
var barDownDistance = scrollContent.scrollTop * barMoveLength / contentMoveLength;
trackUp.style.height = barDownDistance + "px";
}
//将内容区域滚动某一相对距离
function scrollToRelative(relative) {
scrollTo(scrollContent.scrollTop + relative);
}
最终效果:

这篇随笔主要演示制作自定义滚动条的技术要点,当然还有一些可以改进的地方,比如根据内容的多少设置不同的滑块高度,内容不足以滚动时隐藏滚动条,横向滚动条以及代码的可复用等。
用 HTML 元素实现自定义的滚动条的更多相关文章
- 自定义浏览器滚动条的样式,打造属于你的滚动条风格——兼容IE和webkit(ff不支持)
前段时间,到网上找素材时,看到了一个很个性的滚动条式,打开Chrome的调试工具看了一下,发现不是用JavaScript来模拟实现的,觉得 有必要折腾一下.于是在各大浏览器中对比了一下,发现只用Chr ...
- vue3系列:vue3.0自定义虚拟滚动条V3Scroll|vue3模拟滚动条组件
基于Vue3.0构建PC桌面端自定义美化滚动条组件V3Scroll. 前段时间有分享一个Vue3 PC网页端弹窗组件,今天带来最新开发的Vue3.0版虚拟滚动条组件. V3Scroll 使用vue3. ...
- [分享] 通过修改CSS自定义chrome滚动条样式
首先得说一句 我不懂CSS的写法之类的 这段CSS也是在网上找的 所以有更先进的需求的话 我肯定不能满足你们了 不好意思效果图在10楼有人上了 我这边不管怎么弄 上传图片都卡在96% 而且不翻wall ...
- 如何自定义CSS滚动条的样式?
欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由前端林子发表 本文会介绍CSS滚动条选择器,并在demo中展示如何在Webkit内核浏览器和IE浏览器中,自定义一个横向以及一个纵向的 ...
- JavaScript自定义浏览器滚动条兼容IE、 火狐和chrome
今天为大家分享一下我自己制作的浏览器滚动条,我们知道用css来自定义滚动条也是挺好的方式,css虽然能够改变chrome浏览器的滚动条样式可以自定义,css也能够改变IE浏览器滚动条的颜色.但是css ...
- AngularJs中,如何在父元素中调用子元素为自定义Directive中定义的函数?
最近一段时间准备使用AngularJs中的自定义Directive重构一下代码. 在这里说明一下,把自定义控件封装成Directive并不一定是要复用,而是要让代码结构更加清晰.就好像你将一个长方法拆 ...
- jQuery 自定义网页滚动条样式插件 mCustomScrollbar 的介绍和使用方法(转)
系统默认的滚动条样式,真的已经看的够恶心了.试想一下,如果在一个很有特色和创意的网页中,出现了一根系统中默认的滚动条样式,会有多么的别扭. 为了自己定义网页中的滚动条的方法,我真的已经找了很久了,就目 ...
- vue中的组件,Component元素,自定义路由,异步数据获取
组件是Vue最强大的功能之一.组件是一组可被复用的具有一定功能,独立的完整的代码片段,这个代码片段可以渲染一个完整视图结构组件开发如何注册组件?第一步,在页面HTML标签中使用这个组件名称,像使用DO ...
- 小程序隐藏或自定义 scroll-view滚动条
css 隐藏滚动条 ::-webkit-scrollbar { width:; height:; color: transparent; } 自定义滚动条样式 ::-webkit-scrollbar ...
随机推荐
- hibernate连接mysql,自动建表失败
hibernate的列名使用了mysql的关键字.
- 转:通过Spring Session实现新一代的Session管理
长期以来,session管理就是企业级Java中的一部分,以致于我们潜意识就认为它是已经解决的问题,在最近的记忆中,我们没有看到这个领域有很大的革新. 但是,现代的趋势是微服务以及可水平扩展的原生云应 ...
- [BZOJ2946] [Poi2000]公共串解题报告|后缀数组
给出几个由小写字母构成的单词,求它们最长的公共子串的长度. 单词个数<=5,每个单词长度<=2000 尽管最近在学的是SAM...但是看到这个题还是忍不住想写SA... (其实是不 ...
- [bzoj1568][JSOI2008]Blue Mary开公司——李超线段树
题目大意 题解 这道题需要用到一种叫做李超线段树的东西.我对于李超线段树,是这样理解的: 给节点打下的标记不进行下传,而是仅仅在需要的时候进行下传,这就是所谓永久化标记. 对于这道题,借用一张图, 这 ...
- mongoDB支持的数据类型
下表为MongoDB中常用的几种数据类型. 数据类型 描述 String 字符串.存储数据常用的数据类型.在 MongoDB 中,UTF-8 编码的字符串才是合法的. Integer 整型数值.用于存 ...
- 培训补坑(day7:线段树的区间修改与运用)(day6是测试,测试题解以后补坑QAQ)
补坑咯~ 今天围绕的是一个神奇的数据结构:线段树.(感觉叫做区间树也挺科学的.) 线段树,顾名思义就是用来查找一段区间内的最大值,最小值,区间和等等元素. 那么这个线段树有什么优势呢? 比如我们要多次 ...
- BigDecimal常用操作
import java.math.BigDecimal; public class BigDecimalUtil { /** * 由于Java的简单类型不能够精确的对浮点数进行运算,这个工具类提供精 ...
- 写了一个迷你toast提示插件,支持自定义提示文字和显示时间
写了一个迷你toast提示插件,支持自定义提示文字和显示时间,不想用其他第三方的ui插件,又想要toast等小效果来完善交互的同学可以试试, 代码中还贡献了一段css能力检测js工具函数,做项目的时候 ...
- 《Java编程思想》笔记 第六章 访问权限控制
1.编译单元 一个 编译单元即 .java 文件 内只能有一个 public 类 且该文件名必须与public 类名 完全一致. 编译单元内也可以没有public类 文件名可随意. 2. 包:库单元 ...
- SVN代码提交
SVN代码提交(转载) 原文链接:http://www.softown.cn/post/100.html 1.SVN代码提交 1) 原则 先更新再提交: SVN是为了多人协同开发而产生的,如果你在提交 ...