8

Issue

I am creating a page at work for a client, with a slide out search bar. I want the focus (cursor) to move to the search bar (input) when I click a button that opens the search input field (it starts off hidden).

For some strange reason, the ONLY way I can seem to get the focus to switch the the input field is if I put it in a setTimeout with a minimum time of about 50ms (I tried 0, but doesn't work).

Weird caveat - I can momentarily that the input field gets focus/cursor when I close the search bar (click the open/close button that hides the search), but not when it opens, which is what I want. Unless it's in the setTimeout....

What I've tried

Setting the focus to something else, then blurring that, then setting the focus to my input field. [x]

  • Wrapping the .focus() in a setTimeout with 0 ms [x]

  • Setting the focus to a different, 'test' input field I created. [WORKS]

  • Adding tabindex to the input of -1 [x]

const icon = document.getElementById("search-icon");
const form = document.getElementById("search-form");
const input = document.getElementById("search-input");

icon.parentElement.addEventListener("click", e => {
  form.classList.toggle("visible");
  icon.classList.toggle("fa-search");
  icon.classList.toggle("fa-times");  

  setTimeout(() => input.focus(), 50);

});
<div class="header__search">
    <i id="search-icon" class="fas fa-search">click me</i>
    <form
      id="search-form"
      class="header__search-area"
      onclick="event.stopPropagation()"
    >
      <input
        id="search-input"
        tabindex="-1"
        type="text"
        placeholder="Enter Search Term..."
      />
      <button type="submit">search</button>
    </form>
</div>

For a quicker understanding, you click the icon (id=search-icon), and it displays the form alongside it which is position absolute and contains the 'input' I want to focus on.

If anyone can explain this, amazing. Google hath provideth nay answers.

Chris Barr
  • 29,851
  • 23
  • 95
  • 135
WillOSW
  • 107
  • 1
  • 8
  • I've edited your question so the code is runnable, and the icon element is visible & clickable – Chris Barr May 22 '19 at 16:09
  • My browser is Chrome, although the same thing is happening in Firefox and Safari. Chris, the other chat is preventing me from adding comments and I can't 'chat' on SO yet. I am adding a visible class to the input, yes, which unhides it, but that's happening before the focus theoretically? – WillOSW May 22 '19 at 16:18

1 Answers1

6

Because you've just clicked something and set the focus with your mouse, and then you are immediately changing it. The first time you focus (when you click) needs to complete first.

instead you click just change the focus immediately without a delay if you did it on the mouseup event instead, since that is when the mouse button is released. See below.


Edit

One second thought... I'm not really sure any more. copied your code and removed the delay, but it works just fine.

const icon = document.getElementById("search-icon");
const form = document.getElementById("search-form");
const input = document.getElementById("search-input");

icon.parentElement.addEventListener("click", e => {
  form.classList.toggle("visible");
  icon.classList.toggle("fa-search");
  icon.classList.toggle("fa-times");  
  
  input.focus();

});
<div class="header__search">
    <i id="search-icon" class="fas fa-search">click me</i>
    <form
      id="search-form"
      class="header__search-area"
      onclick="event.stopPropagation()"
    >
      <input
        id="search-input"
        tabindex="-1"
        type="text"
        placeholder="Enter Search Term..."
      />
      <button type="submit">search</button>
    </form>
</div>
Community
  • 1
  • 1
Chris Barr
  • 29,851
  • 23
  • 95
  • 135
  • 1
    Ok, that makes sense, but a couple of things: - Why does a ms time on the setTimeout of 1 not work. - Why does a ms time on the setTimeout of 0 not work (as presumably this would still make it happen after everything else has finished?). – WillOSW May 22 '19 at 15:51
  • Is it because the callback in the setTimeout isn't competing with JS, and rather the browser function of however focusing works? (Sorry for my patchy knowledge). – WillOSW May 22 '19 at 15:55
  • What happens if you click and hold down the mouse button for a little while and then release it? A working code sample in your question would be helpful. – Chris Barr May 22 '19 at 15:58
  • Check my answer again - I've made a working code sample with no delay and it works fine. – Chris Barr May 22 '19 at 16:04
  • I'll try the mouseup thing, see if that works. Your code sample is working, but is slightly different to mine, as the button to change focus is within the form, whereas mine is outside. I know this shouldn't make any difference, but that's my dilemma :) Thanks for responding though. – WillOSW May 22 '19 at 16:09
  • 1
    ha, sorry - edited it AGAIN! Your code with no delay is working fine for me. perhaps it's something else? Could it be that your are adding a `visible` class to the form? I assume this means it's hidden initially, and you can't set focus on hidden elements. – Chris Barr May 22 '19 at 16:12
  • I am adding a visible class to the input, yes, which unhides it, but that's happening before the focus theoretically? I would imagine this happens almost instantly but I could be wrong I guess. There is a .25s transition on the 'unhiding' which just sets opacity to 1 and visibility: visible. – WillOSW May 22 '19 at 16:40
  • 2
    @AlexisWilke I removed the transition, and removed the setTimeout, and it is working! So presumably it is something to do with that. What's strange though, is if I decrease the transition to .125s, the setTimeout still only works with a minimum duration of about 15 (0015ms). Odd... – WillOSW May 23 '19 at 08:45
  • @WillOSW, this answer may be of interest to you then: https://stackoverflow.com/questions/2794148/css3-transition-events – Alexis Wilke May 23 '19 at 16:32
  • Thanks! Looks interesting I'll test it when I can. – WillOSW May 24 '19 at 17:36