Skip to content

Commit 53cd4d4

Browse files
committed
Use user-select: none instead of preventDefault.
The use of preventDefault while dragging or zooming was preventing desirable defaults from being triggered. Using user-select: none is a less intrusive way to prevent text from being selected. If user-select or a vendor-prefixed user-select is not supported, we suppress the selectstart event instead (for IE9). Note that Firefox has a bug where absolutely-positioned elements do not inherit the -moz-user-select style from the body element, so users may wish to additionally set -moz-user-select: none on absolutely-positioned elements, at least until this bug is fixed: https://bugzilla.mozilla.org/show_bug.cgi?id=648624 Fixes d3#1288, d3#1099.
1 parent 788855b commit 53cd4d4

10 files changed

Lines changed: 77 additions & 34 deletions

File tree

d3.js

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ d3 = function() {
55
if (!Date.now) Date.now = function() {
66
return +new Date();
77
};
8-
var d3_document = document, d3_window = window;
8+
var d3_document = document, d3_documentElement = d3_document.documentElement, d3_window = window;
99
try {
1010
d3_document.createElement("div").style.setProperty("opacity", 0, "");
1111
} catch (error) {
@@ -480,7 +480,7 @@ d3 = function() {
480480
return Array.prototype.slice.call(pseudoarray);
481481
}
482482
try {
483-
d3_array(d3_document.documentElement.childNodes)[0].nodeType;
483+
d3_array(d3_documentElement.childNodes)[0].nodeType;
484484
} catch (e) {
485485
d3_array = d3_arrayCopy;
486486
}
@@ -497,21 +497,38 @@ d3 = function() {
497497
return point;
498498
}) : [];
499499
};
500+
var d3_vendor = function(p) {
501+
var i = -1, n = p.length, s = d3_documentElement.style;
502+
while (++i < n) if (p[i] + "Transform" in s) return p[i];
503+
return "";
504+
}([ "webkit", "ms", "Moz", "O" ]);
505+
var d3_event_userSelectProperty = "userSelect" in d3_documentElement.style ? "userSelect" : d3_vendor + "UserSelect" in d3_documentElement.style ? d3_vendor + "UserSelect" : null;
506+
var d3_event_userSelectSuppress = d3_event_userSelectProperty ? function() {
507+
var style = d3_documentElement.style, select = style[d3_event_userSelectProperty];
508+
style[d3_event_userSelectProperty] = "none";
509+
return function() {
510+
style[d3_event_userSelectProperty] = select;
511+
};
512+
} : function(type) {
513+
var w = d3.select(d3_window).on("selectstart." + type, d3_eventCancel);
514+
return function() {
515+
w.on("selectstart." + type, null);
516+
};
517+
};
500518
d3.behavior.drag = function() {
501519
var event = d3_eventDispatch(drag, "drag", "dragstart", "dragend"), origin = null;
502520
function drag() {
503521
this.on("mousedown.drag", mousedown).on("touchstart.drag", mousedown);
504522
}
505523
function mousedown() {
506-
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;
524+
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");
507525
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);
508526
if (origin) {
509527
offset = origin.apply(target, arguments);
510528
offset = [ offset.x - origin_[0], offset.y - origin_[1] ];
511529
} else {
512530
offset = [ 0, 0 ];
513531
}
514-
if (touchId == null) d3_eventCancel();
515532
event_({
516533
type: "dragstart"
517534
});
@@ -544,6 +561,7 @@ d3 = function() {
544561
if (d3.event.target === eventTarget) d3_eventSuppress(w, "click");
545562
}
546563
w.on(touchId != null ? "touchmove.drag-" + touchId : "mousemove.drag", null).on(touchId != null ? "touchend.drag-" + touchId : "mouseup.drag", null);
564+
selectEnable();
547565
}
548566
}
549567
drag.origin = function(x) {
@@ -561,7 +579,7 @@ d3 = function() {
561579
return n.querySelector(s);
562580
}, d3_selectAll = function(s, n) {
563581
return n.querySelectorAll(s);
564-
}, d3_selectRoot = d3_document.documentElement, d3_selectMatcher = d3_selectRoot.matchesSelector || d3_selectRoot.webkitMatchesSelector || d3_selectRoot.mozMatchesSelector || d3_selectRoot.msMatchesSelector || d3_selectRoot.oMatchesSelector, d3_selectMatches = function(n, s) {
582+
}, d3_selectMatcher = d3_documentElement.matchesSelector || d3_documentElement[d3_vendor.toLowerCase() + "MatchesSelector"], d3_selectMatches = function(n, s) {
565583
return d3_selectMatcher.call(n, s);
566584
};
567585
if (typeof Sizzle === "function") {
@@ -1105,15 +1123,15 @@ d3 = function() {
11051123
};
11061124
d3.select = function(node) {
11071125
var group = [ typeof node === "string" ? d3_select(node, d3_document) : node ];
1108-
group.parentNode = d3_selectRoot;
1126+
group.parentNode = d3_documentElement;
11091127
return d3_selection([ group ]);
11101128
};
11111129
d3.selectAll = function(nodes) {
11121130
var group = d3_array(typeof nodes === "string" ? d3_selectAll(nodes, d3_document) : nodes);
1113-
group.parentNode = d3_selectRoot;
1131+
group.parentNode = d3_documentElement;
11141132
return d3_selection([ group ]);
11151133
};
1116-
var d3_selectionRoot = d3.select(d3_selectRoot);
1134+
var d3_selectionRoot = d3.select(d3_documentElement);
11171135
d3.behavior.zoom = function() {
11181136
var translate = [ 0, 0 ], translate0, scale = 1, scale0, scaleExtent = d3_behavior_zoomInfinity, event = d3_eventDispatch(zoom, "zoom"), x0, x1, y0, y1, touchtime;
11191137
function zoom() {
@@ -1184,9 +1202,7 @@ d3 = function() {
11841202
});
11851203
}
11861204
function mousedown() {
1187-
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));
1188-
d3_window.focus();
1189-
d3_eventCancel();
1205+
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");
11901206
function mousemove() {
11911207
moved = 1;
11921208
translateTo(d3.mouse(target), l);
@@ -1195,6 +1211,7 @@ d3 = function() {
11951211
function mouseup() {
11961212
if (moved) d3_eventCancel();
11971213
w.on("mousemove.zoom", null).on("mouseup.zoom", null);
1214+
selectEnable();
11981215
if (moved && d3.event.target === eventTarget) d3_eventSuppress(w, "click.zoom");
11991216
}
12001217
}
@@ -1892,7 +1909,7 @@ d3 = function() {
18921909
d3_timer_queueTail = t0;
18931910
return time;
18941911
}
1895-
var d3_timer_frame = d3_window.requestAnimationFrame || d3_window.webkitRequestAnimationFrame || d3_window.mozRequestAnimationFrame || d3_window.oRequestAnimationFrame || d3_window.msRequestAnimationFrame || function(callback) {
1912+
var d3_timer_frame = d3_window.requestAnimationFrame || d3_window[d3_vendor.toLowerCase() + "RequestAnimationFrame"] || function(callback) {
18961913
setTimeout(callback, 17);
18971914
};
18981915
var d3_format_decimalPoint = ".", d3_format_thousandsSeparator = ",", d3_format_grouping = [ 3, 3 ];

d3.min.js

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/behavior/drag.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import "../core/rebind";
33
import "../event/event";
44
import "../event/mouse";
55
import "../event/touches";
6+
import "../event/user-select";
67
import "behavior";
78

89
d3.behavior.drag = function() {
@@ -21,7 +22,8 @@ d3.behavior.drag = function() {
2122
touchId = d3.event.touches ? d3.event.changedTouches[0].identifier : null,
2223
offset,
2324
origin_ = point(),
24-
moved = 0;
25+
moved = 0,
26+
selectEnable = d3_event_userSelectSuppress(touchId != null ? "drag-" + touchId : "drag");
2527

2628
var w = d3.select(d3_window)
2729
.on(touchId != null ? "touchmove.drag-" + touchId : "mousemove.drag", dragmove)
@@ -34,8 +36,6 @@ d3.behavior.drag = function() {
3436
offset = [0, 0];
3537
}
3638

37-
// Only cancel mousedown; touchstart is needed for draggable links.
38-
if (touchId == null) d3_eventCancel();
3939
event_({type: "dragstart"});
4040

4141
function point() {
@@ -70,6 +70,7 @@ d3.behavior.drag = function() {
7070

7171
w .on(touchId != null ? "touchmove.drag-" + touchId : "mousemove.drag", null)
7272
.on(touchId != null ? "touchend.drag-" + touchId : "mouseup.drag", null);
73+
selectEnable();
7374
}
7475
}
7576

src/behavior/zoom.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import "../core/rebind";
33
import "../event/event";
44
import "../event/mouse";
55
import "../event/touches";
6+
import "../event/user-select";
67
import "../selection/selection";
78
import "behavior";
89

@@ -102,10 +103,8 @@ d3.behavior.zoom = function() {
102103
eventTarget = d3.event.target,
103104
moved = 0,
104105
w = d3.select(d3_window).on("mousemove.zoom", mousemove).on("mouseup.zoom", mouseup),
105-
l = location(d3.mouse(target));
106-
107-
d3_window.focus();
108-
d3_eventCancel();
106+
l = location(d3.mouse(target)),
107+
selectEnable = d3_event_userSelectSuppress("zoom");
109108

110109
function mousemove() {
111110
moved = 1;
@@ -116,6 +115,7 @@ d3.behavior.zoom = function() {
116115
function mouseup() {
117116
if (moved) d3_eventCancel();
118117
w.on("mousemove.zoom", null).on("mouseup.zoom", null);
118+
selectEnable();
119119
if (moved && d3.event.target === eventTarget) d3_eventSuppress(w, "click.zoom");
120120
}
121121
}

src/core/array.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ function d3_arraySlice(pseudoarray) {
1313
}
1414

1515
try {
16-
d3_array(d3_document.documentElement.childNodes)[0].nodeType;
16+
d3_array(d3_documentElement.childNodes)[0].nodeType;
1717
} catch(e) {
1818
d3_array = d3_arrayCopy;
1919
}

src/core/document.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
var d3_document = document,
2+
d3_documentElement = d3_document.documentElement,
23
d3_window = window;

src/core/vendor.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import "document";
2+
3+
var d3_vendor = (function(p) {
4+
var i = -1, n = p.length, s = d3_documentElement.style;
5+
while (++i < n) if (p[i] + "Transform" in s) return p[i];
6+
return "";
7+
})(["webkit", "ms", "Moz", "O"]);

src/event/timer.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import "../core/document";
2+
import "../core/vendor";
23

34
var d3_timer_queueHead,
45
d3_timer_queueTail,
@@ -78,8 +79,5 @@ function d3_timer_sweep() {
7879
}
7980

8081
var d3_timer_frame = d3_window.requestAnimationFrame
81-
|| d3_window.webkitRequestAnimationFrame
82-
|| d3_window.mozRequestAnimationFrame
83-
|| d3_window.oRequestAnimationFrame
84-
|| d3_window.msRequestAnimationFrame
82+
|| d3_window[d3_vendor.toLowerCase() + "RequestAnimationFrame"]
8583
|| function(callback) { setTimeout(callback, 17); };

src/event/user-select.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import "../core/document";
2+
import "../core/vendor";
3+
import "event";
4+
5+
var d3_event_userSelectProperty = "userSelect" in d3_documentElement.style ? "userSelect"
6+
: d3_vendor + "UserSelect" in d3_documentElement.style ? d3_vendor + "UserSelect"
7+
: null;
8+
9+
var d3_event_userSelectSuppress = d3_event_userSelectProperty
10+
? function() {
11+
var style = d3_documentElement.style,
12+
select = style[d3_event_userSelectProperty];
13+
style[d3_event_userSelectProperty] = "none";
14+
return function() { style[d3_event_userSelectProperty] = select; };
15+
}
16+
: function(type) {
17+
var w = d3.select(d3_window).on("selectstart." + type, d3_eventCancel);
18+
return function() { w.on("selectstart." + type, null); };
19+
};

src/selection/selection.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import "../core/array";
22
import "../core/document";
3+
import "../core/vendor";
34

45
function d3_selection(groups) {
56
d3_arraySubclass(groups, d3_selectionPrototype);
@@ -8,8 +9,7 @@ function d3_selection(groups) {
89

910
var d3_select = function(s, n) { return n.querySelector(s); },
1011
d3_selectAll = function(s, n) { return n.querySelectorAll(s); },
11-
d3_selectRoot = d3_document.documentElement,
12-
d3_selectMatcher = d3_selectRoot.matchesSelector || d3_selectRoot.webkitMatchesSelector || d3_selectRoot.mozMatchesSelector || d3_selectRoot.msMatchesSelector || d3_selectRoot.oMatchesSelector,
12+
d3_selectMatcher = d3_documentElement.matchesSelector || d3_documentElement[d3_vendor.toLowerCase() + "MatchesSelector"],
1313
d3_selectMatches = function(n, s) { return d3_selectMatcher.call(n, s); };
1414

1515
// Prefer Sizzle, if available.
@@ -53,14 +53,14 @@ import "transition";
5353
// TODO fast singleton implementation?
5454
d3.select = function(node) {
5555
var group = [typeof node === "string" ? d3_select(node, d3_document) : node];
56-
group.parentNode = d3_selectRoot;
56+
group.parentNode = d3_documentElement;
5757
return d3_selection([group]);
5858
};
5959

6060
d3.selectAll = function(nodes) {
6161
var group = d3_array(typeof nodes === "string" ? d3_selectAll(nodes, d3_document) : nodes);
62-
group.parentNode = d3_selectRoot;
62+
group.parentNode = d3_documentElement;
6363
return d3_selection([group]);
6464
};
6565

66-
var d3_selectionRoot = d3.select(d3_selectRoot);
66+
var d3_selectionRoot = d3.select(d3_documentElement);

0 commit comments

Comments
 (0)