2010-11-21

Capturing right-click events in OpenLayers

I've been looking at using OpenLayers as the mapping API for an application I've been working on for some time.  One of the key features of the application requires capturing mouse click events on the map.  This is fairly normal with OpenLayers for many of its controls, but when the right-click event is involved, I would always get the default context menu from the browser, and OpenLayers didn't seem to capture the rightclick event on its own.  There are a few workarounds posted on the web...but I found they were not easily adaptable, or didn't quite work the way I wanted.  So here's what I came up with - paste the code below at the end of the init() function in any of OpenLayers' examples:


===================================================

// Get control of the right-click event:
document.getElementById('map').oncontextmenu = function(e){
 e = e?e:window.event;
 if (e.preventDefault) e.preventDefault(); // For non-IE browsers.
 else return false; // For IE browsers.
};


// A control class for capturing click events...
OpenLayers.Control.Click = OpenLayers.Class(OpenLayers.Control, {                

defaultHandlerOptions: {
'single': true,
'double': true,
'pixelTolerance': 0,
'stopSingle': false,
'stopDouble': false
},
handleRightClicks:true,
initialize: function(options) {
this.handlerOptions = OpenLayers.Util.extend(
{}, this.defaultHandlerOptions
);
OpenLayers.Control.prototype.initialize.apply(
this, arguments
); 
this.handler = new OpenLayers.Handler.Click(
this, this.eventMethods, this.handlerOptions
);
},
CLASS_NAME: "OpenLayers.Control.Click"

});


// Add an instance of the Click control that listens to various click events:
var oClick = new OpenLayers.Control.Click({eventMethods:{
'rightclick': function(e) {
alert('rightclick at '+e.xy.x+','+e.xy.y);
},
'click': function(e) {
alert('click at '+e.xy.x+','+e.xy.y);
},
'dblclick': function(e) {
alert('dblclick at '+e.xy.x+','+e.xy.y);
},
'dblrightclick': function(e) {
alert('dblrightclick at '+e.xy.x+','+e.xy.y);
}
}});
map.addControl(oClick);
oClick.activate();

===================================================



The first bit overrides the oncontextmenu event that causes the browser to display a context menu when the right-click event happens.  The next bit creates a simple control class for handling the click events, with the handleRightClicks option enabled.  After that, an instance of the control class is created, with functions for the four different click event types included in the eventMethods option.  Finally, this control is added to the map and activated...any better solutions are welcome.


EDIT: This has been corrected to utilize the 'handleRightClicks' property in the OpenLayers class. Other than this, all that is essentially needed is the overridden oncontextmenu function, which uses preventDefault (for non-IE browsers), or returns false (for IE browsers).

8 comments:

  1. Hi
    Thanks for the code. But this is not working in IE.Getting the error "which" is null

    Can you please tell what can be the reason?

    ReplyDelete
  2. Yes, it's because the 'e' object hasn't been passed by IE. I had originally had this setup in combination with jQuery, which I could use to 'fix' the event object...and after I removed the jQuery-related code, I forgot to re-test in IE.

    If you have jQuery, you can try:

    e = $.event.fix(e?e:window.event)

    Or maybe without jQuery:

    e = e?e:window.event;

    This still leaves us with one more problem - IE ignores the mouse button on the 'contextmenu' event. So you'd have to set e.which = 3 or e.button = 2 to get OpenLayers to detect the event as a right-click.

    I've actually abandoned this method somewhat, however, mostly due to the above complication. I'm going to try a different approach...right now all I'm doing in the contextmenu event is calling e.preventDefault(); in order to stop the context menu from popping up. From there, it's a matter of utilizing OpenLayers' keyMask feature with event handlers. Unfortunately, this is a bit tricky due to inconsistent use of the keyMask property in various controls. It is also a bit limiting because keyMask only allows you to limit a control to one key combination. If I want to allow an event to be triggered for more than one combination, I either have to create multiple controls, or rework the checkModifiers function in the OpenLayers Handler object...if I get something that's easily presentable, I'll post that.

    ReplyDelete
  3. Harshu: Please try this revised example - you should find it works in IE as well as other browsers (I only have IE 8 to test with though).

    ReplyDelete
  4. Thanks a lot Mgleahy.

    Now it is working in both IE and Firefox

    ReplyDelete
  5. thanks for the code, it works like a charm. what i am wondering is, how can i add a popup menu to my right click event? actually, i am trying to display two options (as, add point A and add point B) in my menu and when user selects either of them, i want to add it to my map. can you please help me with this?

    regards

    ReplyDelete
    Replies
    1. In my case, I was using jQuery to dynamically create a div tag, then use the x/y coordinates captured by the rightclick event to position the top/left style properties of the div, then I'd append html content for the menu items, use jQuery to bind to the click event for each menu item, then append/display the div on the page.

      I don't have the code readily on hand, but a line that would do it within the rightclick callback using jQuery would look something like this:

      =============================
      var div = $('div').css({top:e.xy.y,left:e.xy.x,zIndex:999});
      // Add menu items to the div, bind to the click events, etc...
      $('body').append(div);
      =============================

      If you haven't gone far down the road with you're development, I'd recommend plugging jQuery into your application. Their documentation (at jquery.com) is very thorough, with lots of examples to be found.

      Delete
  6. Great tip, thanks for posting this code. I've added this to an application and it has proven to be very useful.

    John

    ReplyDelete
  7. Hi! Thanks for the code.
    I have a question. I want to listen to middle mouse button clicks too. I should register this event in the eventMethods, but i don't know the exact name of this event. Can you help?

    ReplyDelete