Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generalize suppression of undesired default behaviors when dragging. #1341

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
88 changes: 38 additions & 50 deletions d3.js
Expand Up @@ -357,6 +357,34 @@ d3 = function() {
return value === source ? target : value;
};
}
function d3_vendorSymbol(object, name) {
if (name in object) return name;
name = name.charAt(0).toUpperCase() + name.substring(1);
for (var i = 0, n = d3_vendorPrefixes.length; i < n; ++i) {
var prefixName = d3_vendorPrefixes[i] + name;
if (prefixName in object) return prefixName;
}
}
var d3_vendorPrefixes = [ "webkit", "ms", "moz", "Moz", "o", "O" ];
var d3_event_dragSelect = d3_vendorSymbol(d3_documentElement.style, "userSelect");
function d3_event_dragSuppress(type) {
var selectstart = "selectstart." + type, dragstart = "dragstart." + type, click = "click." + type, w = d3.select(d3_window).on(selectstart, d3_eventPreventDefault).on(dragstart, d3_eventPreventDefault), style = d3_documentElement.style, select = style[d3_event_dragSelect];
style[d3_event_dragSelect] = "none";
return function(suppressClick) {
w.on(selectstart, null).on(dragstart, null);
style[d3_event_dragSelect] = select;
if (suppressClick) {
function off() {
w.on(click, null);
}
w.on(click, function() {
d3_eventPreventDefault();
off();
}, true);
setTimeout(off, 0);
}
};
}
d3.dispatch = function() {
var dispatch = new d3_dispatch(), i = -1, n = arguments.length;
while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch);
Expand Down Expand Up @@ -400,25 +428,14 @@ d3 = function() {
return event;
}
d3.event = null;
function d3_eventCancel() {
d3.event.stopPropagation();
function d3_eventPreventDefault() {
d3.event.preventDefault();
}
function d3_eventSource() {
var e = d3.event, s;
while (s = e.sourceEvent) e = s;
return e;
}
function d3_eventSuppress(target, type) {
function off() {
target.on(type, null);
}
target.on(type, function() {
d3_eventCancel();
off();
}, true);
setTimeout(off, 0);
}
function d3_eventDispatch(target) {
var dispatch = new d3_dispatch(), i = 0, n = arguments.length;
while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch);
Expand Down Expand Up @@ -497,34 +514,13 @@ d3 = function() {
return point;
}) : [];
};
function d3_vendorSymbol(object, name) {
if (name in object) return name;
name = name.charAt(0).toUpperCase() + name.substring(1);
for (var i = 0, n = d3_vendorPrefixes.length; i < n; ++i) {
var prefixName = d3_vendorPrefixes[i] + name;
if (prefixName in object) return prefixName;
}
}
var d3_vendorPrefixes = [ "webkit", "ms", "moz", "Moz", "o", "O" ];
var d3_event_userSelectProperty = d3_vendorSymbol(d3_documentElement.style, "userSelect"), d3_event_userSelectSuppress = d3_event_userSelectProperty ? function() {
var style = d3_documentElement.style, select = style[d3_event_userSelectProperty];
style[d3_event_userSelectProperty] = "none";
return function() {
style[d3_event_userSelectProperty] = select;
};
} : function(type) {
var w = d3.select(d3_window).on("selectstart." + type, d3_eventCancel);
return function() {
w.on("selectstart." + type, null);
};
};
d3.behavior.drag = function() {
var event = d3_eventDispatch(drag, "drag", "dragstart", "dragend"), origin = null;
function drag() {
this.on("mousedown.drag", mousedown).on("touchstart.drag", mousedown);
}
function mousedown() {
var target = this, event_ = event.of(target, arguments), eventTarget = d3.event.target, touchId = d3.event.touches ? d3.event.changedTouches[0].identifier : null, offset, origin_ = point(), moved = 0, selectEnable = d3_event_userSelectSuppress(touchId != null ? "drag-" + touchId : "drag");
var target = this, event_ = event.of(target, arguments), eventTarget = d3.event.target, touchId = d3.event.touches ? d3.event.changedTouches[0].identifier : null, offset, origin_ = point(), moved = 0, dragRestore = d3_event_dragSuppress(touchId != null ? "drag-" + touchId : "drag");
var w = d3.select(d3_window).on(touchId != null ? "touchmove.drag-" + touchId : "mousemove.drag", dragmove).on(touchId != null ? "touchend.drag-" + touchId : "mouseup.drag", dragend, true);
if (origin) {
offset = origin.apply(target, arguments);
Expand All @@ -546,7 +542,6 @@ d3 = function() {
var p = point(), dx = p[0] - origin_[0], dy = p[1] - origin_[1];
moved |= dx | dy;
origin_ = p;
d3_eventCancel();
event_({
type: "drag",
x: p[0] + offset[0],
Expand All @@ -556,15 +551,11 @@ d3 = function() {
});
}
function dragend() {
w.on(touchId != null ? "touchmove.drag-" + touchId : "mousemove.drag", null).on(touchId != null ? "touchend.drag-" + touchId : "mouseup.drag", null);
dragRestore(moved && d3.event.target === eventTarget);
event_({
type: "dragend"
});
if (moved) {
d3_eventCancel();
if (d3.event.target === eventTarget) d3_eventSuppress(w, "click");
}
w.on(touchId != null ? "touchmove.drag-" + touchId : "mousemove.drag", null).on(touchId != null ? "touchend.drag-" + touchId : "mouseup.drag", null);
selectEnable();
}
}
drag.origin = function(x) {
Expand Down Expand Up @@ -1205,17 +1196,15 @@ d3 = function() {
});
}
function mousedown() {
var target = this, event_ = event.of(target, arguments), eventTarget = d3.event.target, moved = 0, w = d3.select(d3_window).on("mousemove.zoom", mousemove).on("mouseup.zoom", mouseup), l = location(d3.mouse(target)), selectEnable = d3_event_userSelectSuppress("zoom");
var target = this, event_ = event.of(target, arguments), eventTarget = d3.event.target, moved = 0, w = d3.select(d3_window).on("mousemove.zoom", mousemove).on("mouseup.zoom", mouseup), l = location(d3.mouse(target)), dragRestore = d3_event_dragSuppress("zoom");
function mousemove() {
moved = 1;
translateTo(d3.mouse(target), l);
dispatch(event_);
}
function mouseup() {
if (moved) d3_eventCancel();
w.on("mousemove.zoom", null).on("mouseup.zoom", null);
selectEnable();
if (moved && d3.event.target === eventTarget) d3_eventSuppress(w, "click.zoom");
dragRestore(moved && d3.event.target === eventTarget);
}
}
function mousewheel() {
Expand Down Expand Up @@ -7935,7 +7924,7 @@ d3 = function() {
g.selectAll(".extent,.e>rect,.w>rect").attr("height", extent[1][1] - extent[0][1]);
}
function brushstart() {
var target = this, eventTarget = d3.select(d3.event.target), event_ = event.of(target, arguments), g = d3.select(target), resizing = eventTarget.datum(), resizingX = !/^(n|s)$/.test(resizing) && x, resizingY = !/^(e|w)$/.test(resizing) && y, dragging = eventTarget.classed("extent"), center, origin = mouse(), offset;
var target = this, eventTarget = d3.select(d3.event.target), event_ = event.of(target, arguments), g = d3.select(target), resizing = eventTarget.datum(), resizingX = !/^(n|s)$/.test(resizing) && x, resizingY = !/^(e|w)$/.test(resizing) && y, dragging = eventTarget.classed("extent"), dragRestore = d3_event_dragSuppress("brush"), center, origin = mouse(), offset;
var w = d3.select(d3_window).on("mousemove.brush", brushmove).on("mouseup.brush", brushend).on("touchmove.brush", brushmove).on("touchend.brush", brushend).on("keydown.brush", keydown).on("keyup.brush", keyup);
if (dragging) {
origin[0] = extent[0][0] - origin[0];
Expand All @@ -7952,7 +7941,6 @@ d3 = function() {
type: "brushstart"
});
brushmove();
d3_eventCancel();
function mouse() {
var touches = d3.event.changedTouches;
return touches ? d3.touches(target, touches)[0] : d3.mouse(target);
Expand All @@ -7965,15 +7953,15 @@ d3 = function() {
origin[1] -= extent[1][1];
dragging = 2;
}
d3_eventCancel();
d3_eventPreventDefault();
}
}
function keyup() {
if (d3.event.keyCode == 32 && dragging == 2) {
origin[0] += extent[1][0];
origin[1] += extent[1][1];
dragging = 0;
d3_eventCancel();
d3_eventPreventDefault();
}
}
function brushmove() {
Expand Down Expand Up @@ -8035,10 +8023,10 @@ d3 = function() {
g.style("pointer-events", "all").selectAll(".resize").style("display", brush.empty() ? "none" : null);
d3.select("body").style("cursor", null);
w.on("mousemove.brush", null).on("mouseup.brush", null).on("touchmove.brush", null).on("touchend.brush", null).on("keydown.brush", null).on("keyup.brush", null);
dragRestore();
event_({
type: "brushend"
});
d3_eventCancel();
}
}
brush.x = function(z) {
Expand Down
10 changes: 5 additions & 5 deletions d3.min.js

Large diffs are not rendered by default.

18 changes: 6 additions & 12 deletions src/behavior/drag.js
@@ -1,9 +1,10 @@
import "../core/document";
import "../core/rebind";
import "../core/vendor";
import "../event/drag";
import "../event/event";
import "../event/mouse";
import "../event/touches";
import "../event/user-select";
import "behavior";

d3.behavior.drag = function() {
Expand All @@ -23,7 +24,7 @@ d3.behavior.drag = function() {
offset,
origin_ = point(),
moved = 0,
selectEnable = d3_event_userSelectSuppress(touchId != null ? "drag-" + touchId : "drag");
dragRestore = d3_event_dragSuppress(touchId != null ? "drag-" + touchId : "drag");

var w = d3.select(d3_window)
.on(touchId != null ? "touchmove.drag-" + touchId : "mousemove.drag", dragmove)
Expand Down Expand Up @@ -54,23 +55,16 @@ d3.behavior.drag = function() {

moved |= dx | dy;
origin_ = p;
d3_eventCancel();

event_({type: "drag", x: p[0] + offset[0], y: p[1] + offset[1], dx: dx, dy: dy});
}

function dragend() {
event_({type: "dragend"});

// if moved, prevent the mouseup (and possibly click) from propagating
if (moved) {
d3_eventCancel();
if (d3.event.target === eventTarget) d3_eventSuppress(w, "click");
}

w .on(touchId != null ? "touchmove.drag-" + touchId : "mousemove.drag", null)
.on(touchId != null ? "touchend.drag-" + touchId : "mouseup.drag", null);
selectEnable();

dragRestore(moved && d3.event.target === eventTarget);
event_({type: "dragend"});
}
}

Expand Down
8 changes: 3 additions & 5 deletions src/behavior/zoom.js
@@ -1,9 +1,9 @@
import "../core/document";
import "../core/rebind";
import "../event/drag";
import "../event/event";
import "../event/mouse";
import "../event/touches";
import "../event/user-select";
import "../selection/selection";
import "behavior";

Expand Down Expand Up @@ -105,7 +105,7 @@ d3.behavior.zoom = function() {
moved = 0,
w = d3.select(d3_window).on("mousemove.zoom", mousemove).on("mouseup.zoom", mouseup),
l = location(d3.mouse(target)),
selectEnable = d3_event_userSelectSuppress("zoom");
dragRestore = d3_event_dragSuppress("zoom");

function mousemove() {
moved = 1;
Expand All @@ -114,10 +114,8 @@ d3.behavior.zoom = function() {
}

function mouseup() {
if (moved) d3_eventCancel();
w.on("mousemove.zoom", null).on("mouseup.zoom", null);
selectEnable();
if (moved && d3.event.target === eventTarget) d3_eventSuppress(w, "click.zoom");
dragRestore(moved && d3.event.target === eventTarget);
}
}

Expand Down
20 changes: 20 additions & 0 deletions src/event/drag.js
@@ -0,0 +1,20 @@
var d3_event_dragSelect = d3_vendorSymbol(d3_documentElement.style, "userSelect");

function d3_event_dragSuppress(type) {
var selectstart = "selectstart." + type,
dragstart = "dragstart." + type,
click = "click." + type,
w = d3.select(d3_window).on(selectstart, d3_eventPreventDefault).on(dragstart, d3_eventPreventDefault),
style = d3_documentElement.style,
select = style[d3_event_dragSelect];
style[d3_event_dragSelect] = "none";
return function(suppressClick) {
w.on(selectstart, null).on(dragstart, null);
style[d3_event_dragSelect] = select;
if (suppressClick) { // suppress the next click, but only if it’s immediate
function off() { w.on(click, null); }
w.on(click, function() { d3_eventPreventDefault(); off(); }, true);
setTimeout(off, 0);
}
};
}
12 changes: 1 addition & 11 deletions src/event/event.js
Expand Up @@ -2,8 +2,7 @@ import "dispatch";

d3.event = null;

function d3_eventCancel() {
d3.event.stopPropagation();
function d3_eventPreventDefault() {
d3.event.preventDefault();
}

Expand All @@ -13,15 +12,6 @@ function d3_eventSource() {
return e;
}

// Registers an event listener for the specified target that cancels the next
// event for the specified type, but only if it occurs immediately. This is
// useful to disambiguate dragging from clicking.
function d3_eventSuppress(target, type) {
function off() { target.on(type, null); }
target.on(type, function() { d3_eventCancel(); off(); }, true);
setTimeout(off, 0); // clear the handler if it doesn't fire
}

// Like d3.dispatch, but for custom events abstracting native UI events. These
// events have a target component (such as a brush), a target element (such as
// the svg:g element containing the brush) and the standard arguments `d` (the
Expand Down
16 changes: 0 additions & 16 deletions src/event/user-select.js

This file was deleted.

9 changes: 5 additions & 4 deletions src/svg/brush.js
@@ -1,6 +1,7 @@
import "../core/document";
import "../core/rebind";
import "../event/dispatch";
import "../event/drag";
import "../event/event";
import "../event/mouse";
import "../event/touches";
Expand Down Expand Up @@ -100,6 +101,7 @@ d3.svg.brush = function() {
resizingX = !/^(n|s)$/.test(resizing) && x,
resizingY = !/^(e|w)$/.test(resizing) && y,
dragging = eventTarget.classed("extent"),
dragRestore = d3_event_dragSuppress("brush"),
center,
origin = mouse(),
offset;
Expand Down Expand Up @@ -139,7 +141,6 @@ d3.svg.brush = function() {
// Notify listeners.
event_({type: "brushstart"});
brushmove();
d3_eventCancel();

function mouse() {
var touches = d3.event.changedTouches;
Expand All @@ -154,7 +155,7 @@ d3.svg.brush = function() {
origin[1] -= extent[1][1];
dragging = 2;
}
d3_eventCancel();
d3_eventPreventDefault();
}
}

Expand All @@ -163,7 +164,7 @@ d3.svg.brush = function() {
origin[0] += extent[1][0];
origin[1] += extent[1][1];
dragging = 0;
d3_eventCancel();
d3_eventPreventDefault();
}
}

Expand Down Expand Up @@ -267,8 +268,8 @@ d3.svg.brush = function() {
.on("keydown.brush", null)
.on("keyup.brush", null);

dragRestore();
event_({type: "brushend"});
d3_eventCancel();
}
}

Expand Down