Search code examples
javascriptiostouch-event

How to stop double-touching in iOS from scaling the entire page


I have a webapp with an inner-pane inside an outer-window.
The user can do various touch operations, e.g. zoom-in in the inner-pane via 2-finger pinch, without zooming-in the outer-window, touch move to pan the inner window etc..
On Chrome on Android, the app works as expected.
But Safari on iOS device (iPad), the entire window sometimes scales when quickly double-toching on the border-line of the inner window.

I found out that I can prevent this side effect by adding an event listener on the touchend event.

window.addEventListener('touchend', onTouchEnd5, {capture: false, passive: false});

and calling event.preventDefault() in the binded function.

async function onTouchEnd5( event ) {
    console.log('BEG onTouchEnd5');
    event.preventDefault();
};

But this causes another side effect where touch-clicks on buttons are not intercepted.

I tried adding eventlisteners on other events that are possibly trigerred by the default behavior, e.g. click, dblclick, touchcancel, fullscreenchange, fullscreenerror, resize, onscroll
The idea was to try and prevent the default behaviour for these events in case that the side effect is caused there. (the rational was that iphone/ipad can trigger unexpected events).
From the listed events above, the event onscroll is trigerred. In the binded function to this event, I added preventDefault():

function onScroll5( event ) {
    console.log('BEG onScroll5');
    event.preventDefault();
};

But the I can still cause the entire window to rescale by quickly double-touching on the border-line of the inner window.

How can I prevent the entire window from scaling in iOS when double touching?


Note:
The scaling of the entire window is the latest observation.
Before that, I found that the entire window scales when e.g. doing a twon-finger pinch in another section of the window.
The solution in this case, was to add preventDefault on another event listener binded function.

Given that there are multiple events in the app, a more general question is:
Is there a way to prevent the entire window from scaling alltogether, in eny event?

Thanks


Solution

  • I found out the solution.
    I had the following DOM structure:

    <body>
        <div id="div1">
            <div id="div2">
                <div id="div3">
                    <canvas>...</canvas>
                </div>
            </div>
            <div id="div4">
                <div id="div5"><button></div>
            </div>
        </div>
    </body>
    

    I initially tried to add event.preventDefault() in onTouchStartDiv3(), which listens to events in the canvas element (the closest upstream element with event listener is div3).
    This solved the problem of double-touch in the border but caused another problem:
    onTouchMoveDiv3() was called immediately (without even moving the finger), which caused other side-effects. (I have some state machine in the level, that depends on the onTouchStartDiv3(), onTouchMoveDiv3(), onTouchEndDiv3()).

    I then tried to add event listeners at the window/document levels (onTouchStartWindow(), onTouchStartDocument()) and call event.preventDefault() in them.
    This again solved the problem of double-touch in the border but caused another problem:
    The button (div5), stopped responding to touch events.
    The reason was that, because there is no specific event listener on the button, the event was intercepted higher up, and default behaviour was applied.
    Because the window/document event listener called event.preventDefault(), this behaviour was prevented (which stopped the button from responding to touch events).

    Finally, I added event listener at the touched border level (div2).
    Inside onTouchStartDiv2() I compare event.currentTarget (div2), to event.target where the event originated.
    If the event.target is div2 then the event originated from clicking on the border, so I apply event.preventDefault() to prevent the side-effect of resizing the entire image on iOS.
    If the event.target is not div2 then the event originated from clicking, e.g. in the canvas, so I do not apply event.preventDefault() to maintain the state machine.
    With this logic, I solved the problem:

    • when touching the canvas element (div3 is the closest upstream element with event listener), the regular behaviour was applied.

    • when touching the border element (div2), event.preventDefault() was applied to the event listener, and prevent the default behaviour of resizing the entire window (which I wanted to disable).

    • when touching the button element (div5), the regular behaviour was applied so the button responded to touch events.