Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically disabling touch-action (overscroll) for SVG elements

I'm having problems with touch screen overscrolling on Chrome.

I have a document with am SVG element in it, which contains some shape, say a rectangle:

enter image description here

Now, I want to make the rectangle draggable, which means that I want to disable all kinds of touch actions on the respective <rect> element, by setting it's style property touch-action: none.

This works fine on all desktop browsers, except Chrome. On Chrome, when I touch and move on a rectangle, browser's overscroll feature kicks in. This causes the browser window to awkwardly move, as well as all Pointer events that I have set on the rectangle cancelled.

I.e. pointermove is registered for a fraction of second, then it just stops when overscroll kicks in. pointerup is never called even when touch is released.

enter image description here

Now, if I had an HTML element instead of an SVG element, setting touch-action: none would do the job. The same thing on an SVG element fails.

Technically, this could be solved by either setting touch-action: none on document.body or wrapping the whole SVG into a <div> element with touch-action: none set.

Unfortunately, that is not an option for me since I need the document (and rest of the rectangle-surrounding SVG) to retain all of its original touch gestures, except when directly on a rectangle.

As a solution, I did try to dynamically set touch-action: none on document: body when pointerdown event occurs on a rectangle.

// Get element
var o = document.getElementById( "test" );

// disable touch action on press of the SVG element
o.addEventListener( "pointerdown", function(e) {
  document.body.style.touchAction = "none";
} );

// re-enable touch action when released
o.addEventListener( "pointerup", function(e) {
  document.body.style.touchAction = "auto";
} );

Unfortunately, that does not help. The style on body gets set and next time I try dragging rectangle it works as expected (because pointerup event never gets executed), just not this time.

Adding preventDefault() to event handlers has also no effect.

I would appreciate if someone who have had similar experience could share a solution.

Here's a live example of the above.

// Get element
var o = document.getElementById( "test" );

// disable touch action on press of the SVG element
o.addEventListener( "pointerdown", function(e) {
  document.body.style.touchAction = "none";
} );

// re-enable touch action when released
o.addEventListener( "pointerup", function(e) {
  document.body.style.touchAction = "auto";
} );
<svg id="test" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="width: 300px; height: 300px; border: 1px solid #eee;">
  <g transform="translate(50,50)">
    <rect fill="#00fff0" width="200" height="200" style="touch-action: none;"></rect>
  </g>
</svg>

UPDATE: looks like using preventDefault() on an touchstart event does the trick.

However, it seems to be wrong to use both modern pointerdown and legacy touchstart at the same time. This seems like a bug in Chrome 60. If anyone could confirm it, that would be great.

like image 745
martynasma Avatar asked Aug 14 '17 15:08

martynasma


1 Answers

I think it would be enough by preventing touchstart:

window.addEventListener( "touchstart", function(e) {
  // Determine wether or not you are panning from rectangle:
  if (e.target ....)
      e.preventDefault();
} );
like image 128
lilezek Avatar answered Oct 30 '22 15:10

lilezek