Disabling the context menu on long taps on Android
Asked Answered
P

12

68

I would like to disable the context menu that appears after a long tap (touch and hold) on images in my web application. I've seen posts with different ideas how to do it, but none of them seem to work for me.

Is there a way to do this on Android via HTML/CSS/Javascript?

Pinnace answered 5/8, 2010 at 10:8 Comment(0)
D
41

This should work on 1.6 or later (if I recall correctly). I don't believe there's a workaround for 1.5 or earlier.

<!DOCTYPE html>
<html>
<head>
  <script>
    function absorbEvent_(event) {
      var e = event || window.event;
      e.preventDefault && e.preventDefault();
      e.stopPropagation && e.stopPropagation();
      e.cancelBubble = true;
      e.returnValue = false;
      return false;
    }

    function preventLongPressMenu(node) {
      node.ontouchstart = absorbEvent_;
      node.ontouchmove = absorbEvent_;
      node.ontouchend = absorbEvent_;
      node.ontouchcancel = absorbEvent_;
    }

    function init() {
      preventLongPressMenu(document.getElementById('theimage'));
    }
  </script>
</head>
<body onload="init()">
  <img id="theimage" src="http://www.google.com/logos/arthurboyd2010-hp.jpg" width="400">
</body>
</html>
Democritus answered 9/8, 2010 at 8:46 Comment(12)
Not seen anything of the OP since Aug 8th, so I decided to test this myself using the SDK. I tested 1.5, 1.6 and 2.2 and it worked just fine on all of them, so I'm happy to award the bounty now without waiting for the OP to confirm. +1 for a good answer, too.Psychosexual
Roman, thank you very much for your response. I need to test it (not that I disregard Andy's test, but I've seen many things that work fine with the SDK and then fail on the actual devices). I'm sorry for taking so long to respond but I promise to test this before the weekend. Thanks again to both you and Andy!Pinnace
@Andy -- thanks, @Roy -- sure thing; I tested on a Nexus One, I'd recommend testing on devices with non-standard default Browsers too, @Tegeril -- long time no see!Democritus
@Roy: you're welcome to use the test that I set up if you like. jsfiddle.net/M7pfy/1 - the top image should cancel the context menu, the bottom image should allow it.Psychosexual
@Andy -- thanks again! @Roman -- I've tested your solution using Andy's page on two Motorola Blur models. The one with Android 1.5 worked fine, and the one with 2.1-update1 did not work. Damn. I really wanted this to work. Sometimes I wish that the Android world would have been more like the iPhone world, with its single flavor... So what do I do now in terms of stackoverflow customs: Should I mark Roman's solution as an answer or not? I guess that his idea is the way this should be done, but on the other hand it does not solve my problem... Thanks for the advice.Pinnace
@Roy: that seems really odd, for it to work for me in 1.5, 1.6 and 2.2 but not 2.1 for you. You don't have to accept Roman's answer, although you could accept it and ask another question regarding the problems you're having with the solution. The best idea would be to perform a broader test, if possible - more versions on more devices. Asking another question and providing a link to the example might help here. If it works on other devices, then you know this is the correct solution and the issue is with the device it didn't work on.Psychosexual
@Andy: Sounds like a good idea. I've accepted Roman's answer, and will try to perform a broader test. If it seems like a device bug, then so be it. If not, I will post another -- more specific -- question. Thanks again for the warm welcome for a newcomer to stackoverflow!Pinnace
One potential issue I've found with this solution is if the user does any touch command above the image, such as trying to scroll, or zoom, then their command will fail.Decemvir
This solution disables all touch events. This could be limiting !Misdeed
Is this really the right answer?. It seems like img.addEventListener('contetxmenu', function(e) { e.preventDefault(); return false; }, false); should work no? The solution above will prevent all input.Munitions
You see this kind of thing a lot, but you really want to avoid interfering with the propagation of events if you can. A given event can be significant for "your" component as well as others on the page. Eg, a click outside a dropdown may be the queue to roll up the dropdown, as well as whatever action corresponds directly to the click. Find a way that doesn't totally destroy event handling on your page.Misdeed
This prevents every generated event (i.e. click), not just contextmenu.Coming
M
186

The context menu has its own event. You just need to catch it and stop it from propagating.

window.oncontextmenu = function(event) {
     event.preventDefault();
     event.stopPropagation();
     return false;
};
Misdeed answered 26/2, 2015 at 16:55 Comment(7)
works! But now I want to block the context menu only on anchor elements. I have the filter working (return false when [a], else true) but returning true doesn't show the dialog. so.. how do you tell the browser it should show?Pinniped
I wouldn't put a filter in the function. It would be better and cleaner to attach the function only to the elements whose context menus you want to disable.Misdeed
Working on Win10/UWP/MS Edge WebView for Cordova Windows :)Cline
How can you use this in a React Class Component?Editor
didn't work on firefox android for me.Misology
BE WARNED! This method has a side effect on phones which is the annoying vibration. It makes the device vibrate with every long touch as if contextmenu would show. Vote this comment up for others to be warned as well.Littoral
@HolyResistance this sounds like a limitation of the method, not a side-effect. If the phone doesn't let you manage the haptic vibration from the browser, that's a limitation, but you should probably try and live with it. The web platform has moved past lots of limitations like this, and it may sort itself out with time.Misdeed
D
41

This should work on 1.6 or later (if I recall correctly). I don't believe there's a workaround for 1.5 or earlier.

<!DOCTYPE html>
<html>
<head>
  <script>
    function absorbEvent_(event) {
      var e = event || window.event;
      e.preventDefault && e.preventDefault();
      e.stopPropagation && e.stopPropagation();
      e.cancelBubble = true;
      e.returnValue = false;
      return false;
    }

    function preventLongPressMenu(node) {
      node.ontouchstart = absorbEvent_;
      node.ontouchmove = absorbEvent_;
      node.ontouchend = absorbEvent_;
      node.ontouchcancel = absorbEvent_;
    }

    function init() {
      preventLongPressMenu(document.getElementById('theimage'));
    }
  </script>
</head>
<body onload="init()">
  <img id="theimage" src="http://www.google.com/logos/arthurboyd2010-hp.jpg" width="400">
</body>
</html>
Democritus answered 9/8, 2010 at 8:46 Comment(12)
Not seen anything of the OP since Aug 8th, so I decided to test this myself using the SDK. I tested 1.5, 1.6 and 2.2 and it worked just fine on all of them, so I'm happy to award the bounty now without waiting for the OP to confirm. +1 for a good answer, too.Psychosexual
Roman, thank you very much for your response. I need to test it (not that I disregard Andy's test, but I've seen many things that work fine with the SDK and then fail on the actual devices). I'm sorry for taking so long to respond but I promise to test this before the weekend. Thanks again to both you and Andy!Pinnace
@Andy -- thanks, @Roy -- sure thing; I tested on a Nexus One, I'd recommend testing on devices with non-standard default Browsers too, @Tegeril -- long time no see!Democritus
@Roy: you're welcome to use the test that I set up if you like. jsfiddle.net/M7pfy/1 - the top image should cancel the context menu, the bottom image should allow it.Psychosexual
@Andy -- thanks again! @Roman -- I've tested your solution using Andy's page on two Motorola Blur models. The one with Android 1.5 worked fine, and the one with 2.1-update1 did not work. Damn. I really wanted this to work. Sometimes I wish that the Android world would have been more like the iPhone world, with its single flavor... So what do I do now in terms of stackoverflow customs: Should I mark Roman's solution as an answer or not? I guess that his idea is the way this should be done, but on the other hand it does not solve my problem... Thanks for the advice.Pinnace
@Roy: that seems really odd, for it to work for me in 1.5, 1.6 and 2.2 but not 2.1 for you. You don't have to accept Roman's answer, although you could accept it and ask another question regarding the problems you're having with the solution. The best idea would be to perform a broader test, if possible - more versions on more devices. Asking another question and providing a link to the example might help here. If it works on other devices, then you know this is the correct solution and the issue is with the device it didn't work on.Psychosexual
@Andy: Sounds like a good idea. I've accepted Roman's answer, and will try to perform a broader test. If it seems like a device bug, then so be it. If not, I will post another -- more specific -- question. Thanks again for the warm welcome for a newcomer to stackoverflow!Pinnace
One potential issue I've found with this solution is if the user does any touch command above the image, such as trying to scroll, or zoom, then their command will fail.Decemvir
This solution disables all touch events. This could be limiting !Misdeed
Is this really the right answer?. It seems like img.addEventListener('contetxmenu', function(e) { e.preventDefault(); return false; }, false); should work no? The solution above will prevent all input.Munitions
You see this kind of thing a lot, but you really want to avoid interfering with the propagation of events if you can. A given event can be significant for "your" component as well as others on the page. Eg, a click outside a dropdown may be the queue to roll up the dropdown, as well as whatever action corresponds directly to the click. Find a way that doesn't totally destroy event handling on your page.Misdeed
This prevents every generated event (i.e. click), not just contextmenu.Coming
P
9

For me, absorbing all the events was not an option since I wanted to disable long press downloads while still allowing the user to zoom and pan on the image. I was able to solve this with css and html only by layering a "shield" div on top of the image like so:

<div class="img-container">
  <div class="shield"></div>
  <img src="img-file.jpg">
</div>

<style>
.img-container { position: relative; }

img { max-width: 100%; }

.shield {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    z-index: 1;
}
</style>

Hope this helps someone!

Paleobotany answered 12/7, 2013 at 15:29 Comment(2)
You may need to add a position:relative to the parent container in order to prevent the parent to take over all the space. This is specially tricky if the parent is a td which has a an ontouchstart or other interaction event handler linked to it, since you won't visually see that the td is taking up all that area.Menarche
Good point, Daniel. I added the position: relative css rule.Paleobotany
S
9

That can be done using CSS:

img {
  pointer-events: none;
}
Sheepwalk answered 14/4, 2015 at 14:21 Comment(1)
This doesn't only disable the long tap, but also e.g. the normal tap, so not always an option.Aeriell
K
8

It will disable copy, but do not disable selection.

document.oncontextmenu = function() {return false;};

Works in webView.

Kwa answered 3/7, 2019 at 15:36 Comment(1)
This doesn't prevent the event itself though: the currently active touch event still gets killed off, a touchend fires, and then the context menu is prevented from showing after having already ruined the party.Isolt
E
5

I use the complete example by Nurik but the the element (an input image in my page) was disable for the click too.

I change the original line by this:

original line:

node.ontouchstart = absorbEvent_;

my change:

node.ontouchstart = node.onclick;

with this approuch i disable the pop-up save image menu on logpress but keep the click event.

I´m testing on a 7" tablet with Android 2.2 under a Dolphin HD browser and works fine!

Ethnomusicology answered 30/1, 2012 at 18:13 Comment(0)
A
4

Use this CSS codes for mobile

-webkit-touch-callout: none;
-webkit-user-select: none; /* Disable selection/copy in UIWebView */
Amaliaamalie answered 6/11, 2014 at 13:49 Comment(1)
This doesn't work for the requested browser, chrome.Misdeed
I
4
pointer-events: none; // for Android

-webkit-touch-callout: none; // for iOS

-webkit-user-select: none; 

-khtml-user-select: none; 

-moz-user-select: none; 

-ms-user-select: none; 

user-select: none;
Ibbie answered 12/6, 2017 at 14:55 Comment(2)
please add some description explaining answer.Toucan
pointer-events: none; is essentially the return false of CSS for elements, so it makes my button not work in this case.Jiggle
M
1

I've had a similar issue. I've tried couple of solution from this thread and another thread for safari on the same problem (Preventing default context menu on longpress / longclick in mobile Safari (iPad / iPhone)) . The bad part was that I couldn't use onTouchStart,onTouchEnd etc...

Only prevent the event from onContextMenu. Snippet from React 16.5.2. Tested in chrome only.

    <img {...props} onContextMenu={event => event.preventDefault()}
    onTouchStart={touchStart}
    onTouchEnd={touchEnd} />

Hope it helps somebody. Cheers!


Mcfall answered 19/11, 2018 at 15:42 Comment(1)
The question was about plain JS though, not React, which matters since React's event model is wildly different from the browser's event model. React intercepts all page events and kills them off, then uses them to run it's own event handling instead.Isolt
P
0
<a id="moo" href=''> </a>

<script type="text/javascript">
    var moo = document.getElementById('moo');

    function handler(event) {
        event = event || context_menu.event;

        if (event.stopPropagation)
            event.stopPropagation();

        event.cancelBubble = true;
        return false;
    }

    moo.innerHTML = 'right-click here';

    moo.onclick = handler;
    moo.onmousedown = handler;
    moo.onmouseup = handler;
</script>

Capture the onContextMenu event, and return false in the event handler.

You can also capture the click event and check which mouse button fired the event with event.button, in some browsers anyway.

Prestissimo answered 9/8, 2010 at 6:21 Comment(1)
Sorry, but both of these methods do not work on Android. The oncontextmenu event is never fired, and the click event is not yet fired when the user is touch-holding the element.Pinnace
A
0

Just had a similar problem. The above suggestions did not work for me in the Andoid browser, but I found that displaying the problematic image as a CSS background image rather than an embedded image fixed the problem.

Ambi answered 4/9, 2016 at 22:3 Comment(0)
J
-5

Through raw javascript there are no events that get called for the context menu. Perhaps in the Java world there is something... There are actually several issues regarding javascript events (such as focus not working right) in the Android webkit.

Jimmiejimmy answered 9/8, 2010 at 6:42 Comment(1)
Yeah, I agree. Since I've developed a JavaScript library that is going to be used inside a browser, using java is not an option for me.Pinnace

© 2022 - 2024 — McMap. All rights reserved.