在这篇文章中,我们将添加一个元素来调整给定元素的子元素的大小。基本结构如下:

<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-selectpointer-eventsnone,以防止防止两侧的鼠标事件和文本选择:

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)
}

查看效果