Description
There’s a long history here (d3/d3#1322, particularly @jfirebaugh’s comment) but I think it may be best to mousedown.preventDefault and touchstart.preventDefault by default, rather than allow the defaults and then subsequently try to prevent them selectively later.
Here’s my rationale:
-
It’s a Chrome bug that calling mousemove.preventDefault prevents text selection, drag-and-drop, and scrolling (see issue 485892). The mousemove event is defined as having no default action. Thus, the only standards-compliant way of preventing these default behaviors is to do it on mousedown.
Yes, this prevents capturing mouse events outside an iframe in Chrome due to another bug (issue 269917), but when Pointer Events are implemented (issue 471824) we’ll have element.setPointerCapture as the standard way of capturing events without needing the window hack for mousemove and mouseup.
-
If multiple touches start on the same element, you must call touchstart.preventDefault to disable pinch-and-zoom. We’ve ignored this in the past because the old drag behavior assumed the touches were always on different elements (e.g., distinct SVG circles). The new d3-drag is agnostic about the DOM structure, and it should be usable even if the circles are rendered with Canvas. It would be brittle and inconsistent to allow the default behavior on touchstart if there’s only one touch (on a given element) and not allow it if there are multiple touches.
Calling touchstart.preventDefault prevents a subsequent click. That’s somewhat unfortunate because this is not true of mousedown.preventDefault, and in the future, pointerdown.preventDefault:
In user agents that support firing
click
and/orcontextmenu
, callingpreventDefault
during a pointer event typically does not have an effect on whetherclick
and/orcontextmenu
are fired or not.However, you could still implement clickable and draggable things by calling element.click on drag end (if there’s no movement), and this is probably more obvious than checking click.defaultPrevented. I’ll have to test whether it’s possible to call element.click to explicitly click during a mouse-based drag, while still suppressing the default click…
-
The selectstart event is a working draft specification and not yet available in Firefox (Standard). Yes, Firefox supports -moz-user-select, but that’s vendor-prefixed (and buggy; see issue 648624). Given the inconsistency of default behaviors across browsers and input modes, trying to prevent them selectively has been an ongoing headache.
-
Whatever we choose as the default behavior of d3-drag (no pun intended!), that behavior is just the default and can be overridden or disabled. So if we decide that d3-drag should mousedown.preventDefault and touchstart.preventDefault by default, users can disable this behavior by calling drag.on and removing the
.nodefault
listener, and then do whatever they want. In contrast to D3 3.x, it is not necessary to fork the implementation.
The Chrome bug is unfortunate. But it feels like we should assume that this will eventually be fixed when Pointer Events are implemented, and that the most future-proof way to implement d3-drag will be to mousedown.preventDefault and touchstart.preventDefault (and eventually, pointerdown.preventDefault). Changing d3-drag’s default behavior in regards to preventing browser default behaviors in the future should probably be considered a breaking change, so it’d be great to commit to doing it now as part of the D3 4.0 major release.
Activity