skip to Main Content

If you drag the green square over one of the other squares it inserts it before (moves it), but if you continue to drag without letting go of your mouse button, this action doesn’t continue to work for the remaining squares. The onpointermove event only fires when over the green button itself, not the rest of the DOM at this point.

I presume this is something to do with the fact it’s moved in the DOM, any ideas how to work around this? I have to use the Pointer Capture API to perform this action.

const divs = Array.from(document.querySelectorAll("div"));

divs.forEach((div) => {
  function beginSliding(e) {
    div.onpointermove = slide;
    div.setPointerCapture(e.pointerId);
  }

  function stopSliding(e) {
    div.onpointermove = null;
    div.releasePointerCapture(e.pointerId);
  }

  function slide(e) {
    const ele = document.elementFromPoint(e.clientX, e.clientY);
    
    console.log("move");
    
    if (ele === e.target || !divs.includes(ele)) {
        return;
    }
    
    div.parentElement.insertBefore(div, ele)
  }

  div.onpointerdown = beginSliding;
  div.onpointerup = stopSliding;
});
nav {
  display: flex;
  gap: 10px;
}

div {
  height: 30px;
  width: 100px;
  user-select: none;
}

#a { background: grey; }
#b { background: red; }
#c { background: blue; }
#d { background: yellow; }
#e { background: green; }
<nav>
  <div id="a"></div>
  <div id="b"></div>
  <div id="c"></div>
  <div id="d"></div>
  <div id="e"></div>
</nav>

JSFiddle https://jsfiddle.net/u7f2taLj/2/

2

Answers


  1. It seems that setPointerCapture is sensitive to the location of the element. A work around would be to beginSliding again as soon as possible after changing location.

    To improve the code I use insertAfter instead of insertBefore when needed, depending in the order of the elements.

    const divs = Array.from(document.querySelectorAll("div"));
    
    function getElementIndex(element) {
      return Array.from(element.parentNode.children).indexOf(element);
    }
    
    divs.forEach((div) => {
      function beginSliding(e) {
        console.log('beginSliding')
        div.onpointermove = slide;
        div.setPointerCapture(e.pointerId);
      }
    
      function stopSliding(e) {
        console.log('stopSliding')
        div.onpointermove = null;
        div.releasePointerCapture(e.pointerId);
      }
    
      function slide(e) {
        const ele = document.elementFromPoint(e.clientX, e.clientY);
    
        if (ele === e.target || !divs.includes(ele)) {
          return;
        }
    
        if (getElementIndex(div) > getElementIndex(ele)) {
          div.parentElement.insertBefore(div, ele)
        } else {
          div.parentElement.insertBefore(div, ele.nextSibling)
        }
    
        stopSliding(e)
        beginSliding(e)
      }
    
      div.onpointerdown = beginSliding;
      div.onpointerup = stopSliding;
    });
    nav {
      display: flex;
      gap: 10px;
    }
    
    div[id] {
      height: 30px;
      width: 100px;
      user-select: none;
    }
    
    #a {
      background: grey;
    }
    
    #b {
      background: red;
    }
    
    #c {
      background: blue;
    }
    
    #d {
      background: yellow;
    }
    
    #e {
      background: green;
    }
    <nav>
      <div id="a"></div>
      <div id="b"></div>
      <div id="c"></div>
      <div id="d"></div>
      <div id="e"></div>
    </nav>
    Login or Signup to reply.
  2. Copy "div.setPointerCapture(e.pointerId);" after "div.onpointerdown = beginSliding;" in slide function : lines 52 & 53

    <style>
    nav {
      display: flex;
      gap: 10px;
    }
    
    div {
      height: 30px;
      width: 100px;
      user-select: none;
    }
    
    #a { background: grey; }
    #b { background: red; }
    #c { background: blue; }
    #d { background: yellow; }
    #e { background: green; }
    </style>
    
    <nav>
      <div id="a"></div>
      <div id="b"></div>
      <div id="c"></div>
      <div id="d"></div>
      <div id="e"></div>
    </nav>
    
    <script>
    let divs = Array.from(document.querySelectorAll("div"));
    
    divs.forEach((div) => {
      function beginSliding(e) {
        div.onpointermove = slide;
        div.setPointerCapture(e.pointerId);
      }
    
      function stopSliding(e) {
        div.onpointermove = null;
        div.releasePointerCapture(e.pointerId);
      }
    
      function slide(e) {
        let ele = document.elementFromPoint(e.clientX, e.clientY);
        
        console.log("move");
        
        if (ele === e.target || !divs.includes(ele)) { 
            return;
        }
        
        div.parentElement.insertBefore(div, ele);
        div.setPointerCapture(e.pointerId);
      }
    
      div.onpointerdown = beginSliding;
      div.onpointerup = stopSliding;
    });
    </script>
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search