74

Is there a way to have an element set up so that it performs one action on left-click (ng-click) and then another action on a right-click?

Right now I have something like:

<span ng-click="increment()">{{getPointsSpent()}}</span>

And I'd like to also be able to right click on the span to perform the function decrement();

infomofo
  • 1,675
  • 2
  • 12
  • 20

4 Answers4

140

You can use a directive to bind specific action on right click, using the contextmenu event :

app.directive('ngRightClick', function($parse) {
    return function(scope, element, attrs) {
        var fn = $parse(attrs.ngRightClick);
        element.bind('contextmenu', function(event) {
            scope.$apply(function() {
                event.preventDefault();
                fn(scope, {$event:event});
            });
        });
    };
});

Code example on fiddle

Bastien Caudan
  • 4,482
  • 1
  • 17
  • 29
  • 1
    This works great, but if I try to open a new window from the handler it gets blocked when triggered by right click and not when triggered by left click. I'm on chrome. http://jsfiddle.net/aslakhellesoy/QLHUV/3/ – Aslak Hellesøy Oct 10 '13 at 13:04
  • @AslakHellesøy Probably because the browser doesn't recognise the ng-right-click directive as a user-initiated event. – Magne Apr 17 '14 at 12:38
  • @AslakHellesøy Works for me in Chrome (version 34.0.1847.116 m) – xyhhx Apr 24 '14 at 14:37
  • This is cool. If I wanted to pass arguments to the function (say, when `decrement()` is called, I also wanted to log the x and y coordinates of the mouse click) - how would I do that? – Jer Oct 22 '14 at 21:51
  • 1
    @Jer, you can have access to the event with $event, look at this example: http://jsfiddle.net/b0sx1ae1/ – Bastien Caudan Oct 25 '14 at 10:04
  • 2
    Great answer, though I prefer to call it `ngContextmenu` (also change `attrs.ngContextmenu` and use like `ng-contextmenu="..."`) because it handles the contextmenu, and not a right-click, which should be handled by `ng-mousedown`, `ng-mouseup` and `ng-click`. – MicroVirus Feb 17 '16 at 23:45
  • I'm having issues with `ng-click` being also called when I right click. The solution is pretty straightforward... can't figure out why it works on fiddle with increment/decrement functions, but not with my functions. Any suggestions? There's not much to paste, there are just two functions with `console.log` calls. – Zhenya Feb 23 '16 at 20:35
29

Hi this is an old question but I have a solution that I think may be simpler in some cases. The ngMousedown (and ngMouseup) directives are triggered by the right mouse button and have access to the original mouse event through $event so you could do it this way:

<span ng-mousedown="handleClick($event)"
      oncontextmenu="return false">  <!-- use this to prevent context menu -->
          {{getPointsSpent()}}
</span>

Then in the controller, you can do the following:

$scope.handleClick(evt) {
    switch(evt.which) {
        case 1:
            increment(); // this is left click
            break;
        case 2:
            // in case you need some middle click things
            break;
        case 3:
            decrement(); // this is right click
            break;
        default:
            alert("you have a strange mouse!");
            break;
    }
}

Here is a working fiddle. It works the same as the accepted answer but doesn't require the creation of a whole new directive. Although a directive may be a better solution, especially if you plan to attach right-click functions to lots of things. But anyway, another option.

bradimus
  • 825
  • 11
  • 25
  • Pretty cool, thanks. For anyone having problems with it, the oncontextmenu attribute has to be exactly on the right clicked element. Had it on the container and the context menu kept opening. (Chrome 51.0.2700.0 dev-m) – Daniel Z. Apr 11 '16 at 08:51
  • 1
    @Termi2610 works for me with the oncontextmenu on the container. chrome version : 54.0.2840.71 – yannick1976 Oct 26 '16 at 12:10
  • @yannick1976 Just tested it with my current version (53.0.2785.143 m) with the same container I used in april. And yes, it works. Seems like it was a bug in the version I used before. – Daniel Z. Oct 28 '16 at 14:15
7

One way is using a directive that binds an event handler to contextmenu event. I had hard time stopping bubbling to prevent default menu to show up so added native script handler for document. Tried with e.stopPropagation(), e.preventDefault() , return false etc . Checking for target in document handler seems to work well

app.directive('rightClick',function(){
    document.oncontextmenu = function (e) {
       if(e.target.hasAttribute('right-click')) {
           return false;
       }
    };
    return function(scope,el,attrs){
        el.bind('contextmenu',function(e){
            alert(attrs.alert);               
        }) ;
    }
});
<button right-click alert="You right clciked me">Right click me</button>

DEMO http://plnkr.co/edit/k0TF49GVdlhMuioSHW7i

charlietfl
  • 170,828
  • 13
  • 121
  • 150
1

You can use this directive.

<div ng-controller="demoCtrl" save-content="classic-html">
  <div contextmenu="{{lists}}" class="box" click-menu="clickMenu(item)" right-click="rightClick($event)">
    <span>normal dropmenu</span>
  </div>
</div>

<script type="text/javascript">
angular.module('demo', ['ngContextMenu'])

  .controller('demoCtrl', ['$scope', function($scope) {
    $scope.lists = [{
      name: '11'
    }, {
      name: '22'
    }]

    $scope.clickMenu = function (item) {
      console.log(item);
    };

    $scope.rightClick = function (event) {
      console.log(event);
    };
  }])
</script>
boia
  • 43
  • 5