javascript – Prevent link from opening when click and hold to drag in Google Chrome

Question:

How to prevent a link from being opened when holding and dragging the mouse in Google Chrome?

The script below is to perform a scroll action when clicking and dragging, but when it comes to links, when you release the mouse button the link is opened, how can I prevent it from being opened by dragging, and open the link only when clicking?

(function(root, factory) {
  if (typeof define === 'function' && define.amd) {
    define(['exports'], factory);
  } else if (typeof exports !== 'undefined') {
    factory(exports);
  } else {
    factory((root.dragscroll = {}));
  }
}(this, function(exports) {
  var _window = window;
  var _document = document;
  var mousemove = 'mousemove';
  var mouseup = 'mouseup';
  var mousedown = 'mousedown';
  var EventListener = 'EventListener';
  var addEventListener = 'add' + EventListener;
  var removeEventListener = 'remove' + EventListener;
  var newScrollX, newScrollY;

  var dragged = [];
  var reset = function(i, el) {
    for (i = 0; i < dragged.length;) {
      el = dragged[i++];
      el = el.container || el;
      el[removeEventListener](mousedown, el.md, 0);
      _window[removeEventListener](mouseup, el.mu, 0);
      _window[removeEventListener](mousemove, el.mm, 0);
    }

    // cloning into array since HTMLCollection is updated dynamically
    dragged = [].slice.call(_document.getElementsByClassName('dragscroll'));
    for (i = 0; i < dragged.length;) {
      (function(el, lastClientX, lastClientY, pushed, scroller, cont) {
        (cont = el.container || el)[addEventListener](
          mousedown,
          cont.md = function(e) {
            if (!el.hasAttribute('nochilddrag') ||
              _document.elementFromPoint(
                e.pageX, e.pageY
              ) == cont
            ) {
              pushed = 1;
              lastClientX = e.clientX;
              lastClientY = e.clientY;

              e.preventDefault();
            }
          }, 0
        );

        _window[addEventListener](
          mouseup, cont.mu = function() {
            pushed = 0;
          }, 0
        );

        _window[addEventListener](
          mousemove,
          cont.mm = function(e) {
            if (pushed) {
              (scroller = el.scroller || el).scrollLeft -=
                newScrollX = (-lastClientX + (lastClientX = e.clientX));
              scroller.scrollTop -=
                newScrollY = (-lastClientY + (lastClientY = e.clientY));
              if (el == _document.body) {
                (scroller = _document.documentElement).scrollLeft -= newScrollX;
                scroller.scrollTop -= newScrollY;
              }
            }
          }, 0
        );
      })(dragged[i++]);
    }
  }


  if (_document.readyState == 'complete') {
    reset();
  } else {
    _window[addEventListener]('load', reset, 0);
  }

  exports.reset = reset;
}));
.dragscroll{
  width:400px;
  overflow:scroll;
}
.dragscroll div{
  width:555px;
}
.dragscroll a{
  display:block;
  width:180px;
  height:100px;
  margin-right:5px;
  float:left;
}
.dragscroll::after {
  content: "";
  clear: both;
  display: table;
}
<div class="dragscroll">
<div>
<a href="https://www.example.com" style="background:#333;"></a>
<a href="https://www.example.com" style="background:#ddd;"></a>
<a href="https://www.example.com" style="background:#dd3333;"></a>
</div>
</div>

Answer:

There is an alternative using two events: mousedown and mouseup . When the user clicks on the link and holds it for more than 100ms, it will change the flag variable to true , and when releasing, triggering the mouseup , it will call a function ( libera() ) that will cancel the link's click event. If the click event has been canceled, after 100ms it will again call the same function releasing the click links.

Would be like this:

// variáveis de controle
var flag, tempo;

document.onmouseup = function(){
   if(flag){
      libera(flag);
      setTimeout(function(){
         flag = false;
         libera(flag);
      }, 100)
   }else{
      clearTimeout(tempo);
   }
}

document.onmousedown = function(e){
   var el = e.target;
   // verifica se o que foi clicado é um link <a>
   if(el.tagName == "A"){
      tempo = setTimeout(function(){
         flag = true;
      }, 100);
   }
}

function libera(c){
   var links = document.querySelectorAll(".dragscroll a");
   for(let x = 0; x < links.length; x++){
      links[x].onclick = function(e){
         if(!c) return true; // ação normal do link
         e.preventDefault(); // cancela a ação do link
      }
   }

}


(function(root, factory) {
  if (typeof define === 'function' && define.amd) {
    define(['exports'], factory);
  } else if (typeof exports !== 'undefined') {
    factory(exports);
  } else {
    factory((root.dragscroll = {}));
  }
}(this, function(exports) {
  var _window = window;
  var _document = document;
  var mousemove = 'mousemove';
  var mouseup = 'mouseup';
  var mousedown = 'mousedown';
  var EventListener = 'EventListener';
  var addEventListener = 'add' + EventListener;
  var removeEventListener = 'remove' + EventListener;
  var newScrollX, newScrollY;

  var dragged = [];
  var reset = function(i, el) {
    for (i = 0; i < dragged.length;) {
      el = dragged[i++];
      el = el.container || el;
      el[removeEventListener](mousedown, el.md, 0);
      _window[removeEventListener](mouseup, el.mu, 0);
      _window[removeEventListener](mousemove, el.mm, 0);
    }

    // cloning into array since HTMLCollection is updated dynamically
    dragged = [].slice.call(_document.getElementsByClassName('dragscroll'));
    for (i = 0; i < dragged.length;) {
      (function(el, lastClientX, lastClientY, pushed, scroller, cont) {
        (cont = el.container || el)[addEventListener](
          mousedown,
          cont.md = function(e) {
            if (!el.hasAttribute('nochilddrag') ||
              _document.elementFromPoint(
                e.pageX, e.pageY
              ) == cont
            ) {
              pushed = 1;
              lastClientX = e.clientX;
              lastClientY = e.clientY;

              e.preventDefault();
            }
          }, 0
        );

        _window[addEventListener](
          mouseup, cont.mu = function() {
            pushed = 0;
          }, 0
        );

        _window[addEventListener](
          mousemove,
          cont.mm = function(e) {
            if (pushed) {
              (scroller = el.scroller || el).scrollLeft -=
                newScrollX = (-lastClientX + (lastClientX = e.clientX));
              scroller.scrollTop -=
                newScrollY = (-lastClientY + (lastClientY = e.clientY));
              if (el == _document.body) {
                (scroller = _document.documentElement).scrollLeft -= newScrollX;
                scroller.scrollTop -= newScrollY;
              }
            }
          }, 0
        );
      })(dragged[i++]);
    }
  }


  if (_document.readyState == 'complete') {
    reset();
  } else {
    _window[addEventListener]('load', reset, 0);
  }

  exports.reset = reset;
}));
.dragscroll{
  width:400px;
  overflow:scroll;
}
.dragscroll div{
  width:555px;
}
.dragscroll a{
  display:block;
  width:180px;
  height:100px;
  margin-right:5px;
  float:left;
}
.dragscroll::after {
  content: "";
  clear: both;
  display: table;
}
<div class="dragscroll">
   <div>
      <a href="https://www.example.com" style="background:#333;"></a>
      <a href="https://www.example.com" style="background:#ddd;"></a>
      <a href="https://www.example.com" style="background:#dd3333;"></a>
   </div>
</div>
Scroll to Top