大家好,我是前端架构师,关注微信公众号【@程序员大卫】免费领取前端精品资料。
前言
之前有个老的项目,需要使用 jQuery 实现这种双栏布局拖拽的效果,特此记录一下,分享给大家。
实现思路讲解
这个双栏拖拽布局,本质上只做了一件事:
通过拖拽中间的分割线,动态改变左侧容器的宽度,右侧自动填充剩余空间。
我们可以把整个实现拆成 4 个核心步骤 来理解。
一、整体布局结构是关键
先看最核心的 DOM 结构(简化后):
<div class="content-container">
<div class="left-container"></div>
<div class="divider-container"></div>
<div class="right-container"></div>
</div>
为什么要这样结构?
- 父容器
.content-container使用flex - 左侧
.left-container固定宽度(但可变) - 中间
.divider-container是拖拽手柄 - 右侧
.right-container使用flex-grow: 1自动填充剩余空间
👉 重点:我们只需要控制 left 的宽度,right 会自动变化
CSS 里最关键的几行是:
.content-container {
display: flex;
}
.left-container {
flex-shrink: 0;
}
.right-container {
flex-grow: 1;
}
二、拖拽的核心原理(一定要理解)
拖拽其实并不神秘,本质只有 3 个事件:
- mousedown:开始拖拽
- mousemove:拖拽过程中,持续计算距离
- mouseup:结束拖拽
拖拽时我们关心什么?
只关心 鼠标在 X 轴上移动了多少
当前宽度 = 初始宽度 + (当前鼠标 X - 按下时鼠标 X)
代码里对应的是这一段:
var startX = e.clientX; // 鼠标按下时的 X
var leftWidth = $left.width(); // 左侧初始宽度
setLeftContainerWidth(leftWidth + e.clientX - startX);
三、为什么 mousemove 要绑定在 document 上?
这是一个非常重要的细节,新手最容易踩坑。
$document.on("mousemove", onMousemove);
$document.on("mouseup", onMouseup);
如果绑定在 divider 上会怎样?
- 鼠标一旦移动太快
- 光标离开 divider 区域
- 拖拽立刻失效
👉 所以正确做法是绑定到 document,确保鼠标在页面任何地方都能继续拖拽。
四、防止宽度“拖炸”的边界控制
拖拽时一定要做 最大 / 最小宽度限制,否则:
- 左边会被拖成负数
- 或者直接盖住右侧
这里用了一个非常关键的方法:
var setLeftContainerWidth = function (width) {
var contentWidth = $content.width();
var dividerWidth = $divider.width();
var maxLeftWidth = contentWidth - dividerWidth;
width = Math.min(width, maxLeftWidth);
$left.width(width);
};
这里做了什么?
- 最大宽度 = 父容器宽度 - 分割线宽度
- 使用
Math.min防止超过最大值
👉 这一步是拖拽体验是否“丝滑”的关键点之一
五、为什么要加 body.dragging 这个状态?
当拖拽开始时:
$body.addClass("dragging");
结束时:
$body.removeClass("dragging");
对应 CSS:
body.dragging {
cursor: col-resize;
}
body.dragging .left-container,
body.dragging .right-container {
pointer-events: none;
}
这一步解决了什么问题?
- 统一鼠标样式
- 防止 iframe / 内部元素抢占鼠标事件
- 避免拖拽中断(尤其是老项目、复杂页面)
👉 这是很多“看起来能拖,但偶尔会断”的根本解决方案
六、双击 / ESC / 按钮恢复布局的设计思路
为了让体验更好,这里加了几个「快捷恢复」方式:
1️⃣ 点击中间手柄,恢复 50%
recoverWidth();
function recoverWidth() {
$left.width($content.width() / 2);
}
2️⃣ 按 ESC 键恢复
$document.on("keydown", function (e) {
if (e.key === "Escape") {
recoverWidth();
}
});
3️⃣ 左右双箭头一键全屏
setLeftContainerWidth(0); // 左边隐藏
setLeftContainerWidth($content.width()); // 左边全屏
并且配合 CSS 动画:
.left-container.animating {
transition: width 0.4s ease;
}
👉 这让“程序员工具类页面”的体验直接上一个档次
七、实现这个功能的几个核心关键点(必看)
如果你只记住下面 5 条,也能自己写出来 👇
- 布局用 flex,只改左侧宽度
- mousemove / mouseup 一定绑定 document
- 拖拽时要保存起始 X 和初始宽度
- 必须做最大 / 最小宽度限制
- 拖拽中禁用 pointer-events,防止事件丢失
最后
这个方案虽然用了 jQuery,但实现思路是完全通用的:
- 原生 JS
- Vue / React
- 甚至桌面端 WebView
只要你理解了「拖拽本质是计算位移」,这个效果你以后随手就能写出来。