LinDaiDai/niubility-coding-js

⛺️第2期第4题:实现一个拖拽(兼容写法)

Opened this issue · 3 comments

实现一个拖拽(兼容写法)

考察知识点

  1. event的兼容性
  • 其它浏览器window.event
  • 火狐下没有window.event,所以用传入的参数ev代替
  • 最终写法:var oEvent = ev || window.event
  1. 实现拖拽的事件有哪些(box为需要拖拽的元素)
  • box.onmousedown
  • document.onmousemove
  • box.onmouseup
  1. 实现的事件顺序
  • 首先监听box.onmousedown,即鼠标按下box时触发的事件,记录下鼠标按下时距离屏幕上边和左边的距离,以及box距离屏幕上边和左边的距离,再用前者减去后者得到差值distanceXdistanceY
  • 然后在此事件中监听document.onmousemove事件,记录下每次鼠标移动时距离屏幕上边和左边的距离,然后用它们减去distanceXdistanceY,再将其赋值给boxlefttop,使其能跟着鼠标移动
  • 不过需要考虑box距离屏幕最上面/下面/左边/右边的边界情况
  • box.onmouseup的时候需要将document.onmousemove事件设置为null

如图所示:

Coding

css

<style>
  html, body {
    margin: 0;
    height: 100%;
  }
  #box {
    width: 100px;
    height: 100px;
    background-color: red;
    position: absolute;
    top: 100px;
    left: 100px;
  }
</style>

html

<div id="box"></div>

javascript

window.onload = function () {
  var box = document.getElementById('box');
  box.onmousedown = function (ev) {
    var oEvent = ev || window.event; // 兼容火狐,火狐下没有window.event
    var distanceX = oEvent.clientX - box.offsetLeft; // 鼠标到可视区左边的距离 - box到页面左边的距离
    var distanceY = oEvent.clientY - box.offsetTop;
    document.onmousemove = function (ev) {
      var oEvent = ev || window.event;
      var left = oEvent.clientX - distanceX;
      var top = oEvent.clientY - distanceY;
      if (left <= 0) {
        left = 0;
      } else if (left >= document.documentElement.clientWidth - box.offsetWidth) {
        left = document.documentElement.clientWidth - box.offsetWidth;
      }
      if (top <= 0) {
        top = 0;
      } else if (top >= document.documentElement.clientHeight - box.offsetHeight) {
        top = document.documentElement.clientHeight - box.offsetHeight;
      }
      box.style.left = left + 'px';
      box.style.top = top + 'px';
    }
    box.onmouseup = function () {
      document.onmousemove = null;
      box.onmouseup = null;
    }
  }
}

box.onmouseup = function () { document.onmousemove = null; box.onmouseup = null;}
呆呆大佬,这里的mouseup事件的监听对象改成document会不会比较好呢?
如果是box的话,当鼠标超出box的范围,就无法监听到该事件并触发监听函数了;
场景是当按下鼠标拖拽box至超出页面范围(进入浏览器顶部菜单栏等等)并释放鼠标,然后再回到box中,会发现虽然鼠标处在释放的状态,但仍然可以拖拽box;
原因是鼠标并不是在box内部抬起的,所以并没有触发box的mouseup事件的监听函数,所以document的mousemove事件并没有移除。

box.onmouseup = function () { document.onmousemove = null; box.onmouseup = null;}
呆呆大佬,这里的mouseup事件的监听对象改成document会不会比较好呢?
如果是box的话,当鼠标超出box的范围,就无法监听到该事件并触发监听函数了;
场景是当按下鼠标拖拽box至超出页面范围(进入浏览器顶部菜单栏等等)并释放鼠标,然后再回到box中,会发现虽然鼠标处在释放的状态,但仍然可以拖拽box;
原因是鼠标并不是在box内部抬起的,所以并没有触发box的mouseup事件的监听函数,所以document的mousemove事件并没有移除。

是的,感谢指出,我去本地试了确实这样效果会好一些,我修改一下。再次感谢😄

部分逻辑做了优化处理,整体没啥变化。

window.onload = function() {
  const $box = document.querySelector("#box");
  $box.addEventListener("mousedown", function onMouseDown(ev) {
    const { event = ev } = window;
    const distanceX = event.clientX - $box.offsetLeft;
    const distanceY = event.clientY - $box.offsetTop;

    document.addEventListener("mousemove", onMouseMove);
    function onMouseMove(eve) {
      const docEvent = eve || window.event;
      const left = inRange(
        docEvent.clientX - distanceX,
        0,
        document.documentElement.clientWidth - $box.offsetWidth
      );
      const top = inRange(
        docEvent.clientY - distanceY,
        0,
        document.documentElement.clientHeight - $box.offsetHeight
      );

      $box.style.left = `${left}px`;
      $box.style.top = `${top}px`;
    }

    $box.addEventListener("mouseup", onMouseUp);
    function onMouseUp() {
      document.removeEventListener("mousemove", onMouseMove);
      $box.removeEventListener("mouseup", onMouseUp);
    }
  });
};

function inRange(num, start, end) {
  let ret = num;
  ret = Math.max(ret, start);
  ret = Math.min(ret, end);

  return ret;
}