在这篇文章中,我们将添加一个元素来调整给定元素的子元素的大小。基本结构如下:
<div style="display: flex">
<!-- Left element -->
<div>Left</div>
<!-- The resizer -->
<div class="resizer" id="dragMe"></div>
<!-- Right element -->
<div>Right</div>
</div>
为了将 left、resizer 和 right 元素放在同一行,我们将 display: flex
样式添加到父级。
拖动 resizer
元素时更新左侧的宽度
在我们的例子中,可以水平拖动调整器。首先,当用户开始单击调整器时,我们必须存储鼠标位置和左侧的宽度:
// 查询元素
const resizer = document.getElementById('dragMe')
const leftSide = resizer.previousElementSibling
const rightSide = resizer.nextElementSibling
// 鼠标的当前位置
let x = 0
let y = 0
// 左侧宽度
let leftWidth = 0
// 处理当用户拖动大小调整器时触发的 mousedown 事件
const mouseDownHandler = function (e) {
// 获取当前鼠标位置
x = e.clientX
y = e.clientY
leftWidth = leftSide.getBoundingClientRect().width
// 将侦听器附加到 document
document.addEventListener('mousemove', mouseMoveHandler)
document.addEventListener('mouseup', mouseUpHandler)
}
// 连接处理程序
resizer.addEventListener('mousedown', mouseDownHandler)
从标签的结构来看,左侧和右侧是 resizer
的上一个兄弟和下一个兄弟。它们可以被检索如上所述:
const leftSide = resizer.previousElementSibling
const rightSide = resizer.nextElementSibling
接下来,当用户移动鼠标时,我们确定鼠标移动了多远,然后更新左侧的宽度:
const mouseMoveHandler = function (e) {
// 鼠标移动了多远
const dx = e.clientX - x
const dy = e.clientY - y
const newLeftWidth = ((leftWidth + dx) * 100) / resizer.parentNode.getBoundingClientRect().width
leftSide.style.width = `${newLeftWidth}%`
}
这里需要注意两点:
- 左侧的宽度是根据父级宽度的百分比数设置的。它保持了左侧和侧边宽度的比例,并在用户调整浏览器大小时使两侧看起来很好。
- 如果我们总是强制其采用剩余宽度,则无需更新右侧的宽度:
<div style="display: flex">
<!-- Left element -->
...
<!-- The resizer -->
...
<!-- Right element -->
<div style="flex: 1 1 0%;">Right</div>
</div>
修复闪烁的问题
当用户移动大小调整器时,我们应更新其光标:
const mouseMoveHandler = function(e) {
// ...
resizer.style.cursor = 'col-resize'
}
但它引发了另一个问题。只要用户移动鼠标,我们就会看到默认的鼠标光标,因为鼠标不在大小调整器的顶部。用户将看到屏幕闪烁,因为光标不断变化。
为了解决这个问题,我们为整个页面设置了光标:
const mouseMoveHandler = function(e) {
// ...
document.body.style.cursor = 'col-resize'
}
我们还设置 user-select
和 pointer-events
为 none
,以防止防止两侧的鼠标事件和文本选择:
const mouseMoveHandler = function(e) {
// ...
leftSide.style.userSelect = 'none'
leftSide.style.pointerEvents = 'none'
rightSide.style.userSelect = 'none'
rightSide.style.pointerEvents = 'none'
}
这些样式在用户停止移动鼠标后立即删除:
const mouseUpHandler = function () {
resizer.style.removeProperty('cursor')
document.body.style.removeProperty('cursor')
leftSide.style.removeProperty('user-select')
leftSide.style.removeProperty('pointer-events')
rightSide.style.removeProperty('user-select')
rightSide.style.removeProperty('pointer-events')
// 删除 mousemove 和 mouseup 的处理程序
document.removeEventListener('mousemove', mouseMoveHandler)
document.removeEventListener('mouseup', mouseUpHandler)
}