如何在父 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 并调整其大小而不溢出? 的相关文章

  • Bootstrap按钮加载+Ajax

    我正在使用 Twitter Bootstrap 的按钮加载状态 http twitter github com bootstrap javascript html buttons http twitter github com bootst
  • 有没有办法使用 Rspec/Capybara/Selenium 将 javascript console.errors 打印到终端?

    当我运行 rspec 时 是否可以让 capybara selenium 向 rspec 报告任何 javascript console errors 和其他异常 我有一大堆测试失败 但当我手动测试它时 我的应用程序正在运行 如果不知道仅在
  • 如何选择具有“A”类但不具有“B”类的 div?

    我有一些 div div class A Target div div class A B NotMyTarget div div class A C NotMyTarget div div class A D NotMyTarget di
  • php - 解析html页面

    div divbox div p para1 p p para2 p p para3 p table class table tr td td tr table p para4 p p para5 p 有人可以告诉我如何解析这个 html
  • 使用 CSS 使一行 div 高度相同

    我有一排必须具有相同高度的 div 但我无法提前知道该高度可能是多少 内容来自外部源 我最初尝试将 div 放置在封闭的 div 中并将它们向左浮动 然后我将它们的高度设置为 100 但这没有明显的效果 通过将封闭 div 的高度设置为固定
  • 网站的主体和元素固定在 980px 宽度上,不会缩小

    我试图在 Rails 应用程序顶部启动前端 仅 HTML CSS 页面 但在使用 320px 视口时遇到问题 有些元素不会按比例缩小 我不明白为什么 我已经完成了检查元素 为各种元素提供了max width 100 and or width
  • 检查 jQuery 1.7 中是否存在基于文本的选择选项

    所以我有以下 HTML 片段
  • 页面上使用 HTML Editor Extender 进行回发会导致 IE11 中出现 JavaScript 错误

    我已将 HTML 编辑器扩展程序添加到我正在处理的页面中 现在每当我在页面上发回帖子时 都会收到以下 Javascript 错误 JavaScript 运行时错误 参数无效 之后什么也没有发生 这在 IE10 或更低版本以及我所知道的所有其
  • Firebase 函数 onWrite 未被调用

    我正在尝试使用 Firebase 函数实现一个触发器 该触发器会复制数据库中的一些数据 我想观看所有添加的内容votes user vote 结构为 我尝试的代码是 const functions require firebase func
  • CSS3 信封形状

    正如您可能已经猜到的 该图像是邮件信封形状的一部分 如果可能的话 我想使用 CSS3 创建该形状 我已经制作了其他部分 但这个很棘手 该形状需要两侧都有三角形切口和圆角 大概是 border radius bottom left borde
  • 正则表达式 - 从 markdown 字符串中提取所有标题

    我在用灰质 https www npmjs com package gray matter 以便将文件系统中的 MD 文件解析为字符串 解析器产生的结果是这样的字符串 n Clean er ReactJS Code Conditional
  • 在移动设备上滚动

    这个问题更多的是一个建议研究 我确实希望它对其他人有帮助 并且它不会关闭 因为我不太确定在哪里寻求有关此事的建议 在过去的 6 个月里 我一直在进行移动开发 我有机会处理各种设备上的各种情况和错误 最麻烦的是滚动问题 当涉及到在网站的多个区
  • 在 HTML 下拉列表中有一个滚动条

    我正在寻找一种在 HTML 的下拉列表中添加滚动条的方法 这样如果下拉列表包含的内容超过例如 5 项 将出现滚动条以查看其余项 这是因为我将被迫列出一些大清单 过去几个小时我一直在谷歌上搜索它 但没有运气 它需要适用于 IE8 FF 和 C
  • 使用css bootstrap时如何仅向一列添加右边框?

    我正在尝试使用CSS引导框架 http getbootstrap com css tables在我的项目中 我正在使用带有以下类的表table table bordered table striped 我想删除除第一列之外的所有列的边框 这
  • 表格行未扩展到全宽

    我有一个表格 当我将表格的宽度设置为 100 并将表格行的宽度设置为 100 时 没有任何反应或宽度发生变化 Table Normal position relative display block margin 10px auto pad
  • 在 JavaScript 循环之外声明变量可以提高速度和内存?

    C 也有类似的问题 但我们没有看到 JavaScript 的任何问题 在循环内声明变量是否可以接受 假设循环有 200 次迭代 使用样本 2 相对于样本 1 是否有性能要求 内存和速度 我们使用 jQuery 来循环 它提高了我们将 var
  • 使用 MongoDB 和 Nodejs 插入和查询日期

    我需要一些帮助在 mongodb 和 nodejs 中按日期查找记录 我将日期添加到抓取脚本中的 json 对象 如下所示 jsonObj last updated new Date 该对象被插入到 mongodb 中 我可以看到如下 la
  • Jquery - 选择选项后如何获取选项的特定数据类型?

    我将直接跳到标记 然后解释我想要做什么 HTML 选择选项
  • 如何在执行新操作时取消先前操作的执行?

    我有一个动作创建器 它会进行昂贵的计算 并在每次用户输入内容时调度一个动作 基本上是实时更新 但是 如果用户输入多个内容 我不希望之前昂贵的计算完全运行 理想情况下 我希望能够取消执行先前的计算并只执行当前的计算 没有内置功能可以取消Pro
  • 在 Nexus 7 2013 上更改方向时 CSS 媒体查询不起作用

    我目前正在我的笔记本电脑 台式电脑和 Nexus 7 2013 上测试 CSS 媒体查询 除了 Nexus 7 之外 它们在台式机和笔记本电脑上都运行良好 当我更改方向时 除非刷新页面 否则样式不会应用 例如 以纵向模式握住设备时 页面正常

随机推荐