Korok White
Korok White

Reputation: 13

drag event is being fired on click

TL;DR: event that should be fired on element drag is being fired on element click.

I'm using svg.draggable.js and it has 4 custom events, the ones important here are dragstart and dragmove. dragstart fires when the user clicks the element, while dragmove fires on every update on the element position. The problem is that dragmove is fired when the element is clicked too, therefore running logic that it should not (well, at least in my case).

I've already searched on how to stop an event from firing and got to preventDefaul(), stopPropagation() and stopImmediatePropagation() but none of these solves my problem.

Is there a way to make an event prevent another event from firing?

P.S: there's an open issue on the repository, but I'm trying to find an "agnostic" way to do this.

Code:

What should happen on click:

// start dragging
  DragHandler.prototype.start = function(e){
    // check for left button
    if(e.type == 'click'|| e.type == 'mousedown' || e.type == 'mousemove'){
      if((e.ctrlKey || e.metaKey)/*if Ctrl or cmd key pressed, then Multi Select*/ || (e.which || e.buttons) != 1){
          return;
      }
    }

    var _this = this;

    // fire beforedrag event
    this.el.fire('beforedrag', { event: e, handler: this });

    // search for parent on the fly to make sure we can call
    // draggable() even when element is not in the dom currently
    this.parent = this.parent || this.el.parent(SVG.Nested) || this.el.parent(SVG.Doc);
    this.p = this.parent.node.createSVGPoint();

    // save current transformation matrix
    this.m = this.el.node.getScreenCTM().inverse();

    var box = this.getBBox();

    var anchorOffset = 0;
    var anchorOffsetVertical = 0;

    // fix text-anchor in text-element (#37)
    if(this.el instanceof SVG.Text) {
      var tChildren = this.el.node.children;

      if (tChildren && tChildren.length) {
        for(var i = 0; i < tChildren.length; i++) {
          var currentOffset = tChildren[i].getComputedTextLength();

          if (currentOffset > anchorOffset) {
            anchorOffset = currentOffset;
          }
        }
      } else {
        anchorOffset = this.el.node.getComputedTextLength();
      }

      switch(this.el.node.style.textAnchor || this.el.attr('text-anchor')){
        case 'middle':
          anchorOffset /= 2;
          break;
        case 'start':
          anchorOffset = 0;
          break;
      }
    }

    this.startPoints = {
      // We take absolute coordinates since we are just using a delta here
      point: this.transformPoint(e),
      box: box,
      offset: anchorOffset
    };

    // add drag and end events to window
    SVG.on(window, 'mousemove.drag', function(e){ _this.drag(e); });
    SVG.on(window, 'touchmove.drag', function(e){ _this.drag(e); });
    SVG.on(window, 'mouseup.drag', function(e){ _this.end(e); });
    SVG.on(window, 'touchend.drag', function(e){ _this.end(e); });

    // fire dragstart event
    this.el.fire('dragstart', {event: e, p: this.p, m: this.m, handler: this});

    // prevent browser drag behavior
    e.preventDefault();
  };

What should happen while dragging the element:

// while dragging
  DragHandler.prototype.drag = function(e){
    var box = this.getBBox(),
        p   = this.transformPoint(e),
        x   = this.startPoints.box.x + (p.x - this.startPoints.point.x) + this.startPoints.offset,
        y   = this.startPoints.box.y + (p.y - this.startPoints.point.y),
        c   = this.constraint,
        s   = this.el.transform().matrix.a;

    this.el.fire('dragmove', { event: e, p: this.p, m: this.m, handler: this });

    // move the element to its new position, if possible by constraint
    if (typeof c == 'function') {
      var coord = c.call(this.el, x, y, this.m);

      if (typeof coord == 'boolean') {
        coord = {
          x: coord,
          y: coord
        };
      }

      // if true, we just move. If !false its a number and we move it there
      if (coord.x === true) {
        this.el.x(x);
      } else if (coord.x !== false) {
        this.el.x(coord.x);
      }

      if (coord.y === true) {
        this.el.y(y);
      } else if (coord.y !== false) {
        this.el.y(coord.y);
      }
    } else if (typeof c == 'object') {
      // keep element within constrained box
      if (c.minX !== null && x < c.minX)
        x = c.minX;
      else if (c.maxX !== null && x > c.maxX - box.width){
        x = c.maxX - box.width;
      }if (c.minY !== null && y < c.minY)
        y = c.minY;
      else if (c.maxY !== null && y > c.maxY - box.height)
        y = c.maxY - box.height;

      this.el.move(x, y);
      // console.log(x, y, s);
    }
  };

There's a bit of own code on these snippets but in general it is from svg.draggable.js

Upvotes: 1

Views: 2085

Answers (2)

Amit Kumar Gupta
Amit Kumar Gupta

Reputation: 7413

This is how I've solved this problem;

function onMouse(shape, dragCB, clickCB){
    var mousestate = 0;
    shape.on( 'mousedown', function() {
        mousestate = 1;
    });
    shape.on( 'mousemove', function() {
        if(mousestate === 1) mousestate = 2;
    });
    shape.on( 'mouseup', function(e) {
        if(mousestate === 2) {
            dragCB(e);
        }else{
            clickCB(e) 
        }
        mousestate = 0;
    });
}

Upvotes: 0

banna
banna

Reputation: 249

One way I imagine to get around that is to define a flag that is set to true when dragstart is invoked, then inside dragmove handler you clear that flag the first time (so the logic is not invoked when the element is clicked).

So inside the dragmove handler you define something like:

if(flag) {
  flag = false;
  return
}
else {
  // your logic for dragmove here
}

Upvotes: 1

Related Questions