How to disable iOS "Shake to Undo" in a webapp - javascript

I'm building a game as a webapp for the iPad. It incorporates a shake action to trigger an event for one of the screens, but, since it also has a information collection form, sometimes that shake action triggers the annoying "Shake to Undo" dialog. The form input the user has typed in don't even exist in the DOM anymore when the user reaches the shake action, so in my mind the Undo function shouldn't be triggered, but unfortunately it is. There is no way this can be wrapped in a native wrapper (PhoneGap or other), so I was wondering if anyone knows if it's at all possible to disable this functionality for a specific web page/app, through CSS or JavaScript?
Thanks.

For PhoneGap I just inserted this line:
[UIApplication sharedApplication].applicationSupportsShakeToEdit = NO;
in the webViewDidFinishLoad class and it worked wonders for me.
Just go to the MainViewController.m and change webViewDidFinishLoad to look like this:
- (void)webViewDidFinishLoad:(UIWebView*)theWebView
{
// Black base color for background matches the native apps
theWebView.backgroundColor = [UIColor blackColor];
[UIApplication sharedApplication].applicationSupportsShakeToEdit = NO;
return [super webViewDidFinishLoad:theWebView];
}

This blog describes a very simple shake detection using the DeviceMotionEvent listener:
http://www.jeffreyharrell.com/blog/2010/11/creating-a-shake-event-in-mobile-safari/
Try the following code to disable to undo event happening:
window.addEventListener('devicemotion', function (e) {
// This is where you put your code for dealing with the shake event here
// Stop the default behavior from triggering the undo dialog (hopefully)
e.preventDefault();
});
There is one other thing I can think of which would be to turn the text input into a readonly item after you are done with it. Presumably, a read only input field cannot make use of the undo function, though I haven't tested it. It's odd that the feature fires even after the input is no longer part of the DOM.
I'm not sure if preventing a shake action can be done through the -webkit- properties in CSS. Here is a list of webkit specific CSS properties (which may not contain 100% of the properties accessible).
http://qooxdoo.org/documentation/general/webkit_css_styles

I recently ran into this issue while developing a game where a user pairs devices using WebSockets. Unfortunately none of the solutions mentioned above work.
In short, the game involved a user subscribing to a channel via a form on their phone. Once subscribed they would shake the device and a scenario would play out on their desktop. The problem was that any time someone shook an iOS device the "undo typing" alert would pop up.
Here are the only two solutions that I've found for this issue.
Prevent your input from ever losing focus. This will require you to prevent form submission if the input is part of a form. You must also listen for "touchstart" instead of "click" on any anchors and prevent the touchstart event from propagating. This will prevent that anchor from gaining focus and your input from losing focus. This approach has some disadvantages for sure.
Add a "keydown" event handler to the window and prevent the event from propagating. This prevents any values from getting inserted into the input on iOS devices. I also had to create an object that mapped the keycodes to the proper character. This is the route I ended going because all I needed in my input was a 6 character code so I only needed numbers. If you need more than just numbers this may be a pain in the ass. On key down, snag that character via the keycode and set the value of the input. This tricks iOS into thinking that no value was ever inserted into the input via the keyboard so there is nothing for it to undo.
Keep in mind preventing the keydown event from propagating does not prevent input on the stock android browser, so it will just function normally. But that is OK since this is not an android issue. You can prevent duplicate values from getting inserted by listening for the input event on the input itself and removing the keydown listener if this event is received.
var codes = {
48: 0,
49: 1,
50: 2,
51: 3,
52: 4,
53: 5,
54: 6,
55: 7,
56: 8,
57: 9
},
myInput = document.getElementById("myInput");
var keydownHelper = function (e) {
e.preventDefault();
myInput.removeEventListener("input", inputHelper);
var val = myInput.value;
// Delete
if (e.keyCode === 8 && val.length) {
myInput.value = val.slice(0, val.length - 1);
return;
}
// If not a number, do nada
if (typeof codes[e.keyCode] === "undefined") { return; }
val += codes[e.keyCode];
myInput.value = val;
};
var inputHelper = function (e) {
e.preventDefault();
window.removeEventListener("keydown", keydownHelper);
};
myInput.addEventListener("input", inputHelper);
window.addEventListener("keydown", keydownHelper);

Objective-C
Just add the line of code inside "- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions"
[application setApplicationSupportsShakeToEdit:NO];
Swift 2.x
Add single line of code in appDelegate.swift didFinishLaunchingWithOptions method
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
application.applicationSupportsShakeToEdit = false
return true
}
Swift 3.x
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
application.applicationSupportsShakeToEdit = false
return true
}

Perhaps try event.preventDefault(); in your DeviceMotionEvent listener ?
Looking around Stack Overflow, I can see other people have had success disabling default "events" using CSS (oddly).
Prevent default Press but not default Drag in iOS MobileSafari?

See: How to stop the UITextField from responding to the shake gesture?
For phonegap, set this property within the webViewFinishLoad method in AppDelegate.m

First solution
The easiest way is to use this plugin:
https://github.com/nleclerc/cordova-plugin-ios-disableshaketoedit
Second solution
If you don't want to use the above plugin then you should do it manually by opening the MainViewController.m and change webViewDidFinishLoad to look like this:
- (void)webViewDidFinishLoad:(UIWebView*)theWebView
{
// Black base color for background matches the native apps
theWebView.backgroundColor = [UIColor blackColor];
[UIApplication sharedApplication].applicationSupportsShakeToEdit = NO;
return [super webViewDidFinishLoad:theWebView];
}
Third solution
it's another solution i designed to disable "Shake To Undo" which works on text inputs. note that users are not allowed to insert emojis.
<body>
<textarea cols="40" rows="8" name="textarea" id="textarea"></textarea>
<script>
textArea = document.getElementById("textarea");
textArea.onkeypress = function(event){
event.preventDefault();
var nationUnicode=event.which;
var utf8=encodeURIComponent(String.fromCharCode(parseInt(nationUnicode, 10)));
if (utf8.search("%EF") != 0) //value is not an Emoji
textArea.value= textArea.value + String.fromCharCode(nationUnicode);
}
textArea.onkeydown = function (event){
if (event.keyCode == 8){
event.preventDefault();
var txtval = textArea.value;
textArea.value = txtval.slice(0, - 1);
}
}
</script>
</body>

I found a workaround that works consistently: you can host the text input in a frame. When you're done with the input, remove the frame from the page. This seems to do the trick.
function resetIframe() {
// Remove the old frame
document.body.removeChild(frame);
// Create a new frame that has an input internally
frame = document.createElement('iframe');
frame.src = '/html-with-input.html';
frame.style = 'border: 0; height: 30px; width: 200px;'
document.body.appendChild(frame);
// When the frame is ready, grab a reference to the input
frame.addEventListener('load', () => {
const input = frame.contentDocument.body.querySelector('input');
console.log(frame, frame.contentDocument.body)
// When the input changes, grab the value
input.addEventListener('input', e => {
document.getElementById('output').innerText = e.target.value;
});
});
}
Here's a proof of concept: https://codepen.io/joshduck/full/RJMYRd/

Put the input into an iframe, reload the iframe when you don't need the input.

Related

How to keep the text cursor inside an html input field to the right while the user is writing?

Basically I have an input text (I am actually working in react, but probably the solution would be the same or very similar in plain html5-javascript) and I want that the text cursor remains on the right permanently when the users clicks on it, and that, even if the users clicks in another position of the input's content once he wrote something, the text cursor is still moved instantly to the right end.
I tried different solutions I found around but nothing seems to work.
I tried using the onFocus event passing a function like this as suggested in some questions on this site:
repositionCursorToTheRight = (e) => {
const val = e.target.value;
e.target.value = '';
e.target.value = val;
};
with the idea that resetting the input would force the text cursor to reset its position, but it doesn't happen (not in firefox at least).
I tried working with event.target.selectionStart and event.target.selectionEnd setting them to different values like the full lenght of the input value, 0, etc... but that seems too to have no effect at all.
Any ideas?
Edit: I'll explain better the reasons and give an example, I just want to specify this is a client business request and I cannot override it.
So I need this because I have an input form which must have 2 fixed decimals which must be always showed even if at zero. The client want a form like those of most currency converters apps in which if the user wants to write, for example, 12.35 it should see in order 0.00(default value) -> 0.01 -> 0.12 -> 1.23 -> 12.35. Hope it is clear now
You can test with this, I have tested this in Chrome, Edge, Firefox and IE 11.
<script>
// Get all inputs
var inputs = document.querySelectorAll('input');
// Add event listeners
for (var i = 0; i < inputs.length; i++)
{
inputs[i].addEventListener('focusin', resetCursor, false);
inputs[i].addEventListener('click', resetCursor, false);
}
// Reset a cursor to the end
function resetCursor(event)
{
setTimeout(function () { event.target.selectionStart = event.target.value.length; }, 10);
}
</script>

addeventlistener does not work when browsing on Android?

Please see the code below:
document.getElementById("FirstName").addEventListener("keydown", alphaOnly, false);
document.getElementById("Surname").addEventListener("keydown", alphaOnly, false);
function alphaOnly(event) {
event.preventDefault();
}
It works on my local PC using Chrome; IE; Edge and Firefox i.e. the user is unable to enter characters into the two fields.
However, when I browse using my Android (using Chrome or Samsung Internet browser), then I can enter into these fields. Why is this?
I Googled this and came across this: AddEventListener works on the browser but does not on Android, however there is no accepted answer.
I have Android 5.0 API 21 if that makes any difference. Would it make any difference if I used unobtrusive Javascript?
Update
I have just come across this: https://forums.expo.io/t/linking-addeventlistener-url-never-fires-in-android/7442. Could this be because I have API 21 (see GitHub link).
This is because your Android has no real keyboard attached, it has a virtual one and it does not fire key- events.
Since you're working with <input>, listen for input event instead of keydown or keyup. It solves your problem and also helps to handle paste.
document.getElementById("FirstName").addEventListener("input", alphaOnly, false);
document.getElementById("Surname").addEventListener("input", alphaOnly, false);
function alphaOnly(event) {
event.target.value = event.target.value.replace(/[^a-z]/ig, '');
}
<input id="FirstName"><br>
<input id="Surname">
If your goal is to block user input, the best option would be the “disabled” attribute (<input disabled/>). In JavaScript you can change it as follows:
document.getElementById('FirstName').disabled = true;
If you want to validate input and accept only some characters:
document.getElementById('FirstName').addEventListener('keyup', function(event) {
var rgxp = /[^a-z]/g;
if (rgxp.test(this.value)) {
// This keeps previous value
this.value = this.value.replace(rgxp, '');
// If you do not want to keep old value, use the following:
// this.value = '';
}
});
<input id="FirstName" />

Chrome Extension for Facebook-Clone (automatic "User is listening to...")

I'm building a Chrome Extension to replace the current chat's input box with the music I'm currently listening to by the press of "´". Please keep in mind that like in the actual desktop version of Facebook, there is no input/textarea tag element and it's made with react.
I'm stuck at properly replacing the text in the private Facebook-Clone's input, I can see it there, but when I press Enter, it doesn't send.
Screencast of what happens at the moment:
https://vimeo.com/203014549
This private Facebook-Clone works exactly like the actual desktop version of Facebook in regards to the Chat.
content.js:
$(function() {
if (window.jQuery) {
$('body').append("<div class='facemusic_tip' style='position:fixed;min-width:400px;width:400px;min-height:20px;height:20px;overflow:scroll;background-color:#000000;top:0;left:0;padding:10px;z-index:99999;color:white'>Press ´ in your message input to inject current song</div>");
function findAncestor(el, cls) {
while ((el = el.parentElement) && !el.classList.contains(cls));
return el;
}
$(document).on("keydown", function(e) {
if(e.which === 229){
var currentmusic = getCurrentMusicAsString();
keytarget = findAncestor(e.target, 'fbNubFlyoutOuter');
var $chatbox_outer = $(keytarget);
$chatbox_outer.parent('.fbDockChatTabFlyout.uiContextualLayerParent').find("._5rpu").html("I'm listening to: "+currentmusic);
}
});
};
});
UPDATE:
It seems to be something about react that I don't quite understand, because on enter it just gets replaced with the characters populated by manual keystroke and my manipulated version of the input field is gone, see video for bug.
The ._5rpu element is a contentEditable with auxiliary required elements inside so you can't just nuke its contents by replacing with a new html.
Use document.execCommand instead:
document.execCommand('insertText', false, 'blablabla');
In case you want to insert the text in an inactive input area, focus it first:
document.querySelector(
'#feedx_container textarea, ' +
'#feedx_container [contenteditable="true"]'
).focus();

Not returning focus to browser after alert pop-up

I am trying to do a little pacman game to train my JavaScript skills. To move pacman around, I use the onkeyup event function associated with a move function.
Still I am having a problem. Every time pacman dies (or game ends) I always use an alert box to alert the user of what happened. Still, after I press the OK button to make pacman move again I must click on the browser window to make it responsive to keyboard events.
Somehow I believe this is a windows focus problem, still, tried to use some focus around (after the alert box mainly) and doesn't seem to do the trick.
Can someone give me general pointers of what could be the solution for this?
PS: In the game construction I am using images created on the fly, having only 2 text tags on the page with id's.
EDIT: code associated in the onkeyup:
function muda_sentido(event)
{
//baixo
if(event.keyCode == 40)
{
pacman_status = status[2];
imagem_pacman.src = "Imagens/pacmanb.gif";
imagens[pacman_pos[0]][pacman_pos[1]].src = "Imagens/pacmanb.gif";
}
//direita
else if(event.keyCode == 39)
{
pacman_status = status[0];
imagem_pacman.src = "Imagens/pacmand.gif";
imagens[pacman_pos[0]][pacman_pos[1]].src = "Imagens/pacmand.gif";
}
//cima
else if(event.keyCode == 38)
{
pacman_status = status[3];
imagem_pacman.src = "Imagens/pacmanc.gif";
imagens[pacman_pos[0]][pacman_pos[1]].src = "Imagens/pacmanc.gif";
}
//esquerda
else if(event.keyCode == 37)
{
pacman_status = status[1];
imagem_pacman.src="Imagens/pacmane.gif"
imagens[pacman_pos[0]][pacman_pos[1]].src = "Imagens/pacmane.gif";
}
}
html body contents is a double h2 tag (cannot add it to code format cause it appears messed up on the previewer) with an ID each.
And around the program I have something like:
alert("Pacman died!")
And the problem is after this.
2 EDIT: Managed to grab the piece of code that lies the alert box (just changed it now to look for it in only one place):
function pacman_morreu()
{
alert("O pacman morreu!");
if(pacman_vidas==0)
{
alert("Game Over!");
reinicia_tabuleiro();
}
else
{
pacman_vidas--;
vidas.innerHTML = "Número de vidas: "+ pacman_vidas;
}
pintaEcra();
}
The functions listed inside the code will only do matrix manipulation operations (nothing special really).
EDIT 3: As requested, here goes:
function reinicia_tabuleiro()
{
pacman_vidas = 3;
vidas.innerHTML = "Número de vidas: 3";
pontuacao.innerHTML = "Pontuação: 0";
pontos = 0;
for(i=1;i<24;i++)
{
for(j=1;j<24;j++)
{
if(tabuleiro[i][j] == 0)
tabuleiro[i][j] = 2;
}
}
}
Sorry dude, I don't see anything that would be causing this. What browser are you using? I'm doing similar tests on IE and FF and am getting window focus after clicking 'Ok' in the alert box. Have you tried using Firebug? It is a great program for debugging javascript. I would highly recommend it for this pacman project you are doing. You might have an error somewhere in your code that is causing this that isn't very apparent in the code shown above.
Well right now, I don't have solution to this problem but I have a workaround.
Dont use alertbox, use lightbox(div with higher z-index value) and make the div show and hide based on condition in javascript.
I hope this will help.
I just went through a similar problem with keydown and Firefox 5.0. The window focuses when the page loads and all my $(window).keydown(...) events worked fine.
Then after I show an alert, none of the events fire. It ends up that the window does not focus itself after I dismiss the alert. I was able to solve it by forcing the window to refocus when it blurs. I'm using jQuery so here's what it looks like for me:
$(window).blur(function(){
setTimeout(function(){
$(window).focus();
}, 0);
});
The timeout is necessary since Firefox (& maybe other browsers) won't let you immediately focus during a blur event.
This may have unintended consequences since you're basically never letting the window blur. For example, clicking on the location bar won't even work because the window pulls focus back immediately. To dig a deeper hole, you might set a "refocus" flag for your callback so it knows whether to give up focus or not. Here's the basic idea:
var doRefocusWindow = false;
$(window).blur(function(){
if(!doRefocusWindow) return;
doRefocusWindow = false;
setTimeout(function(){
$(window).focus();
}, 0);
});
...
doRefocusWindow = true;
alert("Game Over");

Disabling Paste option from the Context menu

I have one asp.net application, in which i have to disable or make it as read only to the Paste option from the context menu. But i searched in internet. I didn't get one exact method. Please help me for resolve this issue. Thanks in advance
The short answer is that you can't do this.
The user's browser is their own, and as such they have the sovereign power to interact with your page however they want. You can catch right-click events to prevent the menu from coming up; you can catch keypress events to stop Ctrl-V (and Shift-Insert, which is often forgotten) from being registered. However, you can't remove the Edit -> Paste menu option, which works within the browser and subverts Javascript altogether.
Not to mention that the user could just disable Javascript temporarily, paste into the field, then reenable Javascript if they wanted.
Even if you're happy with the limitations, you cannot modify the actual context menu, only prevent the right click from registering at all.
I suppose isn't possible to disable a single item on context menu, so I think you should try to disable the whole context menu, e.g.
$(document).bind("contextmenu", function(e) {
e.preventDefault();
});
∎ Another way of solving this pesky problem,
using JavaScript's Object-Oriented characteristics
<div class="ro-editable">
<span.....</...
<pre....</...
</div>
<script>
function make_editable_readonly(element){
element.backup_innerHTML = element.innerHTML; /* backup before editable. */
element.contenteditable = "true"; /* editable. CTRL+A is usable (FUN!) */
element.onautocomplete /* what in the world could change the content? actually lot!.. */
= element.onchange
= element.oncontextmenu
= element.oncopy
= element.oncuechange
= element.oncut
= element.ondrag
= element.ondragend
= element.ondrop
= element.onemptied
= element.onkeyup
= element.onmouseup
= element.onpaste
= element.onreset = function restore(){
if (element.innerHTML !== element.backup_innerHTML)
element.innerHTML = element.backup_innerHTML;
};
}
make_editable_readonly(
document.querySelector('div.ro-editable')
);
</script>
∎ Easy! Right? (and with no 💩y key-event "filtering" ☺):
∎ And here is a JSFiddle to play⚽ with
∎ taken from iCompile - ContentEditable And "ReadOnly"? YES! ☕

Categories

Resources