讲师中心 微信公众号
AI工具推荐 视频效率加速

JavaScript实现可拖拽、可调整大小并限制在父容器内的DIV组件

风丽同学_2223

风丽同学_2223

发布时间:2025-12-14 20:11:03

|

776人浏览过

|

来源于php中文网

原创

javascript实现可拖拽、可调整大小并限制在父容器内的div组件

本教程详细介绍了如何使用纯JavaScript实现可拖拽和可调整大小的DIV组件,并确保这些组件在父容器内部活动,不会溢出边界。文章将涵盖HTML结构、CSS样式以及核心JavaScript逻辑,包括事件处理、位置计算、尺寸调整以及关键的边界限制机制,旨在帮助开发者构建功能丰富的交互式用户界面。

在现代Web应用中,创建可交互、用户友好的界面至关重要。其中,允许用户自由拖动和调整页面元素(如窗口、面板)大小的功能非常常见。本教程将指导您如何使用原生JavaScript实现这一功能,并特别强调如何将这些可操作元素限制在一个指定的父容器内,防止它们溢出。

核心概念

实现可拖拽和可调整大小的DIV组件主要涉及以下几个核心概念:

  1. 事件监听: 利用mousedown、mousemove和mouseup事件来捕获用户的交互行为。
  2. 绝对定位: 可拖拽元素必须使用position: absolute;或position: fixed;,以便通过JavaScript动态改变其left和top属性来移动,以及width和height属性来调整大小。
  3. 坐标计算: 根据鼠标的移动距离计算元素的新位置和新尺寸。
  4. 边界限制: 在计算新位置和新尺寸时,加入逻辑判断,确保元素始终位于父容器的可见区域内。
  5. Z-index管理: 当多个可拖拽元素重叠时,通过调整z-index确保当前操作的元素始终位于最上层。

HTML结构

首先,我们需要一个父容器来限制可拖拽元素的活动范围,以及一个或多个可拖拽元素。每个可拖拽元素内部应包含一个用于拖动的“把手”(move handle)和一个用于调整大小的“把手”(resize handle)。

立即学习“Java免费学习笔记(深入)”;

<div class="container">
  <div class="draggable">
    <div class="move">可拖拽把手</div>
    非拖拽内容
    <div class="resize"></div>
  </div>
  <div class="draggable" style="left:230px;">
    <div class="move">可拖拽把手</div>
    非拖拽内容
    <div class="resize"></div>
  </div>
</div>
  • .container:作为所有.draggable元素的父容器,限制它们的活动范围。
  • .draggable:表示一个可拖拽和可调整大小的组件。
  • .move:组件内部的拖动区域,用户在此区域按下鼠标并拖动时,整个组件会移动。
  • .resize:组件右下角的调整大小区域,用户在此区域按下鼠标并拖动时,组件会改变尺寸。

CSS样式

CSS负责设置元素的初始位置、尺寸、样式以及把手的视觉表现。关键在于将.draggable元素设置为绝对定位,并为.move和.resize把手提供明确的交互区域和光标样式。

html,body{
  height:100%;
  margin:0;
  padding:0;
}
*{
  box-sizing: border-box; /* 确保padding和border不增加元素总尺寸 */
}

.draggable{
  position: absolute; /* 绝对定位,便于JS控制位置 */
  padding:45px 15px 15px 15px; /* 为内容区留出空间 */
  border-radius:4px;
  background:#ddd;
  user-select: none; /* 防止拖动时选中文字 */
  left: 15px;
  top: 15px;
  min-width:200px; /* 最小宽度 */
  /* min-height可以根据需要添加 */
}
.draggable>.move{
  line-height: 30px;
  padding: 0 15px;
  background:#bbb;
  border-bottom: 1px solid #777;
  cursor:move; /* 拖动光标 */
  position:absolute;
  left:0;
  top:0;
  height:30px;
  width:100%;
  border-radius: 4px 4px 0;
}
.draggable>.resize{
  cursor:nw-resize; /* 调整大小光标 */
  position:absolute;
  right:0;
  bottom:0;
  height:16px;
  width:16px;
  border-radius: 0 0 4px 0;
  background: linear-gradient(to left top, #777 50%, transparent 50%) /* 调整大小把手的视觉效果 */
}
.container{
  left:15px;
  top:15px;
  background: #111;
  border-radius:4px;
  width:calc(100% - 30px); /* 占据视口大部分宽度 */
  height:calc(100% - 30px); /* 占据视口大部分高度 */
  position: relative; /* 确保绝对定位的子元素相对于它定位 */
  overflow: hidden; /* 隐藏超出容器的内容,虽然JS会限制,但作为安全措施 */
}

JavaScript逻辑

JavaScript是实现拖拽和调整大小功能的核心。我们将创建一个makeDraggableResizable函数,封装所有逻辑,并将其应用于每个.draggable元素。

1. 初始化和Z-index管理

首先获取容器和所有可拖拽元素,并为每个元素调用makeDraggableResizable函数。同时,为确保被点击的元素总是在最上层,添加mousedown事件来更新z-index。

const container = document.querySelector('.container');
const draggables = document.querySelectorAll('.draggable');

draggables.forEach(elem => {
  makeDraggableResizable(elem); // 为每个可拖拽元素启用功能
  elem.addEventListener('mousedown', () => {
     // 获取当前所有可拖拽元素中最大的z-index
     const maxZ = Math.max(...[...draggables].map(elem => parseInt(getComputedStyle(elem)['z-index']) || 0));
     // 将当前点击元素的z-index设置为最大值+1,使其置顶
     elem.style['z-index'] = maxZ + 1;
  });
});

2. makeDraggableResizable函数详解

这个函数接收一个可拖拽元素作为参数,并为其添加拖动和调整大小的功能。

function makeDraggableResizable(draggable){

  // 辅助函数:移动元素
  const move = (x, y) => {
    // 计算新的left和top值
    x = state.fromX + (x - state.startX);
    y = state.fromY + (y - state.startY);

    // 移动边界限制
    if (x < 0) x = 0; // 左边界
    else if (x + draggable.offsetWidth > container.offsetWidth) x = container.offsetWidth - draggable.offsetWidth; // 右边界

    if (y < 0) y = 0; // 上边界
    else if (y + draggable.offsetHeight > container.offsetHeight) y = container.offsetHeight - draggable.offsetHeight; // 下边界

    draggable.style.left = x + 'px';
    draggable.style.top = y + 'px';
  };

  // 辅助函数:调整元素大小
  const resize = (x, y) => {
    // 计算新的width和height值
    x = state.fromWidth + (x - state.startX);
    y = state.fromHeight + (y - state.startY);

    // 调整大小边界限制
    // 当元素右边缘超出容器右边缘时,限制宽度
    if (state.fromX + x > container.offsetWidth) x = container.offsetWidth - state.fromX;
    // 当元素下边缘超出容器下边缘时,限制高度
    if (state.fromY + y > container.offsetHeight ) y = container.offsetHeight - state.fromY;

    // 考虑min-width和min-height,虽然CSS已设置,JS层面也可以加强
    // x = Math.max(x, parseInt(getComputedStyle(draggable).minWidth));
    // y = Math.max(y, parseInt(getComputedStyle(draggable).minHeight));

    draggable.style.width = x + 'px';
    draggable.style.height = y + 'px';
  };

  // 辅助函数:统一添加/移除全局事件监听器
  const listen = (op = 'add') =>
    Object.entries(listeners).slice(1) // 排除mousedown,因为它只在把手上监听
      .forEach(([name, listener]) => document[op + 'EventListener'](name, listener));

  // 使用Proxy管理状态,实现响应式更新和事件序列控制
  const state = new Proxy({}, {
    set(state, prop, val){
      const out = Reflect.set(...arguments); // 执行默认的属性设置
      const ops = {
        // 当startY被设置时,表示拖拽/调整大小开始
        startY: () => {
          listen(); // 添加mousemove和mouseup事件监听
          const style = getComputedStyle(draggable);
          // 记录元素初始位置和尺寸
          [state.fromX, state.fromY] = [parseInt(style.left), parseInt(style.top)];
          [state.fromWidth, state.fromHeight] = [parseInt(style.width), parseInt(style.height)];
        },
        // 当dragY被设置时(即mousemove事件触发),执行当前动作(move或resize)
        dragY: () => state.action(state.dragX, state.dragY),
        // 当stopY被设置时(即mouseup事件触发),移除mousemove和mouseup事件监听,并执行最终动作
        stopY: () => listen('remove') + state.action(state.stopX, state.stopY),
      };
      // 使用Promise.resolve().then()将操作推迟到微任务队列,确保状态更新的顺序不影响逻辑执行
      ops[prop] && Promise.resolve().then(ops[prop]);
      return out;
    }
  });

  // 全局事件监听器定义,用于捕获鼠标移动和抬起事件
  const listeners = {
    mousedown: e => Object.assign(state, {startX: e.pageX, startY: e.pageY}), // 记录鼠标初始位置
    mousemove: e => Object.assign(state, {dragY: e.pageY, dragX: e.pageX}), // 记录鼠标当前位置
    mouseup: e => Object.assign(state, {stopX: e.pageX, stopY: e.pageY}), // 记录鼠标抬起位置
  };

  // 为move和resize把手添加mousedown事件监听
  for(const [name, action] of Object.entries({move, resize})){
    draggable.querySelector(`.${name}`).addEventListener('mousedown', e => {
      state.action = action; // 设置当前要执行的动作(move或resize)
      listeners.mousedown(e); // 触发mousedown事件的初始状态设置
      e.stopPropagation(); // 阻止事件冒泡,防止父容器或其他元素也响应mousedown
    });
  }
}

3. 关键逻辑点解析

  • move(x, y)函数:

    • x = state.fromX + (x - state.startX);:计算新的left值。state.fromX是元素拖动前的初始left值,(x - state.startX)是鼠标从点击到当前位置的水平位移。
    • 边界检查:
      • if (x
      • else if (x + draggable.offsetWidth > container.offsetWidth) x = container.offsetWidth - draggable.offsetWidth;:如果元素右边缘超出容器右边缘,将其left调整为刚好贴合容器右边缘。
    • 垂直方向的y值计算和边界检查同理。
  • resize(x, y)函数:

    • x = state.fromWidth + (x - state.startX);:计算新的width值。state.fromWidth是元素调整大小前的初始宽度,(x - state.startX)是鼠标从点击到当前位置的水平位移。
    • 边界检查:
      • if (state.fromX + x > container.offsetWidth) x = container.offsetWidth - state.fromX;:如果元素新的右边缘(初始left + 新width)超出容器右边缘,则限制其宽度,使其右边缘刚好贴合容器右边缘。
    • 垂直方向的y值计算和边界检查同理。
    • 这里没有显式限制最小宽度/高度,但可以通过Math.max(x, minWidth)等方式添加。
  • state对象与Proxy:

    • Proxy在这里作为一个状态管理器。当state对象的特定属性(如startY, dragY, stopY)被修改时,Proxy的set陷阱会被触发。
    • 通过这种方式,可以集中管理拖拽/调整大小的生命周期:
      • startY被设置时:初始化拖拽/调整大小的起始状态(记录元素初始位置和尺寸),并开始监听全局mousemove和mouseup事件。
      • dragY被设置时(在mousemove中):执行实际的move或resize操作。
      • stopY被设置时(在mouseup中):执行最终的move或resize操作,并移除全局事件监听器。
    • Promise.resolve().then(ops[prop]);:这是一个巧妙的技巧,将状态更新后的操作推迟到当前事件循环的微任务队列中。这确保了state属性的所有设置(如startX, startY)都在ops[prop]执行之前完成,避免了因属性设置顺序导致的潜在问题。
  • 事件监听器的管理:

    • listen()函数负责在拖拽/调整大小开始时,将mousemove和mouseup事件监听器添加到document上。
    • 在拖拽/调整大小结束时,listen('remove')会移除这些全局监听器。这样做可以确保即使鼠标移出元素范围,也能正确捕获mouseup事件,终止操作。

总结与注意事项

通过上述HTML、CSS和JavaScript代码,您已经实现了一个功能完善的、可拖拽、可调整大小并限制在父容器内的DIV组件。

注意事项:

  1. 性能优化: 对于页面上大量可拖拽元素,频繁的DOM操作可能会影响性能。可以考虑使用requestAnimationFrame来批量更新DOM,或者使用CSS transform属性进行位移,因为它通常能获得更好的硬件加速性能。
  2. 兼容性: Proxy对象是ES6特性,在较旧的浏览器中可能不被支持。如果需要兼容IE等老旧浏览器,可能需要使用传统的事件监听和变量管理方式。
  3. 可访问性: 考虑为残障用户提供键盘操作支持,以便他们也能移动和调整元素。
  4. 用户体验:
    • 可以添加“吸附网格”功能,使元素在拖动时能对齐到预设的网格线上。
    • 添加动画效果,使元素的移动和调整大小更加平滑。
    • 在拖动或调整大小过程中,可以改变元素的透明度或添加边框高亮,提供更强的视觉反馈。
  5. 多点触控: 本教程主要针对鼠标事件。若要支持触摸屏设备,需要额外处理touchstart、touchmove和touchend等触控事件。

掌握这些技术,您就可以在Web应用中创建出高度交互性和自定义性的用户界面。

热门AI工具

更多
Seko
Seko Hot

一款AI视频创作工具,主要用于商汤科技推出的创编一体的AI短视频创作Agent,适合需要提升相关任务效率的用户。

UpDream
UpDream Hot

一款AI视频创作工具,主要用于哔哩哔哩推出的自研AI视频创作工具,适合需要提升相关任务效率的用户。

讯飞智作

讯飞智作是一款AI视频创作工具,AI文本配音工具,数字人课程、营销视频制作。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

立刻MV
立刻MV Hot

立刻MV是一款AI文本写作工具,AI 音乐视频(MV)创作工具。

二狗PPT
二狗PPT Hot

一款AI演示文稿工具,主要用于专为中式职场打造的AI PPT生成工具,适合需要提升相关任务效率的用户。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

UP简历
UP简历 Hot

一款AI办公效率工具,主要用于基于AI技术的免费在线简历制作工具,适合需要提升相关任务效率的用户。

相关专题

更多
es6新特性
es6新特性

es6新特性有:1、块级作用域变量;2、箭头函数;3、模板字符串;4、解构赋值;5、默认参数;6、 扩展运算符;7、 类和继承;8、Promise。本专题为大家提供es6新特性的相关的文章、下载、课程内容,供大家免费下载体验。

285

2023.07.17

es6新特性有哪些
es6新特性有哪些

es6的新特性有:1、块级作用域;2、箭头函数;3、解构赋值;4、默认参数;5、扩展运算符;6、模板字符串;7、类和模块;8、迭代器和生成器;9、Promise对象;10、模块化导入和导出等等。本专题为大家提供es6新特性的相关的文章、下载、课程内容,供大家免费下载体验。

406

2023.08.04

JavaScript ES6新特性
JavaScript ES6新特性

ES6是JavaScript的根本性升级,引入let/const实现块级作用域、箭头函数解决this绑定问题、解构赋值与模板字符串简化数据处理、对象简写与模块化提升代码可读性与组织性。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

375

2025.12.24

DOM是什么意思
DOM是什么意思

dom的英文全称是documentobjectmodel,表示文件对象模型,是w3c组织推荐的处理可扩展置标语言的标准编程接口;dom是html文档的内存中对象表示,它提供了使用javascript与网页交互的方式。想了解更多的相关内容,可以阅读本专题下面的文章。

6521

2024.08.14

promise的用法
promise的用法

“promise” 是一种用于处理异步操作的编程概念,它可以用来表示一个异步操作的最终结果。Promise 对象有三种状态:pending(进行中)、fulfilled(已成功)和 rejected(已失败)。Promise的用法主要包括构造函数、实例方法(then、catch、finally)和状态转换。

4580

2023.10.12

html文本框类型介绍
html文本框类型介绍

html文本框类型有单行文本框、密码文本框、数字文本框、日期文本框、时间文本框、文件上传文本框、多行文本框等等。详细介绍:1、单行文本框是最常见的文本框类型,用于接受单行文本输入,用户可以在文本框中输入任意文本,例如用户名、密码、电子邮件地址等;2、密码文本框用于接受密码输入,用户在输入密码时,文本框中的内容会被隐藏,以保护用户的隐私;3、数字文本框等等。

4183

2023.10.12

CSS position定位有几种方式
CSS position定位有几种方式

有4种,分别是静态定位、相对定位、绝对定位和固定定位。更多关于CSS position定位有几种方式的内容,可以访问下面的文章。

352

2023.11.23

PHP 高并发与性能优化
PHP 高并发与性能优化

本专题聚焦 PHP 在高并发场景下的性能优化与系统调优,内容涵盖 Nginx 与 PHP-FPM 优化、Opcode 缓存、Redis/Memcached 应用、异步任务队列、数据库优化、代码性能分析与瓶颈排查。通过实战案例(如高并发接口优化、缓存系统设计、秒杀活动实现),帮助学习者掌握 构建高性能PHP后端系统的核心能力。

18073

2025.10.16

Gradle插件开发入门教程
Gradle插件开发入门教程

本专题介绍Gradle插件开发的基本流程,涵盖插件类编写、自定义任务注册、扩展参数设计和插件应用方法,帮助你理解插件如何组织构建逻辑,减少多个项目之间的重复配置。

60

2026.10.22

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Sass 教程
Sass 教程

共14课时 | 1.5万人学习

Bootstrap 5教程
Bootstrap 5教程

共46课时 | 6.1万人学习

CSS教程
CSS教程

共754课时 | 98.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn