30

I have several input fields in line that acts like a crossword answer line:

enter image description here

Each square has its own input field. The reason for this is amongst other things that sometimes a square can be pre-populated. Now, on desktop browser the cursor jumps to the next input field whenever a char is entered. That works really well using something like:

$(this).next('input').focus();

But the problem on mobile safari (we test on ios) is that I don’t know how to automatically "jump" to the next input field programatically. The user can do it via the the "next" button, but is there a way to do this automatically?

I know that the focus() trigger has some limitations on ios, but I’ve also seen some workaround using synthesized clicks etc.

danronmoon
  • 3,814
  • 5
  • 34
  • 56
David Hellsing
  • 106,495
  • 44
  • 176
  • 212

7 Answers7

30

I found a workaround that might work for you.

Apparently IOS/Safari only "accepts" the focus when inside a touch event handler. I triggered a touch event and inserted the .focus() inside it. I tried this on my iPhone3S and iPhone5S with Safari and it works:

var focused = $('input:first'); //this is just to have a starting point

$('button').on('click', function () { // trigger touch on element to set focus
    focused.next('input').trigger('touchstart'); // trigger touchstart
});

$('input').on('touchstart', function () {
    $(this).focus();   // inside this function the focus works
    focused = $(this); // to point to currently focused
});

Demo here
(press next button in demo)

Community
  • 1
  • 1
Sergio
  • 28,539
  • 11
  • 85
  • 132
  • 11
    this works because your start event is a click (mouse event). If you try to use other event as start event (besides mouse events) it will not work. This link may help: http://www.quora.com/Mobile-Safari-iPhone-or-iPad-with-JavaScript-how-can-I-launch-the-on-screen-keyboard – pauldcomanici Jan 06 '14 at 12:55
  • 1
    @darkyndy Does it matter if the "starting" click event is real or is it ok if it is triggered? – Wytze Feb 08 '14 at 17:25
  • @Wytze - from what I've seen it matters, must be a real event (user interaction) – pauldcomanici Feb 14 '14 at 08:23
  • does not work for me with a click event that I raise from my code – Suhas Apr 08 '14 at 15:20
  • 1
    Very clever -- unfortunately didn't work for my case -- I want to navigate on keyboard entry (eg, n or N or ENTER). See http://jsfiddle.net/EbU6a/202. – Moos Jun 19 '14 at 23:00
  • @darkyndy The following might be worth exploring: chrome for iOS has a custom input accessory for the keyboard for the webView. So naturally it had to reimplement the "Next" "Previous" buttons in this accessory to help user move to the adjacent element. This part must have been done in Javascript, so there IS a way. – Novellizator Jun 08 '16 at 11:12
  • 4
    I can't believe this has got 20 upvotes , why bother creating touchevents and stuff ? just put the focus inside the onClick and it will work , the problem is no one has the click event in their code – Milad Jul 06 '16 at 00:42
  • @Suhas it works good also with `Enter`, check here: http://jsfiddle.net/EbU6a/1105/ – Sergio Jan 08 '17 at 09:01
  • @Milad if you have another suggestion please post a answer. – Sergio Jan 08 '17 at 09:05
10

Programmatically moving to the next input field in a mobile browser without dismissing the keyboard appears to be impossible. (This is terrible design, but it's what we have to work with.) However, a clever hack is to swap the input element positions, values, and attributes with Javascript so that it looks like you are moving to the next field when in fact you are still focused on the same element. Here is code for a jQuery plug-in that swaps the id, name, and value. You can adapt it to swap other attributes as necessary. Also be sure to fix up any registered event handlers.

$.fn.fakeFocusNextInput = function() {
    var sel = this;
    var nextel = sel.next();
    var nextval = nextel.val();
    var nextid = nextel.attr('id');
    var nextname = nextel.attr('name');
    nextel.val(sel.val());
    nextel.attr('id', sel.attr('id'));
    nextel.attr('name', sel.attr('name'));
    sel.val(nextval);
    sel.attr('id', nextid);
    sel.attr('name', nextname);
    // Need to remove nextel and not sel to retain focus on sel
    nextel.remove();
    sel.before(nextel);
    // Could also return 'this' depending on how you you want the
    // plug-in to work
    return nextel;
};

Demo here: http://jsfiddle.net/EbU6a/194/

Kevin Borders
  • 2,933
  • 27
  • 32
  • 2
    Clever. If you use `nextel.detach();` instead of `nextel.remove();`, the fields will be reusable too. (`remove()` also removes the bound listeners) – szupie Nov 11 '14 at 20:34
  • This doesn't work if there are elements in-between the inputs. See http://jsfiddle.net/EbU6a/743/ – RubenSandwich Dec 23 '14 at 00:09
  • @RubenSandwich yes, this sample code makes a number of assumptions, but it can be adapted to elements in any position by updating `.next()` and `.before()` to remove the next form element and re-insert it wherever it needs to go. – Kevin Borders Dec 23 '14 at 03:14
1

UIWebview has the property keyboardDisplayRequiresUserAction

When this property is set to true, the user must explicitly tap the elements in the web view to display the keyboard (or other relevant input view) for that element. When set to false, a focus event on an element causes the input view to be displayed and associated with that element automatically.

https://developer.apple.com/documentation/uikit/uiwebview/1617967-keyboarddisplayrequiresuseractio

pkamb
  • 33,281
  • 23
  • 160
  • 191
0

I hope this is what you are looking for-

$(document).ready(function() {
  $('input:first').focus(); //focus first input element

  $('input').on('keyup', function(e) {
    if(e.keyCode == 8) {  //check if backspace is pressed
      $(this).prev('input').focus(); 
      return;
    }
    if($(this).val().length >= 1) { //for e.g. you are entering pin
      if ($(this).hasClass("last")) {
        alert("done");
        return;
      }
      $(this).next('input').focus(); 
    }
  });

  $('input').on('focus', function() {
    if(!$(this).prev('input').val()){
      $(this).prev('input').focus();
    }
  });
});

check code here-

https://jsbin.com/soqubal/3/edit?html,output

Kapilrc
  • 1,362
  • 15
  • 11
0

Add this line in your config file in ios section

preference name="KeyboardDisplayRequiresUserAction" value="false"

Lakshay Sharma
  • 1,347
  • 12
  • 13
  • 2
    How come you know that the TO is developing a hybrid app? He tagged his question with "javascript", "jquery", "iOS" and "mobile-safari". This does not answer the question at all. At least it's unrelated. – j3141592653589793238 Oct 29 '19 at 12:29
0

I encounter the same problem on safari ios. on login page, I programmatically focus on next input field after user input one sms code. I trigger the auto focus on input change event. I fixed the problem by adding a delay.

For details, see the code below

<InputItem
    value={item}
    type='number'
    maxLength={1}
    ref={el => this[`focusInstRef${index}`] = el}
    onChange={(value) => {
        value&&index !== 5 && setTimeout(() => {this[`focusInstRef${index + 1}`].focus()}, 5);
        vAppStore.setSmsCodeArr(value, index);}
    }
/>
cxytomo
  • 23
  • 4
-7
<!DOCTYPE html>
<html>
<head>
    <style type="text/css">
     #hidebox {position:absolute; border: none; background:transparent;padding:1px;}
     #hidebox:focus{outline: 0;}

    </style>
</head>

<body>

<input type="text" maxlength="1" onkeyup="keyUp(this)" onkeydown="keyDown(this)" size="2" id="hidebox" at="1">
<input type="text" maxlength="1" size="2" id="mFirst" at="1" onfocus="onFocus(this)"><input type="text" maxlength="1" size="2" id="mSecond" at="2" onfocus="onFocus(this)"><input type="text" maxlength="1" size="2" id="mThird" at="3" onfocus="onFocus(this)"><input type="text" maxlength="1" size="2" id="mFourth" at="4" onfocus="onFocus(this)">
</li>
<script type="text/javascript">

window.onload = function() {
     document.getElementById("mFirst").focus(); 
}
var array = ["mFirst","mSecond","mThird","mFourth"];
function keyUp(e) {
  var curId = array[Number(e.getAttribute("at"))-1];
  var nextId = array[Number(e.getAttribute("at"))];
  var curval= e.value;
var letters = /^[0-9a-zA-Z]+$/;
if(e.value.match(letters)){
  document.getElementById(curId).value = curval;
  if(e.getAttribute("at") <= 3){
  var nextPos = document.getElementById(nextId).offsetLeft;
  e.setAttribute("at",Number(e.getAttribute("at"))+1);
  e.style.left = nextPos+"px";
  }
 e.value = ""
}else {
 e.value = "";
}
} 
function keyDown(e) {
    var curId = array[Number(e.getAttribute("at"))-1];
    document.getElementById(curId).value = "";
} 

function onFocus(e) {
  document.getElementById("hidebox").focus();
  document.getElementById("hidebox").setAttribute("at",Number(e.getAttribute("at")));
  document.getElementById("hidebox").style.left = e.offsetLeft+"px";
  document.getElementById("hidebox").style.top = e.offsetTop+"px";
}

</script>
</body>
</html>