移动版Safari:在输入框上使用Javascript的focus()方法只能通过点击实现吗?

90
我有一个像这样的简单输入字段。
<div class="search">
   <input type="text" value="y u no work"/>
</div>

我想在一个函数内部使用 focus() 来聚焦它。 所以在一个随机函数内部(函数名不重要)我有这行代码...

$('.search').find('input').focus();

这在任何桌面设备上都能正常工作。

但是在我的iPhone上无法正常工作。该字段没有获得焦点,键盘也没有显示在我的iPhone上。

为了测试目的并向大家展示问题,我做了一个简单的示例:

$('#some-test-element').click(function() {
  $('.search').find('input').focus(); // works well on my iPhone - Keyboard slides in
});

setTimeout(function() {
  //alert('test'); //works
  $('.search').find('input').focus(); // doesn't work on my iPhone - works on Desktop
}, 5000);​

有没有想法为什么在我的iPhone上,focus()和timeout函数一起使用时不起作用?
请在您的iPhone上测试此fiddle以查看实时示例:http://jsfiddle.net/Hc4sT/ 更新:
我创建了与我当前项目中面临的完全相同的情况。
我有一个选择框,当“更改”时,应将焦点设置到输入字段并在iPhone或其他移动设备上滑入kexboard。我发现focus()被正确设置,但键盘没有显示出来。我需要键盘显示出来。

1
对我有效。你贴的 jsfiddle 只是聚焦于输入框并在我的 iPhone 上打开了键盘。你用的是哪个 iOS 版本?我已检查过 5.1 版本。 - Krizz
1
我正在尝试很多东西。我尝试了triggerHandler(),focusin(),trigger("focus")。我甚至尝试先模糊它,然后重新添加焦点。我认为这可能是webkit中的一个bug。你可能在这个问题上运气不佳...但我还要尝试几件事情... - Alex
1
我放弃了。我尝试了所有的方法。在iOS Safari浏览器中,这似乎是不可能完成的任务。希望苹果公司能够做出改变,因为我绝对不认为这是一个“功能”!哈哈我不知道为什么你可以轻松地模糊(blur())字段,但却无法将焦点设置到它上面。这对我来说没有意义。 - Alex
就我个人而言,我正在使用WP7,除非输入可见,否则我无法弹出键盘;我不一定要点击它。当通过display:none隐藏输入时,我无法弹出键盘。 - SnakeWasTheNameTheyGaveMe
duplicate: https://dev59.com/iW015IYBdhLWcg3w9wp1 - uri.lazar
显示剩余11条评论
9个回答

86

其实,各位,有一种方法可以解决。我为了[LINK REMOVED]这个问题而苦苦挣扎(在 iPhone 或 iPad 上试试)。

基本上,触摸屏设备上的 Safari 在设置 focus() 文本框方面有点吝啬。即使是某些桌面浏览器如果使用 click().focus() 也能做得更好。但触摸屏设备上的 Safari 的设计者们意识到当键盘不断弹出时会让用户感到烦恼,因此他们只在以下情况下显示焦点:

1)用户单击某处并调用 focus() 时执行单击事件。如果您正在进行 AJAX 调用,则必须同步进行,例如使用 jQuery 中已弃用但仍可用的 $.ajax({async:false}) 选项。

2)此外,在一段时间内如果有其他文本框具有焦点,focus() 似乎仍然无法正常工作。我有一个“Go”按钮执行 AJAX,所以我尝试在“Go”按钮的 touchstart 事件上取消文本框的焦点,但这只会使键盘消失并在我完成点击“Go”按钮之前移动视口。最后,我尝试在“Go”按钮的 touchend 事件上取消文本框的焦点,这非常顺利!

当您将#1和#2结合在一起时,您将得到一个神奇的结果,可以使您的登录表单与所有糟糕的 Web 登录表单有所区别,通过将焦点放在密码字段中,使其感觉更本地。尽情享受吧! :)


2
这听起来很聪明,但我很难将“#1和#2放在一起”。在我的应用程序中,使用jQueryMobile,当用户单击页面A中的列表项时,会打开一个新的页面B,该页面只有一个可见输入。我希望键盘自动为该输入打开。您是不是意味着我必须在页面A上的单击处理程序中的某个位置放置.focus()调用? - Wytze
1
谢谢你节省了我的时间。那真的很难找到。 - vogdb
4
我已经明白了!我没有模糊和聚焦元素,而是在touchstart上添加了event.preventDefault()。请注意,这将防止执行ng-click,因此我必须在事件处理程序中添加我的清除文本逻辑并使用$scope.$apply() - ErikAGriffin
2
我发现只需在按钮的点击处理程序中运行input.focus()即可。iPhone 5和iPad上的Safari都可以。如果我将focus()放在setTimeout中,那就不起作用。所以也许这就是“同步”的意思,@Michael。 - Dan Dascalescu
2
感谢您发布了实际的解决方案。在touchend上模糊前一个元素+在click事件上设置焦点确实可以在iOS10上工作。 - Kirill E.
显示剩余7条评论

47

WunderBart的回答的一个原生JavaScript实现。

function onClick() {

  // create invisible dummy input to receive the focus first
  const fakeInput = document.createElement('input')
  fakeInput.setAttribute('type', 'text')
  fakeInput.style.position = 'absolute'
  fakeInput.style.opacity = 0
  fakeInput.style.height = 0
  fakeInput.style.fontSize = '16px' // disable auto zoom

  // you may need to append to another element depending on the browser's auto 
  // zoom/scroll behavior
  document.body.prepend(fakeInput)

  // focus so that subsequent async focus will work
  fakeInput.focus()

  setTimeout(() => {

    // now we can focus on the target input
    targetInput.focus()

    // cleanup
    fakeInput.remove()
    
  }, 1000)

}

其他参考资料:如何在iPhone上的Safari浏览器中禁用输入“文本”标签的自动缩放


8
这对我有用。如果你想防止在虚拟输入框获取焦点时显示键盘,请在其中添加readonly="true"。 - Branislav Kuliha
3
这个答案应该在其他类似的问题中分享,这些问题涉及在Safari浏览器上使用setTimeout和focus。这是唯一真正有效的方法! - wentjun
1
谢谢!这是唯一一个对我有效的解决方案。 - Offek
1
工作正常,+1。但似乎超时很重要,如果是0或小于250毫秒的时间间隔,则不起作用。苹果公司的人应该为自己感到羞耻,因为他们把微不足道的任务变成了庞大而不透明的东西。 - curveball
1
键盘打开在这个解决方案中运作良好,但是在提供OTP代码自动填充时会出现问题。上面的键盘应该是在收到消息后通过点击它来自动填充代码的文本。使用这个解决方案,我必须手动关闭键盘并通过输入点击再次打开,键盘上的文本也会显示出来。愚蠢的苹果,我讨厌这种永无止境的修复。 - mikep
显示剩余6条评论

7
我最近遇到了同样的问题。我找到了一种似乎适用于所有设备的解决方法。你无法通过编程异步聚焦,但是当其他输入已经聚焦时,可以将聚焦切换到目标输入。因此,你需要在触发事件上创建、隐藏、附加到DOM并聚焦一个虚拟输入,并在异步操作完成后再次调用目标输入的聚焦。这里有一个示例片段-在你的手机上运行它。
编辑:
这里有一个具有相同代码的fiddle。显然,你不能在手机上运行附加的片段(或者我做错了什么)。

var $triggerCheckbox = $("#trigger-checkbox");
var $targetInput = $("#target-input");

// Create fake & invisible input
var $fakeInput = $("<input type='text' />")
  .css({
    position: "absolute",
    width: $targetInput.outerWidth(), // zoom properly (iOS)
    height: 0, // hide cursor (font-size: 0 will zoom to quarks level) (iOS)
    opacity: 0, // make input transparent :]
  });

var delay = 2000; // That's crazy long, but good as an example

$triggerCheckbox.on("change", function(event) {
  // Disable input when unchecking trigger checkbox (presentational purpose)
  if (!event.target.checked) {
    return $targetInput
      .attr("disabled", true)
      .attr("placeholder", "I'm disabled");
  }

  // Prepend to target input container and focus fake input
  $fakeInput.prependTo("#container").focus();

  // Update placeholder (presentational purpose)
  $targetInput.attr("placeholder", "Wait for it...");

  // setTimeout, fetch or any async action will work
  setTimeout(function() {

    // Shift focus to target input
    $targetInput
      .attr("disabled", false)
      .attr("placeholder", "I'm alive!")
      .focus();

    // Remove fake input - no need to keep it in DOM
    $fakeInput.remove();
  }, delay);
});
label {
  display: block;
  margin-top: 20px;
}

input {
  box-sizing: border-box;
  font-size: inherit;
}

#container {
  position: relative;
}

#target-input {
  width: 250px;
  padding: 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div id="container">
  <input type="text" id="target-input" placeholder="I'm disabled" />

  <label>
    <input type="checkbox" id="trigger-checkbox" />
    focus with setTimetout
   </label>
</div>


这是我目前找到的最佳解决方案。 - anwerso
1
您可以设置 fontSize: '16px' 以禁用自动缩放。此外,请查看下面我的答案以获取本地js实现。 - Raine Revere
FYI,截至iOS14仍然有效。我一直在试图想出解决方案,结果这个方法非常有效。我发现使用相对定位和display:none可以很好地处理虚拟输入。 - zinndesign
取消那个 - display:none 只在 Android 上有效。 - zinndesign

5
这个解决方案很好,我在我的手机上测试过了。
document.body.ontouchend = function() { document.querySelector('[name="name"]').focus(); };

enjoy


这个几乎可以工作。但是不能适用于两个日期字段,并且会使页面有点故障。 - Shayne
谢谢!尝试了各种各样的东西,但这个终于解决了我的问题,其中某种原因document.getElementById没有起作用。 - daamsie

3
我有一个搜索表单,其中包含一个在点击时清除文本的图标。然而,在移动设备和平板电脑上出现了问题,因为当点击事件删除输入框的焦点时,键盘会折叠/隐藏。
目标:在清除搜索表单后(点击/轻敲x图标),保持键盘可见!
要实现这一目标,请在事件上应用stopPropagation(),如下所示:
function clear ($event) {
    $event.preventDefault();
    $event.stopPropagation();
    self.query = '';
    $timeout(function () {
        document.getElementById('sidebar-search').focus();
    }, 1);
}

以下是HTML表单:

<form ng-controller="SearchController as search"
    ng-submit="search.submit($event)">
        <input type="search" id="sidebar-search" 
            ng-model="search.query">
                <span class="glyphicon glyphicon-remove-circle"
                    ng-click="search.clear($event)">
                </span>
</form>

2
我用以下代码成功实现了它:
event.preventDefault();
timeout(function () {
    $inputToFocus.focus();
}, 500);

我正在使用AngularJS,因此我创建了一个指令来解决我的问题:

指令:

angular.module('directivesModule').directive('focusOnClear', [
    '$timeout',
    function (timeout) {
        return {
            restrict: 'A',
            link: function (scope, element, attrs) {
                var id = attrs.focusOnClear;
                var $inputSearchElement = $(element).parent().find('#' + id);
                element.on('click', function (event) {
                    event.preventDefault();
                    timeout(function () {
                        $inputSearchElement.focus();
                    }, 500);
                });
            }
        };
    }
]);

如何使用该指令:

<div>
    <input type="search" id="search">
    <i class="icon-clear" ng-click="clearSearchTerm()" focus-on-clear="search"></i>
</div>

看起来你正在使用jQuery,所以我不知道这个指令是否有帮助。


2
这在iOS Safari上已知无法工作。Safari特别区分由用户交互生成的事件与纯粹由JS本身生成的事件,并且不会以这种方式传递焦点/选择事件。 - Ben Wheeler
这并不是真的,我正在使用它来开发我的Cordova/Phonegap应用...为什么要点踩?我的回答有什么不同呢?除了我创建了一个你可以使用的Angular指令。 - martinmose
通常在Angular中,设置一个超时时间就足够了,你不需要设置超时值。这会等待摘要周期,因此在其他阻碍焦点的事件完成后运行。 - Maarten
嗨@Maarten,感谢您的评论,您是指我设置的超时时间为500吗? :) - martinmose
好的,我会测试一下。谢谢 :)。 - martinmose

0

更新

我也尝试了这个,但无济于事:

$(document).ready(function() {
$('body :not(.wr-dropdown)').bind("click", function(e) {
    $('.test').focus();
})
$('.wr-dropdown').on('change', function(e) {
    if ($(".wr-dropdow option[value='/search']")) {
        setTimeout(function(e) {
            $('body :not(.wr-dropdown)').trigger("click");
        },3000)         
    } 
}); 

});

我有点困惑,不明白为什么你说这个不起作用,因为你的JSFiddle看起来很正常,但是无论如何,这是我的建议...

在你的点击事件的SetTimeOut函数中尝试使用这行代码:

document.myInput.focus();

myInput 对应于 input 标签的 name 属性。

<input name="myInput">

使用以下代码来模糊该字段:

document.activeElement.blur();

谢谢,看到我的问题更新了。我似乎也无法使用您的建议使其工作。 - matt

-1

试试这个:

input.focus();
input.scrollIntoView()

-3
请尝试使用 on-tap 代替 ng-click 事件。我曾经遇到过这个问题,通过将清除搜索框按钮放在搜索表单标签内,并将清除按钮的 ng-click 替换为 on-tap,我解决了这个问题。现在它可以正常工作了。

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