Always suppress a click event after drag in d3 - javascript

Decoupling of click and drag events is discussed in some previous questions here, e.g. this one
Typically, it is recommended to use if (d3.event.defaultPrevented === false) {...} in the click handler. However, this doesn't seem to work (at least in some browsers) if mouseup and mousedown are not in the same element. Consider this jsfiddle (code below). Here is the behavior I want: click anywhere in SVG triggers click event (rectangle flashes), drag anywhere in SVG drags the rectangle. Observed behavior (Chrome 33): if the mousedown of the click is inside the rectangle and mouseup is outside, both the drag and click events trigger. If both mousedown and mouseup are inside or both are outside, click event is not triggered.
Can someone explain why the click event is triggered if mouseup and mousedown are not in the same element, and how to reliably prevent this from happening?
var container, rect, dragBehavior;
svg = d3.select('svg').attr("width", 500).attr("height", 300);
container = svg.append('g');
rect = container.append('rect').attr('width', 100).attr('height', 100);
dragBehavior = d3.behavior.drag()
.on('dragend', onDragStart)
.on('drag', onDrag)
.on('dragend', onDragEnd);
svg.call(dragBehavior).on('click', onClick);
function flashRect() { rect.attr('fill', 'red').transition().attr('fill', 'black'); }
function onDragStart() { console.log('onDragStart'); }
function onDrag() {
console.log('onDrag');
var x = (d3.event.sourceEvent.pageX - 50);
container.attr('transform', 'translate(' + x + ')');
}
function onDragEnd() { console.log('onDragEnd'); }
function onClick(d) {
if (d3.event.defaultPrevented === false) {
console.log('onClick');
flashRect();
} else {
console.log("default prevented");
}
}

Add dragBehavior to the rectangle instead of the whole svg element.
Here is the fiddle link with the fix http://jsfiddle.net/Mn4Uk/27/
rect
.call(dragBehavior)
.on('click', onClick);

Related

How do I reference and manipulate a targeted object from a called function outside the function's scope?

I am trying to create a program (chess) such that div elements created via JS dom can be dragged with your cursor. However I have it setup so I add an event listener to each piece created which calls a function adding an event listener to the window which then calls the function to move each piece. I can't reference "this" since the target of the movement function is the window and not the piece with the "mousedown" event. Can I reference the intended target from a completely different function?
I'd tried to do var targetedPiece=this.targetedPiece in the "mouseDown" function
Followed by targetedPiece.style.left=cursor.x+'px' in the "movePiece" function but to no avail.
//Basic chess board creation, creating elements with DOM
var board = document.getElementById('gameBoard');
var newSquare = document.createElement('div');
var newPiece = document.createElement('div');
newPiece.addEventListener('mousedown', mouseDown, false);
newSquare.appendChild(newPiece);
board.appendChild(newSquare);
//Assigns cursor position to cursor.x & cursor.y respectively
function getCursorPos(e) {
let cursor = {
x: e.pageX - board.offsetLeft,
y: e.pageY - board.offsetTop
};
}
//Registers last mouseup/mousedown actions
function mouseDown(targetedPiece) {
window.addEventListener('mousemove', movePiece, true);
//This is the function I'm calling with the piece
}
function mouseUp() {
window.removeEventListener('mousemove', movePiece, true);
}
//Function for actually moving pieces with cursor
function movePiece() {
//What do I put here? The targeted piece calls mouseDown, can I reference it in this function?
//The idea is targetedPiece.style.left=cursor.x & targetedPiece.style.top=cursor.y
}
board.addEventListener('mousemove', getCursorPos, true);
window.addEventListener('mouseup', mouseUp, false);
<div id='gameBoard'></div>

Combing "click" and "hover" permanently?

I want to have the click and drag functionality that Raphael.js provides, an example here: https://qiao.github.io/PathFinding.js/visual/.
The way you add and remove obstacles is great, it's essentially combining mousedown event and hover. But how on earth is that done? Any help please?
The closest I have is: https://codepen.io/ProgrammingKea/pen/ZowWJx
The salient bit is
div.addEventListener("mousedown", function(ev){
this.classList.add("obstacle");
});
div.addEventListener("mousemove", function(ev){
this.classList.add("obstacle");
});
div.addEventListener("mouseup", function(ev){
this.classList.add("obstacle");
});
If you press large, then hover over the grid, that's the closest I've got.
But my issue is that its only hover here, I don't have the click functionality the above link does
Please post answers containing only vanilla JS
It maybe feels a bit clunky putting a handler on every element. I'd be tempted to put a handler on the main container and then check from there...
Maybe first add a bit of code to check if mouse is down.
var main = document.getElementById('main')
var mouseDown = 0;
main.onmousedown = function() {
mouseDown=1;
}
main.onmouseup = function() {
mouseDown=0;
}
Then we can check if a mouse is down or over event...
main.addEventListener('mouseover', mousecheck)
main.addEventListener('mousedown', mousecheck)
Then we preventDefault (stop a drag).
If the mouse is down, and the element being acted on is a box, then we'll change it's colour.
function mousecheck( ev ) {
ev.preventDefault();
if( mouseDown && ev.target.className.startsWith( 'box') ) {
ev.target.style.backgroundColor = ev.target.style.backgroundColor == "red" ? 'white' : 'red';
}
}
Codepen
You can use something like:
["mousedown", "mousemove", "mouseup"]
.forEach(function (eve) {
div.addEventListener(eve, function(ev){
this.classList.add("obstacle");
});
});

Is there a way to trigger mouseup when leaving the document body?

I found some code and I made a jsfillde with it. I need to know how to trigger mouseup in the function below, when you leave document body (in the fiddle example it's like dragging the object into HTML section for instance and leave the mouse click there).
function handleMouseMove(e) {
if (canMove) {
var left = getMousePosX(e);
var top = getMousePosY(e);
var newLeft = ($(elemToMove).css('left').toDecNum() + (left - lastPosX));
var newTop = ($(elemToMove).css('top').toDecNum() + (top - lastPosY));
$(elemToMove).css('left', newLeft);
$(elemToMove).css('top', newTop);
lastPosX = left;
lastPosY = top;
}
return false;
}
Thanks for any suggestion.
You can use
document.onmouseout = handleMouseUp;
This says that when the mouse leaves the document object, the handleMouseUp handler should be called.
Updated fiddle:
http://jsfiddle.net/vp14utt1/1/
-
P.S. for event handlers that simply take the event as an argument, you can just use the function name without wrapping it.
document.onmousedown = function (e) { handleMouseDown(e); };
document.onmousedown = handleMouseDown;
$(elemToMove).trigger('mouseup');
Should do it.
Add this line (new Fiddle):
document.onmouseleave = function (e) { handleMouseUp(e); };
This fires your 'mouse up' logic when the mouse is dragged off the browser window.
To quote The difference between mouseout() and mouseleave():
The mouseout event triggers when the mouse pointer leaves any child elements as well the selected element.
The mouseleave event is only triggered when the mouse pointer leaves the selected element.
I think you want the later.

how to find out click event in canvas,kinetic js

I am trying to find click event on shape. when i click on shape like rectangle its become drag-gable but when i click outside of it,it should off its resizable feature.
i tried with blur function but doesn't work. i don't want it on mouse out.
problem is when i click on rectangle it gives me alert but when i click on canvas it gives me that alert twice because that shape is part of canvas.
so please suggest me how to distinguish between click on shape and out side of shape.
how to find out click event in canvas,kinetic js
you can try something like this
appendEvents: function(box, area){
_self = this;
// add cursor styling
box.on('mouseover', function() {
_self.draw = false;
document.body.style.cursor = 'pointer';
});
box.on('mouseout', function() {
document.body.style.cursor = 'default';
});
box.on('click', function() {
_self.draw = true;
_self.focusArea(area, box,box.attrs.x,box.attrs.y);
_self.openFocusArea(area,box,box.attrs.x,box.attrs.y);
});
box.on('dragend', function() {
_self.draw = false;
_self.dragArea(area, box);
});
},

JointJS - Mouse click event triggers cell position change event

I need to define mouse click event for my each cell. I used cell:pointerup event; but this event is triggered when I change positions of cells too. How can I differentiate these 2 events?
Thanks in advance.
What you can do is to create a custom element view and distinct click from dragging by checking whether a pointermove event was triggered between pointerdown and pointerup events.
var ClickableView = joint.dia.ElementView.extend({
pointerdown: function () {
this._click = true;
joint.dia.ElementView.prototype.pointerdown.apply(this, arguments);
},
pointermove: function () {
this._click = false;
joint.dia.ElementView.prototype.pointermove.apply(this, arguments);
},
pointerup: function (evt, x, y) {
if (this._click) {
// triggers an event on the paper and the element itself
this.notify('cell:click', evt, x, y);
} else {
joint.dia.ElementView.prototype.pointerup.apply(this, arguments);
}
}
});
And then tell the joint.dia.Paper to use the view.
var paper = new joint.dia.Paper({
// el, width, height etc.
elementView: ClickableView
});
A fiddle can be found here.

Categories

Resources