1. 练习手写滚动条
2. 市面上多是竖向滚动条,横向滚动条较少
3. 横向滚动条,需要滑动到容器底部才能使用,不方便,因此想自己写一个横向滚动条放置在容器内部的视口高度的最低处
3. 问题复现:
如果容器的内容超过了容器的宽度,就会出现横向滚动条(暂不考虑换行),但是如果该容器的高度过高,用户需要滑到容器的底部才能操作横向滚动条(横向滚动条默认在容器的底部,当然我们不考虑使用shift+鼠标左键的方式横向移动)
横向滚动条-问题演示
样式:
滚动条包含两个方面:track(滚动轨道) + thumb(滚动块)

最开始是只有content容器的,然后我们在content下面添加横向滚动条容器,并且在最外层添加box容器。
最外层的box是相对定位(没有宽高),滚动条是绝对定位
- .box {
- position: relative;
- }
- .content {
- width: 100px;
- height: 1000px;
- background-color: pink;
- overflow: auto;
- }
- .content::-webkit-scrollbar {
- display: none;
- }
- .track {
- position: absolute;
- left: 0;
- bottom: 0; /*0<=bottom<=1000-10px*/
- width: 100px;
- height: 10px;
- background-color: red;
- }
- .thumb {
- cursor: grab;
- position: absolute;
- left: 0;
- top: 0;
- width: 10px;
- height: 100%;
- background-color: blue;
- }
-
-
- <div class="box">
- <div class="content">
- asdawdkjaslkdjalkwjdlkasjdlawkldakslldjawlkdjlkasjdkawdasdawes
- asdawdkjaslkdjalkwjdlkasjdlawkldakslldjawlkdjlkasjdkawdasdawes
- asdawdkjaslkdjalkwjdlkasjdlawkldakslldjawlkdjlkasjdkawdasdawes
- </div>
- <div class="track" id="scrollbar">
- <div class="thumb" id="scrollbar-thumb"></div>
- </div>
- </div>
逻辑:
监听content容器的滚动动作,同步到滑块的位置
- const content = document.querySelector(".content")
- const track = document.querySelector(".track")
- const thumb = document.querySelector(".thumb")
- function updateScrollbar() {
- const scrollPercentage =
- content.scrollLeft / (content.scrollWidth - content.clientWidth)
- const thumbPosition =
- scrollPercentage * (track.clientWidth - thumb.clientWidth)
- thumb.style.left = thumbPosition + "px"
- }
- // content(这样还包括了shift+鼠标左键)
- content.addEventListener("scroll", updateScrollbar)
然后,我们再来制作拖动滑块同时横向移动容器的功能,主要用到三个动作,mousedown,mousemove,mouseup
- // 监听滑块拖动事件
- let isDragging = false
- let startX
- let startScrollLeft
- thumb.addEventListener("mousedown", function (e) {
- isDragging = true
- startX = e.clientX
- startScrollLeft = content.scrollLeft
-
- // 拖动开始时改变鼠标样式为正在抓取
- thumb.style.cursor = "grabbing"
-
- // 防止在拖动时选择文本
- e.preventDefault()
- })
-
- document.addEventListener("mousemove", function (e) {
- if (isDragging) {
- const deltaX = e.clientX - startX
- const scrollDelta =
- (deltaX / track.clientWidth) *
- (content.scrollWidth - content.clientWidth)
- content.scrollLeft = startScrollLeft + scrollDelta
- }
- })
-
- document.addEventListener("mouseup", function () {
- isDragging = false
- })
(横向滚动条的位置最低不低于容器的边界底部,最高不高于容器的边界顶部)
- let contentRec = content.getBoundingClientRect()//得到content容器的边界
- track.style.bottom =
- content.clientHeight - (window.innerHeight - contentRec.top) + "px"
-
-
- let trackBottom = parseInt(track.style.bottom) // 获取轨道的底部位置
- if (
- trackBottom <= 0 ||
- trackBottom >= content.clientHeight - track.clientHeight
- ) {
- track.style.bottom = "0px" // 设置时加上单位
- }
-
-
- // 更新 track.style.bottom 的逻辑
- document.addEventListener("scroll", function () {
- let viewHeight = window.innerHeight + scrollY
- contentRec = content.getBoundingClientRect()
- // 计算新的 bottom 值
- let newBottom =
- content.clientHeight - (window.innerHeight - contentRec.top)
-
- // 确保 newBottom 不小于 0 且不大于 content.clientHeight - track.clientHeight
- newBottom = Math.max(
- 0,
- Math.min(newBottom, content.clientHeight - track.clientHeight)
- )
-
- // 更新 track 的 bottom 样式
- track.style.bottom = `${newBottom}px`
- })
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8" />
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
- <title>Document</title>
- <style>
- * {
- margin: 0;
- padding: 0;
- box-sizing: border-box;
- }
- .header {
- width: 100px;
- height: 1000px;
- background-color: skyblue;
- }
- .footer {
- width: 100px;
- height: 300px;
- background-color: green;
- }
- .box {
- position: relative;
- }
- .content {
- width: 100px;
- height: 1000px;
- background-color: pink;
- overflow: auto;
- }
- .content::-webkit-scrollbar {
- display: none;
- }
- .track {
- position: absolute;
- left: 0;
- bottom: 0; /*0<=bottom<=1000-10px*/
- width: 100px;
- height: 10px;
- background-color: red;
- }
- .thumb {
- cursor: grab;
- position: absolute;
- left: 0;
- top: 0;
- width: 10px;
- height: 100%;
- background-color: blue;
- }
- </style>
- </head>
- <body>
- <div class="header"></div>
- <div class="box">
- <div class="content">
- asdawdkjaslkdjalkwjdlkasjdlawkldakslldjawlkdjlkasjdkawdasdawes
- asdawdkjaslkdjalkwjdlkasjdlawkldakslldjawlkdjlkasjdkawdasdawes
- asdawdkjaslkdjalkwjdlkasjdlawkldakslldjawlkdjlkasjdkawdasdawes
- asdawdkjaslkdjalkwjdlkasjdlawkldakslldjawlkdjlkasjdkawdasdawes
- asdawdkjaslkdjalkwjdlkasjdlawkldakslldjawlkdjlkasjdkawdasdawes
- asdawdkjaslkdjalkwjdlkasjdlawkldakslldjawlkdjlkasjdkawdasdawes
- </div>
- <!-- 我觉得这个滚动条应该是不能在内容容器内部的 -->
- <div class="track" id="scrollbar">
- <div class="thumb" id="scrollbar-thumb"></div>
- </div>
- </div>
- <div class="footer"></div>
-
- <script>
- const content = document.querySelector(".content")
- const track = document.querySelector(".track")
- const thumb = document.querySelector(".thumb")
-
- // console.log("window.innerHeight", window.innerHeight)
- // track.style.bottom = window.innerHeight + "px"
- let contentRec = content.getBoundingClientRect()
- track.style.bottom =
- content.clientHeight - (window.innerHeight - contentRec.top) + "px"
-
- function updateScrollbar() {
- const scrollPercentage =
- content.scrollLeft / (content.scrollWidth - content.clientWidth)
- const thumbPosition =
- scrollPercentage * (track.clientWidth - thumb.clientWidth)
- thumb.style.left = thumbPosition + "px"
- }
- // content(这样还包括了shift+鼠标左键)
- content.addEventListener("scroll", updateScrollbar)
-
- // 提取数值进行比较
- let trackBottom = parseInt(track.style.bottom) // 将字符串转换为整数
- if (
- trackBottom <= 0 ||
- trackBottom >= content.clientHeight - track.clientHeight
- ) {
- track.style.bottom = "0px" // 设置时加上单位
- }
-
- // 更新 track.style.bottom 的逻辑
- document.addEventListener("scroll", function () {
- let viewHeight = window.innerHeight + scrollY
- contentRec = content.getBoundingClientRect()
- // 计算新的 bottom 值
- let newBottom =
- content.clientHeight - (window.innerHeight - contentRec.top)
-
- // 确保 newBottom 不小于 0 且不大于 content.clientHeight - track.clientHeight
- newBottom = Math.max(
- 0,
- Math.min(newBottom, content.clientHeight - track.clientHeight)
- )
-
- // 更新 track 的 bottom 样式
- track.style.bottom = `${newBottom}px`
-
- console.log(track.style.bottom)
- })
-
- // 监听滑块拖动事件
- let isDragging = false
- let startX
- let startScrollLeft
- thumb.addEventListener("mousedown", function (e) {
- isDragging = true
- startX = e.clientX
- startScrollLeft = content.scrollLeft
-
- // 拖动开始时改变鼠标样式为正在抓取
- thumb.style.cursor = "grabbing"
-
- // 防止在拖动时选择文本
- e.preventDefault()
- })
-
- document.addEventListener("mousemove", function (e) {
- if (isDragging) {
- const deltaX = e.clientX - startX
- const scrollDelta =
- (deltaX / track.clientWidth) *
- (content.scrollWidth - content.clientWidth)
- content.scrollLeft = startScrollLeft + scrollDelta
- }
- })
-
- document.addEventListener("mouseup", function () {
- isDragging = false
- })
- </script>
- </body>
- </html>
横向滚动条