Skip to content

clientX/clientY

鼠标位置相对于当前可视区域的的坐标(不包括已经滚动的)

screenX/screenY

鼠标相对于电脑显示器屏幕的位置

offsetX/offsetY

鼠标位置相对于当前触发事件的块级元素左上角的值(包含边框), 当时 Safari 浏览器不包含

pageX/pageY

鼠标位置相对于当前文档左上角的坐标(包括已经滚动的)

javascript
function getScrollOffset() {
  if (window.pageXOffset) {
    getScrollOffset = function () {
      return {
        top: window.pageXOffset,
        left: window.pageYOffset,
      };
    };
  } else {
    getScrollOffset = function () {
      return {
        top: document.body.scrollTop + document.documentElement.scrollTop,
        left: document.body.scrollLeft + document.documentElement.scrollLeft,
      };
    };
  }
  return getScrollOffset();
}

// 请使用这个封装的方法, 让pageX/Y兼容更多的浏览器
function getPagePosition(e) {
  var scrollPos = getScrollOffset(),
    clientLeft = document.documentElement.clientLeft || 0,
    clientTop = document.documentElement.clientTop || 0;
  return {
    x: e.clientX + scrollPos.left - clientLeft,
    y: e.clientY + scrollPos.top - clientTop,
  };
}

layerX/layerY(不推荐使用)

这个其实就是 pageX/Y IE11 及其以下版本, 它的值是 clientX/Y的值

x/y(不推荐使用)

这个其实就是 clientXclientY 但是 低版本的浏览器可能有兼容性问题, 所以推荐使用 clientX/Y

拖拽方法封装(兼容 IE)

index.html

html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>js</title>
    <style>
      #app {
        width: 200px;
        height: 200px;
        background: #000;
      }
    </style>
  </head>
  <body>
    <div id="app"></div>
    <script src="./js/drag.js"></script>
    <script>
      var app = document.getElementById("app");
      drag({
        el: app,
      });
    </script>
  </body>
</html>

js/drag.js

javascript
(function () {
  /**
   * get pageXOffset
   * @returns {Object: {top, left}}
   */
  function getScrollOffset() {
    if (window.pageXOffset) {
      getScrollOffset = function () {
        return {
          top: window.pageXOffset,
          left: window.pageYOffset,
        };
      };
    } else {
      getScrollOffset = function () {
        return {
          top: document.body.scrollTop + document.documentElement.scrollTop,
          left: document.body.scrollLeft + document.documentElement.scrollLeft,
        };
      };
    }
    return getScrollOffset();
  }

  /**
   * 兼容: 取消冒泡
   * @param {Event} e
   * @returns
   */
  function cancelBubble(e) {
    if (e.stopPropagation) {
      cancelBubble = function (e) {
        e = e || window.event;
        return e.stopPropagation();
      };
    } else {
      cancelBubble = function (e) {
        e = e || window.event; // window.event 兼容ie8及其以下版本的浏览器
        e.cancelBubble = true;
      };
    }

    return cancelBubble(e);
  }

  /**
   * 兼容: 取消浏览器默认事件
   * @param {Event} e
   * @returns
   */
  function preventDefault(e) {
    var e = e || widow.event;
    if (e.preventDefault) {
      // w3c standard
      preventDefault = function (e) {
        e.preventDefault(e);
      };
    } else {
      // ie: 9/8/7/6
      preventDefault = function (e) {
        e = e || widow.event;
        e.returnValue = false;
      };
    }
    return preventDefault(e);
  }

  /**
   * get PageX
   * @param {Event} e event object
   * @returns {Object: {x, y}}
   */
  function getPagePosition(e) {
    var scrollPos = getScrollOffset(),
      clientLeft = document.documentElement.clientLeft || 0,
      clientTop = document.documentElement.clientTop || 0;
    return {
      x: e.clientX + scrollPos.left - clientLeft,
      y: e.clientY + scrollPos.top - clientTop,
    };
  }

  /**
   * 兼容: 添加事件处理函数
   * @param {HTMLElement} el 事件源
   * @param {String} type 事件类型
   * @param {Function} handler
   * @returns
   */
  function addEvent(el, type, handler) {
    if (el.addEventListener) {
      addEvent = function (el, type, handler) {
        el.addEventListener(type, handler, false);
      };
    } else if (el.attachEvent) {
      addEvent = function (el, type, handler) {
        el.attachEvent("on" + type, function () {
          handler.call(el);
        });
      };
    } else {
      addEvent = function (el, type, handler) {
        el["on" + type] = handler;
      };
    }
    return addEvent(el, type, handler); // 只有这一次执行会判断
  }

  /**
   * 兼容: 移除事件处理函数
   * @param {HTMLElement} el 事件源
   * @param {String} type 事件类型
   * @returns
   */
  function removeEvent(el, type, handler) {
    if (el.removeEventListener) {
      removeEvent = function (el, type, handler) {
        el.removeEventListener(type, handler);
      };
    } else if (el.detachEvent) {
      removeEvent = function (el, type, handler) {
        el.detachEvent("on" + type, handler);
      };
    } else {
      removeEvent = function (el, type, handler) {
        el["on" + type] = null;
      };
    }
    return removeEvent(el, type, handler);
  }

  /**
   * 兼容: 获取可视区域大小
   * @returns {Object:{width, height}}
   */
  function getViewportSize() {
    if (window.innerWidth) {
      getViewportSize = function () {
        return {
          width: window.innerWidth,
          height: window.innerHeight,
        };
      };
    } else {
      getViewportSize = function () {
        // ie
        var key = document.compatMode === "BackCompat" ? "body" : "documentElement";
        return {
          width: document[key].clientWidth,
          height: document[key].clientHeight,
        };
      };
    }
    return getViewportSize();
  }

  /**
   * 让一个元素可以拖动
   * @param {object} options 配置选项
   * @param {HTMLElement} options.el: 需要被拖动的元素
   * @param {Boolean} options.xMoveable: 水平方向是否可以拖动 default: true
   * @param {Boolean} options.yMoveable: 垂直方向是否可以拖动 default: true
   * @param {Boolean} options.moveOutside: 是否限制移动边界 default: true
   */
  function drag(options) {
    if (!options || Object.prototype.toString.call(options) !== "[object Object]") {
      throw new TypeError("the options must be a object");
    }

    var el = options.el,
      offsetX,
      offsetY,
      maxWidth,
      maxHeight,
      xMoveable,
      yMoveable,
      moveOutside;
    if (!el || el.nodeType !== Node.ELEMENT_NODE) {
      throw new TypeError("The 'el' must be a HTMLElement");
    }
    xMoveable = options.xMoveable === false ? false : true;
    yMoveable = options.yMoveable === false ? false : true;
    moveOutside = options.moveOutside === false ? false : true;

    // mouse down
    var handleMouseDown = function (e) {
      var e = e || window.event;
      var target = e.target || e.srcElement;
      offsetX = e.offsetX;
      offsetY = e.offsetY;
      maxWidth = window.innerWidth - target.offsetWidth - 1;
      maxHeight = window.innerHeight - target.offsetHeight - 1;
      addEvent(document, "mousemove", handleMouseMove);
      addEvent(el, "mouseup", handleMouseUp);
    };

    // mouse move
    var handleMouseMove = function (e) {
      var e = e || window.event;
      var mousePos = getPagePosition(e);
      var left, top;
      el.style.position = "fixed";
      if (xMoveable) {
        left = mousePos.x - offsetX;
        if (moveOutside) {
          left = left >= maxWidth ? maxWidth : left;
          left = left <= 0 ? 0 : left;
        }
        el.style.left = left + "px";
      }
      if (yMoveable) {
        top = mousePos.y - offsetY;
        if (moveOutside) {
          top = top >= maxHeight ? maxHeight : top;
          top = top <= 0 ? 0 : top;
        }
        el.style.top = top + "px";
      }
      preventDefault(e);
      cancelBubble(e);
    };

    // mouse up
    var handleMouseUp = function (e) {
      var e = e || window.event;
      removeEvent(document, "mousemove", handleMouseMove);
      removeEvent(el, "mouseup", handleMouseUp);
    };

    addEvent(el, "mousedown", handleMouseDown);
  }

  window.drag = drag;
})();

Released under the MIT License.