10

I've come across some strange functionality on chrome, mobile.

When I try to focus on an element on chrome, it doesn't work when you try to just load the input, getItById and do .focus(). However, if you wrap it in an event listener attached to a button, and click the button with your mouse, it works fine.

So, I tried to trick it by seeing if you could call btn.click(), but that doesn't activate the .focus()

Have a go below: On mobile, chrome (at least for iOS), load the page. You should get an alert 'Clicked', but it won't focus on the input. Then, try clicking on the button. You will get both the alert AND the focus works.

I found this interesting and wanted to see if people knew of a workaround.

Link here - 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>

Edit

Another thing I've noticed, is that if you put your phone go to sleep, and open it again, the focus() works without the click.

Edit 2 - added link for mobile

ATP
  • 2,939
  • 4
  • 13
  • 34
HJo
  • 1,902
  • 1
  • 19
  • 30
  • 1
    Is it just me or is it working just fine? – Sheshank S. Jan 05 '19 at 04:47
  • can you try setTimeout() function. setTimeout(function(){ input.focus() },10); – Anji Jan 05 '19 at 04:50
  • Possible Duplicate: [https://stackoverflow.com/questions/15859113/focus-not-working](https://stackoverflow.com/questions/15859113/focus-not-working) – Dimi Jan 05 '19 at 04:55
  • Adding set timeout doesn't work - in fact, it breaks it even further as now when you click with the mouse, it doesn't activate the focus like it does without the settimeout – HJo Jan 05 '19 at 05:03
  • I think I might have fixed(avoided) the original problem, at least as defined by the code above. But when/what code @Peter are you using to get the value? – petern0691 Nov 08 '22 at 15:51
  • 1
    *" but document.getElementById('input').value is always empty"* @Peter This is almost certainly unrelated to focus behavior. If you're sure it is, maybe post a code sample? The value of an input element should in normal circumstances not be cleared by focus events or switching application, you can easily verify this [on a plain input](https://www.w3schools.com/tags/tryit.asp?filename=tryhtml_input_test) (I did on Chrome Android). Likely the code you're running (of which we know 1 statement) does something that is affected by focus events. Please post the full code. – inwerpsel Nov 12 '22 at 09:21

5 Answers5

5

Why does this happen?

Because mobile devices usually need to change the screen dimensions when a keyboard is shown, they come with some restrictions on when a focus event can be triggered. This is intentionally limited browser behavior on touch devices to guarantee a good UX and avoid performance issues of resizing the browser window ( = potentially very expensive style recalculation).

There's some cross browser differences in how far these restrictions go, and some browsers have bugs on top of that.

But in general they all require an actual user interaction to have happened not too long before the focus is programmatically triggered. Using btn.click() is not the same as an actual touch event, and so the browser will ignore it seeing there was no recent touch event.

On a tracking issue on this behavior for Webkit, Apple provides a motivation:

We (Apple) like the current behavior and do not want programmatic focus to bring up the keyboard when you do not have a hardware keyboard attached and the programmatic focus was not invoked in response to a user gesture. Why you may ask...because auto bringing up the software keyboard can be seen as annoying and a distraction to a user (not for your customers, but for everyone not using your app) given that:

  1. We bring up the keyboard, which takes up valuable real estate on screen.
  2. When we intent to bring up the software keyboard we zoom and scroll the page to give a pleasing input experience (or at least we hope it is pleasing; file bugs if not).

This similar issue with the autoplay attribute, which also requires a gesture (or page load) to have happened recently enough, could be helpful in understanding the kind of problem.

How to work around these limitations?

Most of the time

Likely you don't need to, as most code that would trigger focus is running as a response to a user action and will be allowed to trigger focus. I didn't find data yet on how big the window of time is, but I guess it's big enough that in regular cases it's not a problem (unless you set timeouts of course).

A possible problem for those cases is the script would run so long that the event is already considered too long ago. In fact that's what happened in the related autoplay issue, where the code would request some network resource, and only after the response would trigger the video to play. Depending on the speed of the network/device the video would sometimes auto play, and sometimes not.

Technically the same could happen with an input that is only focused after a network delay making it not show the keyboard. As long as your code doesn't do any network requests you won't have this kind of problem.

On page load

This is definitely not a "fix" for the problem, but you can do a best effort to manage focus on page load by using the attribute specifically made for it. This still won't make a keyboard appear when it otherwise wouldn't.

The autofocus attribute is at least partially supported on all browsers.

Perhaps just calling focus() directly in a script on page load works, but again there's a chance that this code runs too long after the page started being displayed, especially on slower devices. This probably also happens when adding HTML with the autofocus attribute programmatically (e.g. React). If the initial HTML contains the autofocus attribute on the right element that shouldn't occur.

inwerpsel
  • 2,677
  • 1
  • 14
  • 21
  • So why does "onloading" the click fix the problem. There is still no user interaction. I also tried touch events but that did not work. – petern0691 Nov 10 '22 at 20:47
  • I didn't say adding the `autofocus` fixes the problem. It just seems slightly more reliable but it probably still won't pop up the keyboard without the user having some interaction with the page. See [this related SO answer](https://stackoverflow.com/a/23724848/4961158). – inwerpsel Nov 11 '22 at 10:37
  • @petern0691 I'm not sure if I understood your question correctly. The point is browsers intentionally are selective about when a focus can happen, and they do so for good reason. Can you give an example of functionality you can only build by programmatically triggering focus? When would you want to trigger focus and pop up the keyboard outside a user interaction? – inwerpsel Nov 11 '22 at 12:58
  • "build by only programmatically triggering focus", not reallistically. Build by prog focusing after user action somewhere else, yes, build by providing a user option to focus on an element at startup, yes. My main issue, which is not the exact issue of the OP or SP, is that the element seems to be "half in focus". – petern0691 Nov 11 '22 at 20:28
  • Re my question, I was asking, if the browser was avoiding focusing/ rendering until after a user interaction, why does focusing occur correctly after the focus clothed in a function executed at onload but not executed nakedly. – petern0691 Nov 11 '22 at 21:14
  • *"My main issue, which is not the exact issue of the OP or SP, is that the element seems to be "half in focus"."* If you want to get information about other questions than the original one, it's helpful to provide a sufficient description. What does "half in focus" mean? Can you maybe link to a code sample that description your "additional problem"? – inwerpsel Nov 12 '22 at 08:50
  • My current guess is "half" means it's scrolled in view but doesn't get the keyboard? Or you mean the element is not completely scrolled in view? – inwerpsel Nov 12 '22 at 08:57
  • I was referring to the weirdness I described in my answer. The DOM thinks the element is in-focus as per .activeElement, and the styling of the element is changed to in-focus, but it is not scrolled into view, nor, if already in view, is the cursor set, nor does the keyboard pop up. The focusing is "half-baked". See my answer for relevant code. It looks a design flaw to me. – petern0691 Nov 12 '22 at 09:38
  • I'm sure you can find tons of inconsistencies like that, and cross browser differences, if you "see how far you can get" in different browsers. You can probably endlessly debate about each of them whether they're a design flaw or not. My point is that the general motivation behind this kind of restrictions is intentional and for good reason. – inwerpsel Nov 12 '22 at 10:03
0

There is deinfitely something weird happening here.

It seems "part" of the DOM thinks that the input element is in focus initially, but chrome does not complete the focusing. For exmaple, if the background is set with:

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

Then the input element's background is pink. So, some part of chrome's DOM thinks the input element is in focus.

Initially, in my case, the input element is rendered by chrome in its default non-focus styling.

The alert() seems to be interfering with the process. In my case, taking the alert out had the effect of changing the styling of the input element from non-focus styling to in-focus styling, but the cursor did not appear in the input element, nor did the pop up keyboard appear.

By:

  • removing the alert
  • wrapping the click inside a function
  • calling that function on load

fixed the problem in my case. Initially now the element is in-focus styled, the cursor appears in the element's box, and the keyboard pops up.

The solution to the posting by @HJo, January 2019, is to replace:

btn.click();

with:

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

remove the alert(),

and make the body tag as:

<body onload="load()">

@Peter. If this does not fix your problem too, can you please provide minimal code for your context.

petern0691
  • 868
  • 4
  • 11
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);
    }
}

The code above works when the website tab is just opened, but not on reload for some reason. I tested it on an Ipad and Iphone so hopefully it works well on your end too.

Here's a link to a repl: https://r.hackinggo306.repl.co

Edit: This seems to be the closest I can get, but also acknowledge the fact that mobile devices might be deliberately preventing this. (It would be annoying if a website looped this.)

-1

On the mobile, you will need to use touchstart event instead of click event.
You can find it here. Hope this helps.

JShobbyist
  • 532
  • 3
  • 9
  • 23
  • I tried touchstart but it did not fix the problem. I also tried touchend. – petern0691 Nov 08 '22 at 21:34
  • This is not an answer to the question, nor is it needed. OP says they actually did successfully use the `click` event to trigger the alert. *"On mobile, chrome ... Then, try clicking on the button. You will get both the alert AND the focus works."* – inwerpsel Nov 12 '22 at 09:36
-1

I think, this problem can occur, because of the your JavaScript script is executed before the DOM is Mounted. so you can do different things, One Thing add defer keyword to script tag. Basically it does is, that script is only run after the DOM is mounted. See

Second Thing

Wrap window.onload event with your input focus functionality.

 <script type="text/javascript">
    window.onload = function () { 
const btn = document.getElementById('button');
 const input = document.getElementById('input')
  input.focus()
 
    }
</script>

window.onload event is only fired when the DOM is finishing mounting.so that may be your JavaScript script run before the input element is mounting.so add onload event to your code.see this

  • The defer attribute is only for external scripts. In my case I positioned the script after the body close tag. I also replaced the alert with an innerHTML set of another, div, element, which requires the DOM to be loaded. So, the DOM is loaded. – petern0691 Nov 10 '22 at 20:40