当使用此功能拖动一个元素时,为什么两个元素都会移动



我正在尝试用2个拖动手柄制作一个范围滑块。它仍处于早期阶段,在这一点上,我试图在拖动时将两个手柄独立移动。

但是,我遇到的问题是,如果我将事件处理程序绑定到 document时,当拖动时,两个手柄都会一起移动。如果我通过选择器参数(句柄本身(绑定到传递的元素,则两个句柄都独立移动,但是只有在鼠标指针位于该元素的范围内时才能起作用,这不是一个不错的行为。

这是我的代码。我在做什么错?

function draggable(selector, options) {
  var options = options || {};
  let el = document.querySelector(selector);
  var mx = 0,
    my = 0;
  var dragging = false;
  var offset = [0, 0];
  var constrain = options.constrain || false;
  if (options === undefined) options = [];
  document.addEventListener('mousedown', function(e) {
    dragging = true;
    offset = {
      x: el.offsetLeft - e.clientX,
      y: el.offsetTop - e.clientY
    };
  });
  document.addEventListener('mousemove', function(e) {
    e.preventDefault();
    mx = e.clientX;
    my = e.clientY;
    if (dragging) {
      if (!constrain || constrain == 'x') el.style.left = (mx + offset.x) + 'px';
      if (!constrain || constrain == 'y') el.style.top = (my + offset.y) + 'px';
    }
  });
  document.addEventListener('mouseup', function(e) {
    dragging = false;
  });
}
var min = new draggable('#min', {
  constrain: 'x'
});
var max = new draggable('#max', {
  constrain: 'x'
});
body {
  padding: 50px;
}
.range-input {
  height: 24px;
  position: relative;
}
.rail {
  background: rgba(0, 0, 0, 0.5);
  border-radius: 9px;
  position: absolute;
  top: 50%;
  transform: translate3d(0, -50%, 0);
  height: 9px;
  width: 100%;
}
.handle {
  background: #000;
  border-radius: 50%;
  height: 24px;
  position: absolute;
  top: 50%;
  transform: translate3d(0, -50%, 0) scale(1);
  width: 24px;
}
.handle:hover {
  cursor: grab;
  transform: translate3d(0, -50%, 0) scale(1.2);
}
.handle:active {
  cursor: grabbing;
}
.min {
  left: 0;
}
.max {
  right: 0;
}
<div id="wrap" class="range-input">
  <div class="rail"></div>
  <div id="min" class="handle min"></div>
  <div id="max" class="handle max"></div>
</div>

您的两个元素都在注册一个事件处理程序,该事件处理程序正在收听文档上的拖放事件。您的逻辑不包含任何排除,因此窗口中任何地方的单击和拖动操作都会触发您的逻辑。

不过,我可以看到您要预防的东西。我认为您想确保鼠标在拖放动作(上下或向下(中留下手柄时,拖动动作继续发射。为此,请在元素上注册您的Mousedown事件。您仍然可以在文档中注册您的拖放事件,但将它们移入鼠标的活动处理程序中,例如:

function draggable(selector, options) {
  var options = options || {};
  let el = document.querySelector(selector);
  var mx = 0,
    my = 0;
  var dragging = false;
  var offset = [0, 0];
  var constrain = options.constrain || false;
  if (options === undefined) options = [];
  el.addEventListener('mousedown', function(e) {
    dragging = true;
    offset = {
      x: el.offsetLeft - e.clientX,
      y: el.offsetTop - e.clientY
    };
    document.addEventListener('mousemove', function(e) {
      e.preventDefault();
      mx = e.clientX;
      my = e.clientY;
      if (dragging) {
        if (!constrain || constrain == 'x') el.style.left = (mx + offset.x) + 'px';
        if (!constrain || constrain == 'y') el.style.top = (my + offset.y) + 'px';
      }
    });
    document.addEventListener('mouseup', function(e) {
      dragging = false;
    });
  });
}
var min = new draggable('#min', {
  constrain: 'x'
});
var max = new draggable('#max', {
  constrain: 'x'
});
body {
  padding: 50px;
}
.range-input {
  height: 24px;
  position: relative;
}
.rail {
  background: rgba(0, 0, 0, 0.5);
  border-radius: 9px;
  position: absolute;
  top: 50%;
  transform: translate3d(0, -50%, 0);
  height: 9px;
  width: 100%;
}
.handle {
  background: #000;
  border-radius: 50%;
  height: 24px;
  position: absolute;
  top: 50%;
  transform: translate3d(0, -50%, 0) scale(1);
  width: 24px;
}
.handle:hover {
  cursor: grab;
  transform: translate3d(0, -50%, 0) scale(1.2);
}
.handle:active {
  cursor: grabbing;
}
.min {
  left: 0;
}
.max {
  right: 0;
}
<div id="wrap" class="range-input">
  <div class="rail"></div>
  <div id="min" class="handle min"></div>
  <div id="max" class="handle max"></div>
</div>

请注意,完成拖动后,您需要取消注册文档事件处理程序。我还没有添加该逻辑,但这并不难。

仅适用于 mousedown,您应该检查单击元素是否确实是您想要的

function draggable(selector, options) {
  var options = options || {};
  let el = document.querySelector(selector);
  var mx = 0,
    my = 0;
  var dragging = false;
  var offset = [0, 0];
  var constrain = options.constrain || false;
  if (options === undefined) options = [];
  el.addEventListener('mousedown', function(e) {
    dragging = true;
    offset = {
      x: el.offsetLeft - e.clientX,
      y: el.offsetTop - e.clientY
    };
  });
  document.addEventListener('mousemove', function(e) {
    e.preventDefault();
    mx = e.clientX;
    my = e.clientY;
    if (dragging) {
      if (!constrain || constrain == 'x') el.style.left = (mx + offset.x) + 'px';
      if (!constrain || constrain == 'y') el.style.top = (my + offset.y) + 'px';
    }
  });
  document.addEventListener('mouseup', function(e) {
    dragging = false;
  });
}
var min = new draggable('#min', {
  constrain: 'x'
});
var max = new draggable('#max', {
  constrain: 'x'
});
body {
  padding: 50px;
}
.range-input {
  height: 24px;
  position: relative;
}
.rail {
  background: rgba(0, 0, 0, 0.5);
  border-radius: 9px;
  position: absolute;
  top: 50%;
  transform: translate3d(0, -50%, 0);
  height: 9px;
  width: 100%;
}
.handle {
  background: #000;
  border-radius: 50%;
  height: 24px;
  position: absolute;
  top: 50%;
  transform: translate3d(0, -50%, 0) scale(1);
  width: 24px;
}
.handle:hover {
  cursor: grab;
  transform: translate3d(0, -50%, 0) scale(1.2);
}
.handle:active {
  cursor: grabbing;
}
.min {
  left: 0;
}
.max {
  right: 0;
}
<div id="wrap" class="range-input">
  <div class="rail"></div>
  <div id="min" class="handle min"></div>
  <div id="max" class="handle max"></div>
</div>

以下是获得更大区域以拖动div元素的解决方案。我创建了一个container类的背景div,该div大于要拖动和透明的div

.container {
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: transparent;
  position: absolute;
  top: 50%;
  transform: translate3d(0, -50%, 0) scale(1);
}

function draggable(selector, options) {
  var options = options || {};
  let el = document.querySelector(selector);
  var mx = 0,
    my = 0;
  var dragging = false;
  var offset = [0, 0];
  var constrain = options.constrain || false;
  if (options === undefined) options = [];
  el.addEventListener('mousedown', function(e) {
    dragging = true;
    offset = {
      x: el.offsetLeft - e.clientX,
      y: el.offsetTop - e.clientY
    };
  });
  document.addEventListener('mousemove', function(e) {
    e.preventDefault();
    mx = e.clientX;
    my = e.clientY;
    if (dragging) {
      if (!constrain || constrain == 'x') el.style.left = (mx + offset.x) + 'px';
      if (!constrain || constrain == 'y') el.style.top = (my + offset.y) + 'px';
    }
  });
  document.addEventListener('mouseup', function(e) {
    dragging = false;
  });
}
var min = new draggable('#min', {
  constrain: 'x'
});
var max = new draggable('#max', {
  constrain: 'x'
});
body {
  padding: 50px;
}
.range-input {
  height: 24px;
  position: relative;
}
.rail {
  background: rgba(0, 0, 0, 0.5);
  border-radius: 9px;
  position: absolute;
  top: 50%;
  transform: translate3d(0, -50%, 0);
  height: 9px;
  width: 100%;
}
.container {
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: transparent;
  position: absolute;
  top: 50%;
  transform: translate3d(0, -50%, 0) scale(1);
}
.handle {
  background: #000;
  border-radius: 50%;
  height: 24px;
  position: absolute;
  top: 50%;
  transform: translate3d(0, -50%, 0) scale(1);
  width: 24px;
}
.container:hover {
  cursor: grab;
  transform: translate3d(0, -50%, 0) scale(1.2);
}
.container:active {
  cursor: grabbing;
}
.min {
  left: 0;
}
.max {
  right: 0;
}
<div id="wrap" class="range-input">
  <div class="rail"></div>
  <div id="min" class="container min">
    <div class="handle min"></div>
  </div>
  <div id="max" class="container max">
    <div class="handle max"></div>
  </div>
</div>