4

I have a normal HTML input box: <input id=​"ip">.

And this input box has focused. I know this by looking at the screen and the fact document.activeElement tells me so.

Now I want to replace this input node. And I do so with: var the_new_node = document.createElement("input"); the_new_node.id="ip"; the_input_node.replaceWith(the_new_node);

When I do this, however, the input box loses focus. document.activeElement now points to the body. Is there anyway to prevent this?

Edit: I realise I can call .focus(). Yet in my code I won't necessarily know if the node to be replaced will have an input within it.

For instance, the in various 'virtual dom' implementations they replace segments of the dom tree while retaining focus. How do they do it?

mmm111mmm
  • 3,607
  • 4
  • 27
  • 44
  • 2
    Why don't you use `the_new_node.focus()` after replacing ? – Carle B. Navy Jan 22 '18 at 22:24
  • 1
    Calling `focus()` on the new element after adding it should do the trick. Though it's beyond me what use there is to replacing an input with one that is exactly identical... – Herohtar Jan 22 '18 at 22:30
  • The code is only same, poc code. Please see the edit. – mmm111mmm Jan 22 '18 at 22:34
  • "How do they do it" I'm sure at least one of them is open source, with the code available for perusal... – Heretic Monkey Jan 22 '18 at 22:36
  • Focus works on any element capable of receiving focus, and thus being the target of `activeElement`. So if you want `document.activeElement` to point to the new element, call `focus()` – Herohtar Jan 22 '18 at 22:42
  • Possible duplicate of [Is it possible to change document.activeElement in JavaScript?](https://stackoverflow.com/questions/3995524/is-it-possible-to-change-document-activeelement-in-javascript) – Herohtar Jan 22 '18 at 22:44
  • If your input will always have an ID, you could stash the ID before replacing (`var id = document.activeElement.id`) and call focus the new version using the ID (`document.getElementById(id).focus()`). – Jack A. Jan 22 '18 at 22:59

1 Answers1

2

If you want to only focus the new input if the element you're replacing had a focused element inside, you can code exactly that using .contains(document.activeElement):

function runReplace(idx) {
  document.querySelectorAll("input")[0].focus();
  setTimeout(() => {
    let p = document.getElementById("par").children[idx];
    let wasFocused = p.contains(document.activeElement);
    let newNode = document.createElement("input");
    newNode.type = "text";
    newNode.value = "replacement for child #" + idx;
    p.replaceWith(newNode);
    if (wasFocused)
      newNode.focus();
    
    console.log("Replaced ", p, " with an input; wasFocused=", wasFocused);
  }, 3000);
}
<div id="par">
  <p>This is a paragraph with <b>an <input type="text" id="inp" value="<input>"> inside</b></p>
  <p>This is a paragraph with no input inside</p>
</div>
<button onclick="runReplace(0)">Replace first paragraph in 3 seconds</button>
<button onclick="runReplace(1)">Replace second paragraph in 3 seconds</button>

There's no magic way the browser can "preserve" focus in face of replacing an arbitrary DOM sub-tree with another arbitrary sub-tree, so you have to do it manually.

Nickolay
  • 31,095
  • 13
  • 107
  • 185
  • this example is poor, as it's not using input elements. The problem is in input focus and autocomplete for iOS Safari browser. The goal is to open the Virtual Keyboard, then focus on an element and have the autocomplete suggestions working. If you do that, I will be amazed – Soldeplata Saketos Dec 19 '22 at 16:18
  • The example clearly uses input elements. It seems that you have a different question, in which case you ought to ask it separately (and be sure to describe it in detail). – Nickolay Dec 19 '22 at 17:42