focus() not working within setTimeout on iOS
Asked Answered
D

3

17

jQuery's focus() method is does not appear to work when used from within a setTimeout in iOS.

So,

setTimeout( function () {
    // Appears to have no effect in iOS, fine in Chrome/Safari/Firefox/IE
    $('.search').focus();    

}, 500);

But on it's own,

// works fine.
$('.search').focus();    

See the following example:

http://jsfiddle.net/nwe44/ypjkH/1/

If the focus() call is made outside the setTimeout it works, inside it doesn't. This is doubly curious as other methods do work. For example, in my jsFiddle I'm able to change the border color, just not focus it. Any ideas?

Dharana answered 28/7, 2011 at 22:15 Comment(2)
I supposed it's remotely possible that focus via timer was being abused by some web pages and combined with the way the keyboard works on iOS, it created a bad user experience so Apple decided to block it. I looked, but couldn't find any discussion of it on the web (though it's not obvious exactly what you would search for).Apodal
Hmmm, someone else has come to the same conclusion as you, sencha.com/forum/… this may be a dead end.Dharana
C
2

Check fiddle i have updated at http://jsfiddle.net/ypjkH/7/

    $('#selector').click( function (e) {
        e.preventDefault();
        setTimeout( doFocus  
    , 3000);

    });

  function doFocus() {
      $('.search').focus().css('border', '1px solid red');
  }
Caudle answered 28/7, 2011 at 22:26 Comment(3)
That's even more interesting. You've managed to trigger the focus css classes, but not invoke the keyboard, which is part of the point of focusing. I've no idea why your solution works better than mine, but it's certainly closer.Dharana
it focuses definitely wait for 3 seconds.Caudle
yup, it focuses, but the keyboard is not invoked, so you can't type anything. If you call $('.search').focus() from outside a setTimeout the keyboard is invoked.Dharana
X
2

Safari's approach to focus is that it will only work within the same event loop. Therefore, if it's inside a Promise or a timeout, it will not open the keyboard.

By today, it's impossible to open the keyboard after the timeout.

The only workaround you have is to use this hack:

/**
 * iOS keyboard can only be opened by user interaction + focus event.
 * There are situations in which we need to open keyboard programmatically
 * before other elements are rendered, like when opening an overlay. this function can be used
 * to open the keyboard before the overlay is rendered, and once the overlay is
 * rendered, it's needed to focus to the desired input element, and the keyboard
 * will stay open.
 *
 * This function needs to be called from native events attached to HTML elements. It will
 * not work if called from a "passive" event, or after any event not coming from
 * user interaction (onLoad, onFocus, etc).
 *
 * It's recommended to use it on MouseEvents or TouchEvents.
 */
export function openIosKeyboard() {
        const input = document.createElement("input");
        input.setAttribute("type", "text");
        input.setAttribute("style", "position: fixed; top: -100px; left: -100px;");
        document.body.appendChild(input);
        input.focus();
        // it's safe to remove the fake input after a 30s timeout
        setTimeout(() => {
            document.body.removeChild(input);
        }, 30 * 1000);
}
Xe answered 19/12, 2022 at 13:50 Comment(1)
The downside of this approach is that autocompletion will not pop up. Autocompletion will be the worst nightmare if one needs it. One would need to add id attribute, and it still won't work well. It's a hack. The best one can do is to use a dedicated page with a formXe
T
0

For my app it works, if you just put the x.focus() into a(nother) separate function and call it from within the setTimeout

Tyrannize answered 27/10, 2013 at 21:4 Comment(1)
I don't know how wrapping functions into other functions would ever work. Can you provide a working example?Xe

© 2022 - 2024 — McMap. All rights reserved.