如何在父 DIV 容器中移动 DIV 并调整其大小而不溢出?

2024-04-06

  let isSelecting = false;
let selectionStartX, selectionStartY, selectionEndX, selectionEndY;
let selectionRectangle;
let draggedElements = [];
const widgets = document.querySelectorAll('.widgets');
const widgetContainer = document.getElementById('widget-container');

document.addEventListener('mousedown', (event) => {
  const header = event.target.closest('.widget-header');
  if (header) {
    const widget = header.parentElement;
    // If the clicked widget is not selected
    if (!widget.classList.contains('selected')) {
      // deselect all widgets 
      widgets.forEach((widget) => {
        widget.classList.remove('selected');
      });
      // and only drag the clicked one
      draggedElements = [widget];
    } else {
      // otherwise drag the selected 
      draggedElements = Array.from(widgets).filter((widget) => widget.classList.contains('selected'));
    }

    draggedElements.forEach((widget) => {
      const shiftX = event.clientX - widget.getBoundingClientRect().left;
      const shiftY = event.clientY - widget.getBoundingClientRect().top;

      function moveElement(event) {
        const x = event.clientX - shiftX;
        const y = event.clientY - shiftY;

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

      function stopMoving() {
        document.removeEventListener('mousemove', moveElement);
        document.removeEventListener('mouseup', stopMoving);
      }

      document.addEventListener('mousemove', moveElement);
      document.addEventListener('mouseup', stopMoving);
    });

    return;
  }

  const isWidgetClicked = Array.from(widgets).some((widget) => {
    const widgetRect = widget.getBoundingClientRect();
    return (
      event.clientX >= widgetRect.left &&
      event.clientX <= widgetRect.right &&
      event.clientY >= widgetRect.top &&
      event.clientY <= widgetRect.bottom
    );
  });

  if (!isWidgetClicked && event.target !== selectionRectangle) { // Check if the target is not the selection rectangle
    isSelecting = true;
    selectionStartX = event.clientX;
    selectionStartY = event.clientY;
    selectionRectangle = document.createElement('div');
    selectionRectangle.id = 'selection-rectangle';
    selectionRectangle.style.position = 'absolute';
    selectionRectangle.style.border = '2px dashed blue';
    selectionRectangle.style.pointerEvents = 'none';
    selectionRectangle.style.display = 'none';
    document.body.appendChild(selectionRectangle);

    // Remove selected class from widgets
    widgets.forEach((widget) => {
      widget.classList.remove('selected');
    });
  }
});

document.addEventListener('mousemove', (event) => {
  if (isSelecting) {
    selectionEndX = event.clientX;
    selectionEndY = event.clientY;

    let width = Math.abs(selectionEndX - selectionStartX);
    let height = Math.abs(selectionEndY - selectionStartY);

    selectionRectangle.style.width = width + 'px';
    selectionRectangle.style.height = height + 'px';
    selectionRectangle.style.left = Math.min(selectionEndX, selectionStartX) + 'px';
    selectionRectangle.style.top = Math.min(selectionEndY, selectionStartY) + 'px';
    selectionRectangle.style.display = 'block';

    widgets.forEach((widget) => {
      const widgetRect = widget.getBoundingClientRect();
      const isIntersecting = isRectangleIntersecting(widgetRect, {
        x: Math.min(selectionStartX, selectionEndX),
        y: Math.min(selectionStartY, selectionEndY),
        width,
        height,
      });
      if (isIntersecting) {
        widget.classList.add('selected');
      } else {
        widget.classList.remove('selected');
      }
    });
  }
});

document.addEventListener('mouseup', () => {
  if (isSelecting) {
    isSelecting = false;
    selectionRectangle.remove();
  }
});

function isRectangleIntersecting(rect1, rect2) {
  return (
    rect1.left >= rect2.x &&
    rect1.top >= rect2.y &&
    rect1.right <= rect2.x + rect2.width &&
    rect1.bottom <= rect2.y + rect2.height
  );
}
  #selection-rectangle {
  position: absolute;
  border: 2px dashed blue;
  pointer-events: none;
  display: none;
  z-index: 9999999;
}

.widgets.selected {
  outline-color: blue;
  outline-width: 2px;
  outline-style: dashed;
}

.widgets {
  position: absolute;
  z-index: 9;
  background-color: #000000;
  color: white;
  font-size: 25px;
  font-family: Arial, Helvetica, sans-serif;
  border: 2px solid #212128;
  text-align: center;
  width: 500px;
  height: 200px;
  min-width: 166px;
  min-height: 66px;
  overflow: hidden;
  resize: both;
  image-resolution: none;
  border-radius: 10px;
}

.widget-header {
  padding: 10px;
  cursor: move;
  z-index: 10;
  background-color: #040c14;
  outline-color: white;
  outline-width: 2px;
  outline-style: solid;
}

#purple-div {
  width: 1000px;
  height: 700px;
  background-color: purple;
  position: absolute;
}
  <div id="widget1" class="widgets" style="left: 50px; top: 50px;">
    <div id="widget1header" class="widget-header"></div>
  </div>

  <div id="widget2" class="widgets" style="left: 150px; top: 150px;">
    <div id="widget2header" class="widget-header"></div>
  </div>

  <div id="widget3" class="widgets" style="left: 250px; top: 250px;">
    <div id="widget3header" class="widget-header"></div>
  </div>

  <div id="purple-div"></div>

我希望小部件受到紫色 div 的限制,但我不知道该怎么做。鼠标还必须始终从开始拖动的位置拖动,这意味着如果它碰到边框,鼠标必须返回到拖动小部件的位置才能移动(当鼠标按下时,这意味着拖动)。


继续我的回答:是否有在多个事件侦听器上执行逻辑的设计模式 https://stackoverflow.com/questions/76420174/is-there-a-design-pattern-for-logic-performed-on-multiple-event-listeners/76420340#76420340

const container = document.querySelector('.container');

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

draggables.forEach(elem => {
  makeDraggableResizable(elem);
  elem.addEventListener('mousedown', () => {
     const maxZ = Math.max(...[...draggables].map(elem => parseInt(getComputedStyle(elem)['z-index']) || 0));
     elem.style['z-index'] = maxZ + 1;
  });
});

function makeDraggableResizable(draggable){

  const move = (x, y) => {

    x = state.fromX + (x - state.startX);
    y = state.fromY + (y - state.startY);

    // don't allow moving outside the container
    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) => {

    x = state.fromWidth + (x - state.startX);
    y = state.fromHeight + (y - state.startY);

    // don't allow moving outside the container
    if (state.fromX + x > container.offsetWidth) x = container.offsetWidth - state.fromX;

    if (state.fromY + y > container.offsetHeight ) y = container.offsetHeight - state.fromY;

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

  const listen = (op = 'add') => 
    Object.entries(listeners).slice(1)
      .forEach(([name, listener]) => document[op + 'EventListener'](name, listener));

  const state = new Proxy({}, {
    set(state, prop, val){
      const out = Reflect.set(...arguments);
      const ops = {
        startY: () => {
          listen();
          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: () => state.action(state.dragX, state.dragY),
        stopY: () => listen('remove') + state.action(state.stopX, state.stopY),
      };
      // use a resolved Promise to postpone the move as a microtask so
      // the order of state mutation isn't important
      ops[prop] && Promise.resolve().then(ops[prop]);
      return out;
    }
  });

  const listeners = {
    mousedown: e => Object.assign(state, {startX: e.pageX, startY: e.pageY}),
    // here we first provide dragY to check that the order of props is not important
    mousemove: e => Object.assign(state, {dragY: e.pageY, dragX: e.pageX}),
    mouseup: e => Object.assign(state, {stopX: e.pageX, stopY: e.pageY}),
  };

  for(const [name, action] of Object.entries({move, resize})){
    draggable.querySelector(`.${name}`).addEventListener('mousedown', e => {
      state.action = action;
      listeners.mousedown(e);
    }); 
  }

}
html,body{
  height:100%;
  margin:0;
  padding:0;
}
*{
  box-sizing: border-box;
}

.draggable{
  position: absolute;
  padding:45px 15px 15px 15px;
  border-radius:4px;
  background:#ddd;
  user-select: none;
  left: 15px;
  top: 15px;
  min-width:200px;
}
.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;
}
<div class="container">
  <div class="draggable"><div class="move">draggable handle</div>Non-draggable content<div class="resize"></div></div>
    <div class="draggable" style="left:230px;"><div class="move">draggable handle</div>Non-draggable content<div class="resize"></div></div>
</div>
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在父 DIV 容器中移动 DIV 并调整其大小而不溢出? 的相关文章

  • 我可以在 Express POST 请求中进行 DOM 操作吗?

    我正在使用基本的 HTML CSS 前端 目前有一个登陆页面 上面有一个表单 可将 一些数据发送到数据库 当请求完成后 它期待某种响应 在这种情况下 我正在重新渲染页面 但是 我想用某种感谢消息替换表单 以便用户知道它已正确发送 我尝试过简
  • 我可以检测焦点来源吗? (Javascript、jQuery)

    快速提问 是否可以检测焦点是来自鼠标单击还是来自焦点事件的选项卡 我想如果没有 我将不得不在同一元素上使用单击句柄来确定源 但我更喜欢通过焦点事件的方式 Thanks Gausie 可能无法 100 工作 但如果没有直接的方法 那么你不能直
  • 最有用的 jQuery 原生 API 函数

    前 5 10 个最常用的 jQuery 本机 API 函数是什么 请不要建议 jQuery 函数本身 因为毫无疑问这是最常用的函数 如果可能的话 还请提供它们所涵盖的场景 提出这个问题的原因是我尝试创建一个类似 jQuery 的 API充足
  • JavaScript 附加和前置与 jQuery 附加和前置

    QA Style 我最近读了一篇文章 里面说JavaScript已经实现了append and prepend受 jQuery 启发的方法 这对我来说是一个新知识 因为据我所知 要附加一个元素 我必须使用element appendChil
  • 在 AMP 中包含自定义 JavaScript 的最佳方式

    我阅读了有关脚本标记的所有文档 但找不到如何在 AMP HTML 中包含自定义 JavaScript 我知道
  • 从右到左的语言和编程问题

    我正在创建一个网络文本编辑器 它使用我创建的新语言 如 BBcode 这种标记语言将采用阿拉伯语 但我面临这些问题 在所有 IDE 和编辑器中将英语和阿拉伯语文本混合在同一行中确实很困难 因为会发生奇怪的事情 单词和字符的顺序发生变化 使用
  • 如何使用startsWith过滤并获取每个对象键的值?

    我试图通过获取每个键来过滤对象checkpoint并输出其值 目前 我只能输出键而不是值 下面 我有一个简单的对象 我正在使用过滤器和startsWith 我怎样才能得到这些值呢 var data practicals 0 checkpoi
  • 重定向后 HTML5 CORS 请求在 safari 中失败

    我正在使用 jQuery 制作 CORS 请求来完成 SSO 类型系统 用户登录 WordPress 同时使用钩子也登录 Moodle 我遇到的问题是 在 Safari 中 仅限 safari 7 当初始 POST 请求设置为 mudles
  • Karma 测试报告运行速度快,但实际上运行速度慢

    最好的解释是a video https youtu be Zwwi01JuPrQ 或参见下面的 gif 您会注意到 Karma 进度报告器报告测试只需要几毫秒 但显然需要相当长的时间 我在推特上提到了这一点 https twitter co
  • 如何将背景颜色(或自定义 css 类)应用于网格中的列 - ExtJs 4?

    看起来应该很简单 但我根本无法完成此任务 我什至不需要动态完成它 例如 假设我有一个简单的 2 列网格设置 如下所示 columns header USER dataIndex firstName width 70 cls red head
  • JavaScript 数组中的空项和未定义项有什么区别? [复制]

    这个问题在这里已经有答案了 考虑以下 JavaScript 代码 在节点 REPL 中 gt let a new Array 10 undefined gt a lt 10 empty items gt gt a map e gt 1 lt
  • Mocha 测试对原生 ES6 模块的“esm”支持

    有一个很棒的帖子 使用 Mocha 和 esm 测试原生 ES 模块 https web archive org web 20220318155753 https alxgbsn co uk 2019 02 22 testing nativ
  • HttpRequest 和 XMLHttpRequest 之间的真正区别

    阅读前注意事项 这不是重复的xmlhttprequest 和 httprequest 之间的区别是什么 https stackoverflow com questions 8499062 what are differences betwe
  • 将纬度/经度转换为 X/Y,以便在美国地图图像上进行阿尔伯斯投影

    我正在尝试使用 C 或 Javascript 将纬度 经度转换为 X Y 坐标 以将带有 CSS 的 div 左 上 定位到美国地图的背景图像上 美国的标准地图投影是阿尔伯斯投影 如下所示 但 StackOverflow 仅提供参考基本墨卡
  • 解析器阻塞与渲染阻塞

    我一直在阅读有关优化网络性能的 Google 开发人员文档 我对那里使用的术语有点困惑 CSS 和 JavaScript 文件都会阻止 DOM 构建 但是 CSS 被称为渲染阻塞 而 JavaScript 被称为解析器阻塞 解析器阻塞 和
  • 如何获取元素相对于当前屏幕位置的偏移量?

    我正在尝试用纯 Javascript 重构所有 jQuery 除了非常具体的值之外 一切都正常工作 根据此代码的浏览器供应商 我得到了不同的值 对于 jQuery 我会使用 var topSelected figure offset top
  • 包括来自raw.github.com的js

    我有一个 github com 演示页面 链接到https raw github com master file js https raw github com master file js这样我就不需要总是复制 js文件转移到gh pag
  • Dojo require,模块加载失败时连接错误

    当我尝试加载不存在的模块时 它失败并出现 404 错误 当然 我想处理此错误 但不知道如何连接到 错误 事件 根据 Dojo 文档 我应该能够使用它的微事件 API http livedocs dojotoolkit org loader
  • JavaScript TypedArray 混合类型

    我正在尝试使用 WebGL 并希望将一些不同类型混合到一个字节缓冲区中 我知道 TypedArrays 可以达到这个目的 但不清楚我是否可以与它们混合类型 OpenGL 顶点数据通常是与无符号字节或整数混合的浮点数 在我的测试中 我想将 2
  • Angular 计算 HTML 中的百分比

    我试图在 HTML 中显示百分比值 如下所示 td myvalue totalvalue 100 td 它可以工作 但有时它会给出一个很长的小数 这看起来很奇怪 如何四舍五入到小数点后两位 有更好的方法吗 您可以使用过滤器 如下所示杰夫约翰

随机推荐