移动版Safari自动对焦文本框

57
在移动版Safari中,我无法在设置延迟时间后将焦点放在文本字段上。以下是一些示例代码展示了这个问题。
如果你在按钮的onclick事件中触发.focus(),那么一切都按预期工作。但是,如果你将焦点挂在一个回调函数上,比如setTimeout函数,那么它只会在移动版Safari中失败。在所有其他浏览器中,会有一个延迟,然后焦点就会出现。
令人困惑的是,即使在移动版Safari中,“focusin”事件也会触发。这(以及Stack Overflow中类似的评论)让我认为这是一个移动版Safari的bug。欢迎任何指导。
我已经在模拟器和iPhone 3GS/4 iOS4上进行了测试。
示例HTML:
<!DOCTYPE html> 
  <html lang='en'> 
    <head> 
      <title>Autofocus tests</title> 
      <meta content='width=device-width, initial-scale=1.0, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0' name='viewport'> 
      <meta content='yes' name='apple-mobile-web-app-capable'> 
    </head> 
    <body>
      <h1> 
        Show keyboard without user focus and select text:
      </h1> 
      <p> 
        <button id='focus-test-button'> 
          Should focus on input when you click me after .5 second
        </button> 
        <input id='focus-test-input' type='number' value='20'> 
      </p> 
      <script type="text/javascript"> 
        //<![CDATA[
        var button = document.getElementById('focus-test-button');
        var input  = document.getElementById('focus-test-input');

        input.addEventListener('focusin', function(event) {
          console.log('focus');
          console.log(event);
        });

        button.addEventListener('click', function() {
          // *** If triggered immediately - functionality occurs as expected
          // input.focus();
          // *** If called by callback - triggers the focusin event, but does not bring up keyboard or cursor
          var t = setTimeout("input.focus();",500);
        });
        //]]>
      </script>
    </body>
  </html>

类似的SO问题:


移动版Safari存在一些焦点问题。例如,您无法通过body的onload事件将焦点应用于字段。我猜这也是一个错误。 - DA.
@DA 我也开始认为这与Mobile Safari中出现的focus()问题有关。 - boymc
6个回答

84
我认为这是移动Safari的一个特性而不是一个bug。在我们对FastClick进行的工作中,我和我的同事发现,如果调用堆栈中的第一个函数由非编程事件触发,则iOS只允许从函数内触发其他元素上的焦点。在你的情况下,对setTimeout的调用启动了一个新的调用堆栈,安全机制会阻止你设置输入框的焦点。
请记住,在iOS上设置输入框的焦点会弹出键盘——因此那些在页面加载时在输入框上设置焦点的网页(如Google)将在iOS上使用起来非常烦人。我想苹果公司决定采取措施来防止这种情况的发生。所以我不同意@DA的看法:这是一个特性而不是一个bug。
目前没有已知的解决方法,所以你必须放弃使用延迟的想法。
更新于2012年8月:
自iOS 5以来,由合成点击事件触发的处理程序被允许触发输入元素上的焦点。尝试更新的FastClick输入焦点示例

1
Matt在2012年8月的更新中是否意味着我们可以从setTimeout事件中触发输入元素的焦点? - inlokesh
21
只是想补充一点,整个行为是由UIWebView控件的这个属性引起的:http://developer.apple.com/library/ios/#documentation/uikit/reference/UIWebView_Class/Reference/Reference.html#//apple_ref/occ/instp/UIWebView/keyboardDisplayRequiresUserAction 在Safari中,它被设置为“是”,在主屏幕Web应用程序中,它被设置为“否”,如果你在自己的应用程序中实现该控件,则由你决定。 - Ronny Heuschkel
10
@RonnyHeuschkel 谢谢!我们正在实现一个PhoneGap应用程序,并刚刚在我们的(void)webViewDidFinishLoad:(UIWebView*)theWebView方法中插入了以下代码:theWebView.keyboardDisplayRequiresUserAction = NO;非常有效! - Eric Galluzzo
1
如果你问我,这不是一种非常健壮的预防形式。在我的网站上,打开下拉菜单会自动聚焦其中的输入框,因为打开下拉菜单是通过轻触触发的,所以它会自动弹出键盘。我更希望他们让.focus()直接聚焦输入框而不打开键盘,并且只有通过轻触才能打开键盘。 - Andy
显示剩余10条评论

6

当原始事件不是来自setTimeout,而是来自用户交互时,我可以通过调度点击事件来提高键盘。 我相信结果是您可以从touchend事件中提高键盘,但仍无法从timeout中提高键盘。


1
你如何在键盘事件中提高键盘?听起来很奇怪,但是当我处理键盘事件以使新的文本字段获得焦点时,我的键盘会消失... - Michael
他的意思是你可以通过触摸结束事件在手机上弹出“软键盘”或“键盘”,而不是弹出真实的物理键盘。 - Leo

2
似乎只有在将网站添加到主屏幕并使用此链接打开网站时,focus() 才有效。

对我来说,只有在从主屏幕运行为Web应用程序时,focus()才有效。 - jakob.j
请参见此页面中的评论https://dev59.com/iW015IYBdhLWcg3w9wp1#7aWeEYcBWogLw_1b3Na3,了解其工作原理。 - Maarten

0

补充一下Matt的答案。至少在iOS 5.1上的Safari中,此问题已经解决。您的FastClick有效,也就是说,合成单击事件不会导致焦点丢失。但是这并不能帮助那些希望他们的单个focus()代码在所有iOS版本上都能工作的人,唉。


0

我能够通过将它附加到事件映射中的两个不同事件来使 .focus() 起作用,但这有点 hacky。

添加 FastClick.js 后,在 iOS 中会发生以下情况:只有在通过附加到事件的函数激活时,.focus() 才起作用。但是,focus 也是移动 Safari 的事件映射中的一个事件,当使用 jQuery 的 .focus() 时实际上会调用它。因此,您可以重复并在对象的 focus 事件上附加另一个 .focus(),以确保它被传递。当您在 DOM 中创建输入时,这种方法特别有效。我最近喜欢为 MeteorJS 编程,这就是解决方案的样子:

Template.templateName.events({
    "click button":function(){
        Session.set("makeButtonVisible",true);
        $("input.created").focus();
    },
    "focus input.created":function(){
        $("input.created").focus();
    }
});

希望这对某个人有用,我花了两个小时才弄清楚这个问题。
编辑: 对于MeteorJS而言,你也不能使用Template.templateName.rendered函数,因为必须从事件中调用.focus()。但是出于某种原因,当你通过jQuery添加输入时,你可以在事件中聚焦它。我想这就是解决的方法。这就是我最终做的事情:
Template.templateName.events({
    "click button":function(){
        $("body").append("<input class='created' type='tel'>");
        $("input.created").focus();
    }
});

2
呃,糟糕,算了吧。它是偶然成功的,因为我添加了其他事件,使函数运行两次。不过解决方案大概就在那附近。 - Marz

-10
你已经回答了自己。如果你使用Jquery,你只需要触发(trigger)焦点(focus)事件。在代码的任何部分将focus()更改为trigger("focus")即可。

$("#searchField").trigger("focus");


根据jQuery文档,.focus()被别名为.trigger("focus")。 - Ben Wheeler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接