dbushell / Responsive-Off-Canvas-Menu

A responsive off-canvas menu using CSS transforms and transitions. This repository contains several demos to support a Smashing Magazine article.
819 stars 217 forks source link

Allow menu to be dismissed on touching the partial off-screen content (iOS) #2

Open paulrobertlloyd opened 11 years ago

paulrobertlloyd commented 11 years ago

From my limited to reading on the matter, for elements that are not links or buttons, iOS doesn’t translate click events to ontouchstart; therefore touching outside the menu doesn’t work in iOS.

dbushell commented 11 years ago

The click event listener line for reference.

Looks like I was trying to be too clever for my own good :) This needs more thought/testing obviously, but maybe something along the lines of:

var close_event = ('ontouchstart' in window) ? 'touchstart' : 'mousedown';

document.addEventListener(close_event, function(e)
{
    if (nav_open && !hasParent(e.target, 'nav')) {
        e.preventDefault();
        app.closeNav();
    }
},
true);

Problem is that it will probably cancel any attempts to scroll vertically if the user touches the right-hand side (i.e. not the nav element). You'd want a slight delay to ensure the touchend, touchcancel or mouseup happen within 200–300ms.

paulrobertlloyd commented 11 years ago

Cheers, I’ll give that a go. Meanwhile, I used some CSS:

.touch #inner-wrap {
    cursor: pointer;
}
i-like-robots commented 11 years ago

In the past when working with dropdown-type interface elements I've implemented this functionality (modified to make sense here) to best-guess whether or not a user is scrolling:

$(document).on('click touchstart touchend', function(e) {

    var touch;
    var sensitivity = 20;
    var closeThis  = function() {...};

    // Distinguish scroll from a 'tap'
    if ( e.type === 'touchstart' && e.originalEvent.touches.length === 1 ) {
        touch = {
            x: e.originalEvent.touches[0].pageX,
            y: e.originalEvent.touches[0].pageY
        };

        return;
    }

    if ( e.type === 'touchend' ) {
        var difX = Math.abs(touch.x - e.originalEvent.changedTouches[0].pageX);
        var difY = Math.abs(touch.y - e.originalEvent.changedTouches[0].pageY);

        touch = undefined;

        if ( difX > sensitivity || difY > sensitivity ) {
            return;
        }
    }

    closeThis();

});

EDIT: I guess this is actually simpler but I seem to remember changedTouches is/was a bitch:

$(document).on('click touchend', function(e) {

    var closeThis  = function() {...};

    if ( e.type === 'touchend' && e.originalEvent.changedTouches.length ) {
        return;
    }

    closeThis();

});