Reputation: 285
I have a problem that i can't fix.
Keyboard doesn't show on input.focus() on IOS
searchMobileToggle.addEventListener('click', function() {
setTimeout(function(){
searchField.focus();
}, 300);
});
I've been looking for a solution with no result, i know this is a frequently unsolved question but i see NIKE (https://m.nike.com/fr/fr_fr/) and FOODSPRING (https://www.foodspring.fr/) doing it on mobile.
So i'm wondering how do they do ?
Upvotes: 26
Views: 55380
Reputation: 1
I needed to implement this feature in my code, and the solution from @n8jadams was very helpful. I have created two examples to illustrate the implementation.
The first example: Here, when the user lands on the page, the input field is automatically focused by default.
The second example: In this scenario, the user opens the dialog, and the input element is automatically focused when the dialog opens.
Both scenarios utilize the same function.
// setAutofocus function
const setAutofocus = (wrapper, autofocusInputElem) => {
const tmpElement = document.createElement('input')
tmpElement.classList.add('hidden-input')
document.querySelector(`${wrapper}`).prepend(tmpElement)
tmpElement.focus()
setTimeout(() => {
autofocusInputElem.focus()
tmpElement.remove()
}, 0)
}
// FIRST CASE
// The user arrives at the page and the input field has autofocus
const autofocusInputElemOne = document.querySelector('#default-autofocus [autofocus]')
if (autofocusInputElemOne) {
setAutofocus('#default-autofocus', autofocusInputElemOne)
}
// SECOND CASE
// With a dialog, when the dialog opens and the input element has the autofocus attribute, it automatically receives focus
const autofocusInputElemTwo = document.querySelector('#dialog-autofocus [autofocus]')
const dialog = document.querySelector("dialog")
const showButton = document.querySelector("dialog + button")
const closeButton = document.querySelector("dialog button")
// "Show the dialog" button opens the dialog modally
showButton.addEventListener("click", () => {
dialog.showModal()
if (autofocusInputElemTwo) {
setAutofocus('#dialog-autofocus', autofocusInputElemTwo)
}
})
// "Close" button closes the dialog
closeButton.addEventListener("click", () => {
dialog.close()
})
input.hidden-input {
position: absolute;
opacity: 0;
z-index: -1;
}
<h2>Default autofocus FIRST CASE</h2>
<section id="default-autofocus">
<form>
<div>
<label for="name">Name</label>
<input autofocus for="name" autofocus type="text">
</div>
<div>
<label for="company">Company</label>
<input for="company" type="text">
</div>
</form>
</section>
<h2>Dialog autofocus SECOND CASE</h2>
<section id="dialog-autofocus">
<dialog>
<form>
<div>
<label for="name">Name</label>
<input autofocus for="name" type="text">
</div>
<div>
<label for="company">Company</label>
<input for="company" type="text">
</div>
</form>
<button>Close</button>
</dialog>
<button>Show the dialog</button>
</section>
Upvotes: 0
Reputation: 89
You can add focus by using useRef.
const inputRef = useRef(null)
// run this on load, click, or after a submission
inputRef.current?.focus()
<input ref={inputRef}/>
As an aside, my keyboard would close on mobile after I would send a message in my chat. To fix this, I changed my code to use onClick instead of onSubmit. I then set it to submit onKeyDown of "Enter".
This made it so my send button would keep the keyboard open as well as using "Enter"/"Return" to submit a message.
Upvotes: 0
Reputation: 1
import {FocusTrap} from 'vueuc';
<FocusTrap active>
<input/>
</FocusTrap>
Upvotes: 0
Reputation: 4951
n8jadams seemed to be the only thing that actually solved this. In my application sometimes the modal response time was unknown (server side) and in these cases a predictable timeout could not be made.
I adjusted their answer to use an observer to detect when the element becomes visible instead of using a timeout, thus removing the timeout parameter.
I also put a check to see if the element is visible before applying the "hack" as it is not needed.
focusAndOpenKeyboard: function (elementId) {
var el = document.getElementById(elementId);
var __tempEl__ = document.createElement('input');
if (el) {
// Function to focus on the target element and remove the observer
function focusOnElementAndCleanup() {
el.focus();
el.click();
// Conditionally check these two as we only set them up when the target input is invisible.
if (document.body.contains(__tempEl__)) { document.body.removeChild(__tempEl__); } // Remove the temporary element
if (observer) { observer.disconnect(); }// Cleanup the observer
}
// Check if the target element is already visible
if (isVisible(el)) {
focusOnElementAndCleanup();
} else {
focusOnDummyElementToOpenIOSKeyboard();
// Create a MutationObserver to watch for changes in the DOM
var observer = new MutationObserver(function (mutationsList) {
for (var mutation of mutationsList) {
if (mutation.type === 'childList' && isVisible(el)) {
focusOnElementAndCleanup();
break;
}
}
});
// Start observing changes in the parent node (you can change this to a more appropriate parent)
observer.observe(document.body, { childList: true, subtree: true });
}
// Create a temporary input element to focus on and open the keyboard
function focusOnDummyElementToOpenIOSKeyboard() {
__tempEl__.style.position = 'absolute';
__tempEl__.style.top = (el.offsetTop + 7) + 'px';
__tempEl__.style.left = el.offsetLeft + 'px';
__tempEl__.style.height = 0;
__tempEl__.style.opacity = 0; // Set opacity to 0 to make it invisible
document.body.appendChild(__tempEl__);
__tempEl__.focus();
}
}
// Function to check if the element is visible in the DOM
function isVisible(element) {
return element && element.offsetParent !== null;
}
// Carry on with opening modal, and showing elementId to be focused.
},
Upvotes: 0
Reputation: 31
Worked in 2022 with ios 16! OMG, I searched for so long and the above solution won't work for me.
Here is how it worked for me. I wrapped the input in a React FocusLock component. Check this package out: https://www.npmjs.com/package/react-focus-lock
Here is a small example:
<FocusLock>
<Input />
</FocusLock>
Upvotes: 3
Reputation: 233
Angular solution:
on button click we need to create temporary input, append to existing container (close to our input) and focus on it.
btnClicked() {
this.showModal = true;
this.searchBar = this.renderer2.selectRootElement('#searchBar', true);
// 2nd argument preserves existing content
// setting helper field and focusing on it
this.inputHelper = this.renderer2.createElement('input');
this.renderer2.appendChild(this.searchBar, this.inputHelper);
this.inputHelper.focus();
let event = new KeyboardEvent('touchstart',{'bubbles':true});
this.searchBarButton.nativeElement.dispatchEvent(event);
}
after modal/target input is shown, we move focus and remove temporary one:
initiateKeyboard() {
setTimeout(()=> {
this.searchBarInput.nativeElement.focus();
this.renderer2.removeChild(this.searchBar, this.inputHelper);
},180);
}
and template:
<div id="searchBar">
<input type="button" class="button is-link is-light" value="Search" (click)="btnClicked()" (touchstart)="initiateKeyboard()" #searchBarButton>
</div>
You just need to remember that iPhone may zoom screen, so you need to adjust parameters of temporary input.
working solution: https://inputfocus.vercel.app/
Upvotes: 0
Reputation: 387
This really drives me/us crazy. It works fine on the Android phone, but something is disabled by the Apple developer. (I understand it's annoying to pop the keyboard when not necessary though).
I accidentally found out that the "popup" module from Semantic-UI fixes this magically.
Note that the solution works for SemanticUI (@semantic-ui team may tell what event makes this work)
Here are how I did:
const [search, setSearch] = useState(false);
const inputRef = useRef(null);
React.useEffect(() => {
if (search) {
inputRef.current.focus();
} else {
inputRef.current.blur();
}
}, [search]);
<div onClick={() => setSearch(true)}>
<Popup
content="Search for Swimmers and Time Standards."
offset={[-500, -1000]}
trigger={<Icon name="search" />}
/>
</div>
{search && <Input ref={inputRef} />}
As you see, I wrapped the trigger Icon with the Popup module, and hide the Popup content by setting the crazy offset. And then it magically works.
See the demo here: https://swimstandards.com/ (check it out on your iPhone)
Upvotes: 0
Reputation: 1164
None of the other answers worked for me. I ended up looking into the Nike javascript code and this is what I came up with as a reusable function:
function focusAndOpenKeyboard(el, timeout) {
if(!timeout) {
timeout = 100;
}
if(el) {
// Align temp input element approximately where the input element is
// so the cursor doesn't jump around
var __tempEl__ = document.createElement('input');
__tempEl__.style.position = 'absolute';
__tempEl__.style.top = (el.offsetTop + 7) + 'px';
__tempEl__.style.left = el.offsetLeft + 'px';
__tempEl__.style.height = 0;
__tempEl__.style.opacity = 0;
// Put this temp element as a child of the page <body> and focus on it
document.body.appendChild(__tempEl__);
__tempEl__.focus();
// The keyboard is open. Now do a delayed focus on the target element
setTimeout(function() {
el.focus();
el.click();
// Remove the temp element
document.body.removeChild(__tempEl__);
}, timeout);
}
}
// Usage example
var myElement = document.getElementById('my-element');
var modalFadeInDuration = 300;
focusAndOpenKeyboard(myElement, modalFadeInDuration); // or without the second argument
Note that this is definitely a hacky solution, but the fact that Apple hasn't fixed this in so long justifies it.
Upvotes: 25
Reputation: 285
I found a solution, click()
didn't work, but i figured it out.
searchMobileToggle.addEventListener('click', function() {
if(mobileSearchblock.classList.contains('active')) {
searchField.setAttribute('autofocus', 'autofocus');
searchField.focus();
}
else {
searchField.removeAttribute('autofocus');
}
});
I was working with vue.js that was removing input autofocus
attribute, when the component was loaded.
So i had it on click, but there was another problem, the autofocus only worked once, but combined with focus(), it now work all the time :)
Thanks for your help !
Upvotes: 0
Reputation: 97
There is no legitimate way to do this since iOS kind of wants to only open the keyboard on a user interaction, however you can still achieve this with either using prompt()
or using focus()
from within a click()
event it and will show up.
Upvotes: 0