Stop dictation on iOS using JavaScript - javascript

I'm working on a chat app and I'm facing a weird problem with the dictation feature on iOS.
Here is the scenario:
The user focuses the input field and starts to dictate some text (using the native dictation offered by iOS)
Now they click the send button, which creates the chat messages, clears the text of the input and focuses the input again (to enable the user to write the next message right away).
But the dication doesn't seem to be stopped. When the user clicks the input field again, the text that has already been sent will re-appear and anything the user said in the meantime will also show up.
Does anyone know how to tell iOS to stop the dictation (preferably using JavaScript but a solution in Swift would also help).
Thanks!
Jesse
Minimal example:
const input = document.getElementById('input');
const button = document.getElementById('button');
button.addEventListener('click', () => {
input.value = '';
input.focus();
})
<input id="input"/>
<button id="button">Send</button>
<ul>
<li>Open this page on iOS</li>
<li>Focus the input and start dication</li>
<li>Click send => input is cleared</li>
<li>Click on the input => text is entered again :(</li>
</ul>
https://codepen.io/jjd/pen/vYZMGBy

I had the same situation recently and managed to replicate it on some chat demos around the web with the same issue, and after digging up for a while, I found old answers as well as explanations to why that occurs (and why some solutions fails).
The key here is that the iOS dictation ends when:
User lose focus of the input (taps elsewhere).
User taps on the Done button of the iOS keyboard.
While in theory tapping your Send button should remove the focus from the chat input and finish the dictation, in practice the sequence between losing input focus and reverting it is so quick that iOS doesn't bother to finish the dictation, as it recovers the focus right away anyways. And plus, adding some delay to allow the iOS keyboard to finish will end up hiding the keyboard, which could end up closing and opening many times as you send messages and handle the focus manually, which is not desirable.
If you look here, you can see a quick explanation of the subject, with a possible solution for your problem, I tested most solutions in that thread and they still works as for iOS 14. The idea that worked for me is to remove the focus on an element and focus on a similar element to avoid hiding the keyboard, and then revert it back manually, with the difference that it must be asynchronous to guarantee a proper response from iOS. The way I did it was to create a dummy text input and redirect the focus to that:
const dummy = document.createElement('input');
dummy.setAttribute('type', 'text');
dummy.style.position = 'absolute';
dummy.style.opacity = 0;
dummy.style.height = 0;
document.body.append(dummy);
dummy.focus();
Then, set an async timeout to redirect to the real input element, clear input, etc:
setTimeout(() => {
input.value = '';
input.focus();
dummy.remove(); //Notice this
}, 100);
Is hacky, but it gets the job done. I have no idea how other websites/applications handle this situation, as I only tested native applications instead of websites. Perhaps their clean code doesn't have this issue in the first place? 😛

Related

a11y & angularjs - windows screen reader overrides keydown event

im working on making a photo gallery app more accessible.
the app has a feature of showing expanded view of an image when clicked.
one of the a11y requirements is that when a user focus an image and click enter the expand mode will open and the focus will go inside the expanded view, and will be set on one of the buttons in it. it work's fine without screen reader, or with screen reader on mac. but on windows
when using screen reader it seems that the code that fires is the one that subscribed to the click event and not the keydown event. because the flag that suppose to be set to true on keydown is false (both events fire the same function but the keydown also add the enterClicked variable set to true).
this is the div that hold the image and subscribed to the events:
<div
tabindex="0"
id="{{media.id}}"
data-ng-repeat="media in row track by media.id"
data-ng-mouseenter="events.toggleVideoPlay(media.type, media.id, media.link, ( rowNummer ) * (row.length) + ($index + 1))" class="imgContainer"
ng-keydown="$event.keyCode == 13 ? events.openExpandMode(media.id, true) : null"
data-ng-click="events.openExpandMode(media.id)"
>
openExpandMode function:
$scope.events.openExpandMode = (mediaId, isEnterClicked) => {
const state = {
isEnterClicked,
mediasId,
currentIndex,
pagination: $scope.mediasPagination,
settings: {
isUserName: $scope.settings.user_name_checkbox,
isTemplate: !$scope.userConnected && !$scope.userConnectedWithOldApi,
isLikeComments: $scope.settings.like_comments_checkbox,
isDescriptions: $scope.settings.description_checkbox,
isComments: $scope.settings.comments_checkbox,
theme: $scope.settings.expand_theme,
lang: $translate.use()
}
};
localStorageService.set('state', state);
}
expand mode component init:
const _init = () => {
if ($scope.isOpenFromEnter) {
document.getElementById('nextArrow').setAttribute('data-focus-visible-added', "");
document.getElementById('nextArrow').className += ' focus-visible';
document.getElementById('nextArrow').focus();
}
}
is there a way to stop windows screen reader event interception ?
Short answer
It's common to send a click event instead of a press enter event.
The best is probably to adapt your code so that click and enter do the same thing, and that either or both event can be sent, because
you only have a quite limited influence on which is sent or not and when
Longer answer
You haven't indicated which screen reader you were using (Jaws or NVDA), but anyway, it's common for both to send a click event when pressing enter, instead of sending key events.
Reasons for that may seem strange and illogical at first, but there are at least two good ones:
It's certainly as much illogical to have two different things happening when clicking or pressing enter. IN all applications since GUI exist, most often, both do the same action (the only exception I can think of right now is multiline or rich text fields).
Scren readers existed before web accessibility, and accessibility is still rarely implemented nowadays. Sending a click event when pressing enter provide a minimal usability in all the places where designers didn't even thought that the keyboard could be used instead of the mouse.
By the way, screen reader or not, guess which event is sent if you press enter when the focus is on a link or a button?
Depending on the browser, the answer isn't unanimous as far as I know.
And on the screen reader side, it isn't unanimous either. Some even allow to configure the exact behavior to take, in order to adapt to different more or less unaccessible sites.
is there a way to stop windows screen reader event interception ?
You can stop some form of interception by calling preventDefault in your event listener function, if the click event is generated by the browser.
By doing so, you can actually do something different on click and on enter. But ask yourself first if it is really justified. Think about my first point above.
However, you can't prevent screen readers from intercepting keyboard events, translate them to something else and send or don't send them to your page.
There exists the ARIA application mode, but it has several important implications, so you shouldn't use it unless you have true good reasons.
To wrap up, the best is probably to adapt your code so that click and enter do the same thing, and that either or both event can be sent.
adding role="application" to the container div fixed it.

Cordova/Phonegap iOS - html5 input text type changed after application is sent to background

I am totally new in phonegap/cordova development and I have a problem with the keyboard on cordova webview for my app.
What I am trying to achieve is to have an input text with keyboard type that only shows numeric pad (which is type="tel") and also hides the characters that are typed into the input textbox (which is type="password"). What I have tried to achieve this behaviour is by changing the input type twice with javascript. Here's the code for that :
<script>
function changeInputType(){
document.getElementById("txtPin").setAttribute("type", "tel");
setTimeout(function(){document.getElementById("txtPin").setAttribute("type", "password");}, 750);
}
</script>
//here is how I use it on the html input
<input type = "tel" id="txtPin" onlick="changeInputType()" onkeypress="changeInputType()"/>
This dirty trick works fine when the user types on the textbox, but when the keyboard is present and I press the home button or do whatever that results to the app being sent to the background, and then I go back to open the app again, the keyboard type will change to the default text keyboard. And the javascript seems to not work anymore when I dismiss the keyboard and then open it up again by clicking on the textbox.
So, if anyone has any idea what I'm doing wrong here or has any suggestion on what I can do to fix this problem, please help.
Thank you! cheers!
I found out that to make the keyboard go back to numeric (type="tel") again is to call the javascript method on my AppDelegate through the current visible view controller like so :
//get the current visible view controller
UIViewController *viewController = ((UINavigationController*)self.window.rootViewController).visibleViewController;
if([viewController isKindOfClass:[MyViewController class]])
{
MyViewController *vc = (MyViewController *)viewController;
if(vc.webView respondsToSelector:#selector(stringByEvaluatingJavaScriptFromString:)
{
//call the javascript function on the webview
[vc.webView stringByEvaluatingJavaScriptFromString:#"changeInputType"];
}
}

How to keep on-screen keyboard from disappearing when focus changes from one text field to another

I've got an HTML5 app that uses a some input type="text" fields for entering text. I am catching keystroke events in order to link together certain fields.
I am having problems when testing this on any mobile device. Initially I tried it on an iPad using Safari or Chrome. However, later I also tried it on an android device running the latest version of Mobile Firefox and experienced the same problem.
The issue is that as soon as the input element changes, the on-screen keyboard goes away. I have tried doing this in several different ways but it always seems to produce the same effect - as soon as I focus() on a different text input, the keyboard disappears. This represents a serious impediment to flow, as it requires an extra motion to correctly re-select a text field that is already selected and should have focus and get keyboard input. On a small device such as a phone it is even worse, as all of this is accompanied by zooming and panning as the removal and re-display of the keyboard causes the page to re-render. This is similar in severity to what would happen if one's keyboard would stop working and require replugging every time one hit enter when editing a text document - it clearly would be a critical issue.
The following minimal code reproduces the problem:
<!doctype html>
<html lang="en">
<head>
<style type="text/css">
:focus { color: red; }
</style>
<script type="text/javascript">
function init() {
var tf1 = document.getElementById('tf1')
var tf2 = document.getElementById('tf2')
tf1.focus()
tf1.onkeydown = function(evt) {
if (evt.which == KeyEvent.DOM_VK_RETURN
|| evt.which == KeyEvent.DOM_VK_Q
|| evt.which == KeyEvent.DOM_VK_ENTER) {
tf2.focus()
if (evt.stopPropogation) evt.stopPropogation()
if (evt.preventDefault) evt.preventDefault()
return false
}
}
}
</script>
<body onload="init()">
<input type='text' id='tf1' value='field1' />
<input type='text' id='tf2' value='field2' />
</body>
</html>
First thing I note is that, while the initial focus() works (the element turns red) the on-screen keyboard does not appear. While this is troubling, it is not a huge deal to tap to initially tap a text field. However, as soon as I press any of the triggering keys (I have thrown an ordinary character in there to confirm that it doesn't matter what it is), suddenly the keyboard disappears. This doesn't make any sense to me, not only is a text element still focused, which requires the on-screen keyboard, but it was a keyboard event that changed the focus, which would suggest that the keyboard might still be needed - it's not like I did this from a delay or something.
I have tried onkeydown, onkeyup, and onkeypress - all exhibit the same behavior.
How can I keep the on-screen keyboard from going away? I have wasted a lot of time trying to get this one critical piece of functionality to work, and it still doesn't! Is there some sort of meta-tag I need to include or something? Or is the just a pervasive, cross-platform bug that all mobile devices have
try using the click() method instead of or after tf1.focus();
Like this:
tf1.click();
This may cause little to no software keyboard hiding.

Force cursor in form field

I am trying to force the cursor to stay in one form field. I have an onFocus in the body so when the page first loads the cursor goes to the form field but I need to figure out how to not let it be moved from that field.
I will only have one form field on the whole page. I also need the cursor to go back to that form field even if the browser is minimized and then reopened.
I have been playing with onBlur but it is not working.
Can anyone help with this?
Thanks!
Forms shouldn't be forcing users to do what they want; instead, they should adapt to users' needs and expectations. You are attempting to force the user to enter only one form field, which goes against web design rules and is a major issue with accessibility.
That said, something of the following should work:
<input type="text" id="field" />
JavaScript:
var field = document.getElementById("field");
field.onblur = function() {
field.focus();
}
Ha ha! I have it!
The trick is to wait a little bit of time after the blur, and then focus back. Fortunately, we can just set a timeout of 0 seconds, like so:
var input = document.getYourInputThatYouWantToFocusOn(),
focuser = function () {
input.focus();
};
focuser();
input.onblur = function () {
setTimeout(focuser, 0);
};
Mwahahahaha!

How can I control the action of onbeforeunload in IE?

I've got a problem about onbeforeunload recently that I need to pop up a voting page when users try to close their IE browsers.I did it by using:
<body onbeforeunload="makevote()">
And the main structure of makevote() in javascript is as follows:
function makevote()
{
comet.distruct();
if(csid != null && isvote == null)
{
window.event.returnValue = false
window.event.returnValue='press “cancel” to vote please!'
showComDiv(popvote,"popiframe",400,120,'your vote here','dovote()');
}
}
In last three months this voting function performed so ugly that I got only less than 8,000 votes from more than 4,50,000 vistors.I think the problem is, when the users try to close their browsers,the onbeforeunload property pops up a comfirm box which covered my voting box while most users click the OK button,which means close comfirming is done,as a habit.So my question is how can I control the comfirming box made by onbeforeunload myself? For example if I click the "OK" ,I'll go to the voting box instead of closing my IE.So far what I can do is only defining the message it shows.And is there any other better way to do this?Help would be greatly appreciated!
Regards
Quite simply, you can't.
This is built-in behaviour, designed to only allow very minimal changes for security purposes. It's the same in every browser; FF, Chrome, etc, all will behave the same way.
The primary purpose for the beforeunload is for things like allowing the users the option to save changes before their changes are lost.
Besides, if your users are leaving, it's already too late - they won't want to answer a survey anymore, they're done!

Categories

Resources