在Chrome移动版中,如果没有通过点击激活,.focus()无法起作用。有解决方法吗?

10

我在chrome的移动端遇到了一些奇怪的功能。

当我尝试聚焦在一个元素上时,仅仅加载输入,用getItById并执行.focus()是无效的。然而,如果你将其包装在附加到按钮的事件监听器中,并用鼠标点击按钮,它就可以正常工作。

于是,我尝试欺骗它,看看是否可以调用btn.click(),但这不会触发.focus()

请在下面尝试:在移动设备上使用chrome(至少在iOS上),加载页面。你应该会收到警报“Clicked”,但它不会聚焦在输入框上。然后,尝试点击按钮。您将获得警报和正常工作的焦点。

我发现这很有趣,并想知道人们是否知道解决方法。

链接在这里-jsfiddle

const btn = document.getElementById('button')

btn.addEventListener('click', () => {
  alert('clicked')
  const input = document.getElementById('input')
  input.focus()
})

btn.click()
<input id="input" type="text">
<button id="button">Button</button>

编辑

我还注意到的另一件事情是,如果你让手机进入睡眠状态,然后再次打开它,focus()函数可以在没有点击的情况下正常工作。

编辑2 - 为移动端添加了链接


1
它只是我还是它工作得很好? - Sheshank S.
你可以尝试使用setTimeout()函数。setTimeout(function(){ input.focus() },10); - Anji
可能重复:https://dev59.com/jWUo5IYBdhLWcg3wpg_N - Dimi
添加设置超时并不起作用 - 实际上,它会使问题变得更糟,因为现在当您使用鼠标单击时,它不会像没有设置超时时那样激活焦点。 - HJo
我认为我可能已经修复(避免)了原始问题,至少按照上面的代码定义。但是,@Peter你使用什么代码/何时来获取该值? - petern0691
1
@Peter,这几乎肯定与焦点行为无关。如果您确定是这样,请发布一份代码示例?在正常情况下,输入元素的值不应该被焦点事件或切换应用程序清除,您可以在普通输入上轻松验证此操作(我在Chrome Android上进行了测试)。很可能您正在运行的代码(我们只知道其中1个语句)会受到焦点事件的影响。请发布完整的代码。但是document.getElementById('input').value始终为空。 - inwerpsel
5个回答

5

为什么会发生这种情况?

因为移动设备通常需要在显示键盘时更改屏幕尺寸,所以它们带有一些限制,限制何时可以触发焦点事件。这是触摸设备上故意限制浏览器行为,以保证良好的用户体验并避免调整浏览器窗口(=潜在的非常昂贵的样式重新计算)的性能问题。

这些限制在不同的浏览器之间存在一些差异,并且某些浏览器还存在错误。

但通常都需要实际用户交互不久之前才能以编程方式触发焦点。使用btn.click()与实际的触摸事件不同,因此浏览器将忽略它,因为没有最近的触摸事件。

Webkit上的此行为跟踪问题中,苹果提供了一个动机:

我们(苹果)喜欢当前的行为,不希望在没有连接硬件键盘且程序焦点不是由用户手势引起的情况下,通过编程方式聚焦带出软键盘。你可能会问为什么……因为自动弹出软键盘可能会被视为对用户的干扰和分心(不仅仅是你的客户,而是所有未使用你的应用程序的人),原因如下:
  1. 我们会弹出键盘,占据屏幕宝贵的空间。
  2. 当我们打算弹出软键盘时,我们会缩放和滚动页面,以提供愉悦的输入体验(或者至少我们希望是愉悦的;如果不是,请报告错误)。

类似的问题还有 autoplay 属性,也需要最近发生过手势(或页面加载),可以帮助理解这种问题。

如何绕过这些限制?

大多数情况下

很可能你不需要这样做,因为大多数触发焦点的代码都是响应用户操作而运行的,并且将被允许触发焦点。我还没有找到有关时间窗口大小的数据,但我猜它足够大,以至于在正常情况下这不是一个问题(除非你设置了超时)。
对于这些情况可能存在的问题是脚本运行时间过长,事件已被认为是太久之前的事件。实际上,在related autoplay issue中就发生了这种情况,代码会请求某些网络资源,只有在响应后才会触发视频播放。根据网络/设备的速度,视频有时会自动播放,有时不会。
从技术上讲,同样的问题可能会出现在仅在网络延迟后才聚焦的输入框中,使其无法显示键盘。只要你的代码不做任何网络请求,就不会遇到这种问题。

页面加载时

这绝对不是解决问题的方法,但你可以尽最大努力通过使用专门为此设计的属性来管理页面加载时的焦点。这仍然不会在其他情况下显示键盘。

autofocus属性在所有浏览器上至少部分支持。

也许直接在页面加载时调用focus()脚本就可以,但是有可能这段代码运行的时间太长,特别是在速度较慢的设备上。当以编程方式添加带有autofocus属性的HTML时(例如React),这种情况可能也会发生。如果初始HTML包含正确元素上的autofocus属性,则不应出现此问题。


那么为什么“onloading”点击可以解决问题呢?仍然没有用户交互。我也尝试了触摸事件,但那并没有起作用。 - petern0691
我并不是说添加autofocus可以解决问题。它只是看起来稍微更可靠一些,但可能仍然无法在用户与页面进行交互之前弹出键盘。请参见这个相关的SO答案 - inwerpsel
@petern0691,我不确定我是否正确理解了你的问题。关键是浏览器有意地选择何时可以进行焦点操作,并且他们这样做是有好处的。你能举个例子说明只有通过编程触发焦点才能构建的功能吗?在什么情况下你想要触发焦点并在用户交互之外弹出键盘呢? - inwerpsel
仅通过编程触发焦点来构建,这不现实。在用户在其他地方执行操作后通过编程聚焦构建是可以的,提供用户选项以在启动时聚焦于元素也可以。我的主要问题,与OP或SP的确切问题不同,是该元素似乎“半聚焦”。 - petern0691
我的猜测是,“一半”表示它已经滚动到视图中,但没有得到键盘?或者你的意思是元素没有完全滚动到视图中? - inwerpsel
显示剩余4条评论

0

这里肯定发生了一些奇怪的事情。

似乎“部分”DOM认为输入元素最初处于焦点状态,但Chrome没有完成对其的聚焦。例如,如果设置了背景:

document.activeElement.style.backgroundColor = "pink";

然后输入元素的背景变成了粉色。因此,Chrome的某些DOM部分认为输入元素处于焦点状态。

最初,在我的情况下,Chrome以其默认的非焦点样式呈现输入元素。

alert()似乎干扰了这个过程。在我的情况下,将警报移除会导致更改输入元素的样式,从非焦点样式变为焦点样式,但光标不会出现在输入元素中,弹出键盘也不会出现。

通过:

  • 移除警报
  • 将单击包装在函数内
  • 在加载时调用该函数

在我的情况下解决了问题。现在初始状态下元素处于焦点样式,光标出现在元素框中,并弹出键盘。

@HJo在2019年1月发布的帖子的解决方案是替换:

btn.click();

使用:

function load() { btn.click(); }

移除 alert(),

将 body 标签改为:

<body onload="load()">

@Peter,如果这个方法也不能解决你的问题,请提供一下你上下文中的最小代码。


0
const event = new Event('tap');
   const btn = document.getElementById('button')
   const input = document.getElementById('input')

    btn.addEventListener('click', () => {
      prompt('clicked')
      input.focus();
    })
    window.onload = function(){
      btn.dispatchEvent(event);
    }
}

上面的代码在网站标签页刚打开时能够运行,但是刷新后就不能了,原因不明。我在iPad和iPhone上测试过了,希望它在你这里也能正常工作。
这里是一个repl链接: https://r.hackinggo306.repl.co 编辑:这似乎是我能得到的最接近的结果,但也要承认移动设备可能会有意阻止此操作。(如果一个网站循环执行这个操作那会很烦人。)

-1
我认为,这个问题可能会发生,因为你的 JavaScript 脚本在 DOM 被挂载之前就被执行了。 所以你可以做不同的事情, 第一件事 在脚本标签中添加 defer 关键字。基本上它所做的就是,在 DOM 挂载后才运行脚本。查看 第二件事window.onload 事件与你的输入焦点功能包装起来。
 <script type="text/javascript">
    window.onload = function () { 
const btn = document.getElementById('button');
 const input = document.getElementById('input')
  input.focus()
 
    }
</script>

window.onload事件只有在DOM完成挂载时才会触发。因此,你的JavaScript脚本可能在输入元素挂载之前运行。所以请在你的代码中添加onload事件。查看这个链接


defer属性仅适用于外部脚本。在我的情况下,我将脚本定位在body关闭标记之后。我还用另一个div元素的innerHTML设置替换了alert,这需要加载DOM。因此,DOM已加载。 - petern0691

-1
在移动设备上,您需要使用touchstart事件而不是点击事件。
您可以在这里找到它。希望这可以帮助您。

我尝试了touchstart,但它没有解决问题。我还尝试了touchend。 - petern0691
这不是问题的答案,也不需要。OP说他们实际上成功地使用了“click”事件来触发警报。 "在移动设备上,Chrome... 然后,尝试点击按钮。您将同时获得警报和焦点工作。" - inwerpsel

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