javascriptangularjsmobileangularjs-ng-clickangularjs-ng-touch

ng-click on body breaks links and input fields on mobile with ngTouch


Not sure if anybody can help me here, I will probably have to come up with something different but I wanted to sound this issue since I didn't find anything like that here.

I have an app-like website with swipable menu, and I want it to go away whenever user taps somewhere outside the menu. So I've used ngTouch for swiping and attached ng-click="menuToggled = false" to close menu on click/tap.

However if ng-click is attached to body, links in the menu don't work and I cannot focus any of the input fields on the body.

This only happens on mobile devices: iOS or Android (or chrome device emulation).

As I said, I will probably have to think of another solution to close menu on tap, but this issue seems strange to me, perhaps somebody has some thoughts on it.

Here's a simple demo, as I said, it works on desktop but if you enable device emulation with F12 on Chrome you will not be able to focus input field, unless you hold mouse button:

http://jsfiddle.net/L85g3grs/

<body ng-app="myApp" ng-click="showMenu = false">
    <input type="text">
    <button type="button" ng-click="showMenu = true; $event.stopPropagation();">Show menu</button>
    <div class="menu" ng-show="showMenu"></div>        
</body>

Solution

  • I can't explain the real cause of your original problem. seems that ng-click on body tag is not a good idea - i think it steals focus in some ways..

    i have put together a someway complex solution - but it works on desktop and emulated mobile - tested in Firefox - and handles the 'click + touch' problem:
    http://jsfiddle.net/s_light/L85g3grs/6/

    setup the click event on the button:

    <button type="button" ng-click="menuShow($event)">
        Show menu
    </button>
    

    and add the handling in your controller:

    app.controller('MainController',[
        '$scope',
        '$document',
        '$timeout',
    function($scope, $document, $timeout) {
    
        // using deep value so that there are no scope/childscope issues
        $scope.menu = {
            visible: false,
        };
    
        // our internal clickPrevent helper
        var menu_clickPrevent = false;
    
        function menuHide(event) {
            console.log("menuHide");
    
            // set menu visibility
            $scope.menu.visible = false;
    
            // we need a apply here so the view gets updated.
            $scope.$apply();
    
            // deactivate handler
            $document.off('click', menuHide);
        }
    
        $scope.menuShow = function(event) {
            console.log("menuShow", event);
    
            // check if we are already handling a click...
            if( !menu_clickPrevent ) {
                // stop default and propagation so our hide handler is not called immediate
                event.preventDefault();
                event.stopPropagation();
    
                // make menu visible
                $scope.menu.visible = true;
    
                // prevent 'double click' bugs on some touch devices
                menu_clickPrevent = true;
                $timeout(function () {
                    menu_clickPrevent = false;
                }, 100);
    
                // activate document wide click-handler
                $document.on('click', menuHide);
            }
        };
    
    }
    ]);