如何防止 iOS 键盘使用 CSS 或 JS 将视图推离屏幕
How to prevent iOS keyboard from pushing the view off screen with CSS or JS
我有一个响应式网页,当您点击一个按钮时,它会打开一个模式。当模式打开时,它被设置为使用固定定位占据页面的整个宽度和高度。模式也有一个输入字段。
在 iOS 设备上,当输入字段获得焦点时,键盘会打开。然而,当它打开时,它实际上将整个文档向上推开,以至于我的页面的一半超出了视口的顶部。我可以确认实际的 html
标签本身已经被推高以补偿键盘并且它没有通过 CSS 或 JavaScript.
发生
有没有人以前见过这个,如果有,有没有办法阻止它,或者在键盘打开后重新定位东西?这是一个问题,因为我需要用户能够看到模式顶部的内容,同时我想自动聚焦输入字段。
IOS8 和 Safari bowsers 对于页面加载后发生的 input.focus() 行为相同。他们都缩放到元素并调出键盘。(不太确定这是否有帮助,但您是否尝试过使用类似的方法?)
HTML IS
<input autofocus>
JS是
for (var i = 0; i < 5; i++) {
document.write("<br><button onclick='alert(this.innerHTML)'>" + i + "</button>");
}
//document.querySelector('input').focus();
CSS
button {
width: 300px;
height: 40px;
}
ALso you will have to use a user-agent workaround, you can use it for all IOS versions
if (!/iPad|iPhone|iPod/g.test(navigator.userAgent)) {
element.focus();
}
first
<script type="text/javascript">
$(document).ready(function() {
document.ontouchmove = function(e){
e.preventDefault();
}
});
then this
input.onfocus = function () {
window.scrollTo(0, 0);
document.body.scrollTop = 0;
}
在某些情况下,可以通过重新聚焦输入元素来缓解此问题。
input.onfocus = function () {
this.blur();
this.focus();
}
对于在 React 中遇到这个问题的任何人,我已经设法通过适应 @ankurJos 解决方案来修复它:
const inputElement = useRef(null);
useEffect(() => {
inputElement.current.onfocus = () => {
window.scrollTo(0, 0);
document.body.scrollTop = 0;
};
});
<input ref={inputElement} />
如果你不想滚动到顶部(0, 0)你也可以这样做
window.scrollBy(0, 0)
我为此苦苦挣扎了一段时间,找不到适合我的东西。
我最后做了一些 JavaScript 黑客来让它工作。
我发现如果输入元素位于屏幕的上半部分,Safari 不会推送视口。这是我的小窍门的关键:
我拦截了输入对象上的焦点事件,而是将焦点重定向到不可见的(通过 transform: translateX(-9999px)
)。然后,一旦键盘出现在屏幕上(通常是 200 毫秒左右),我就会触发原始元素上的焦点事件,该元素已在屏幕上显示动画。
这是一种复杂的交互,但效果很好。
function ensureOffScreenInput() {
let elem = document.querySelector("#__fake_input");
if (!elem) {
elem = document.createElement("input");
elem.style.position = "fixed";
elem.style.top = "0px";
elem.style.opacity = "0.1";
elem.style.width = "10px";
elem.style.height = "10px";
elem.style.transform = "translateX(-1000px)";
elem.type = "text";
elem.id = "__fake_input";
document.body.appendChild(elem);
}
return elem;
}
var node = document.querySelector('#real-input')
var fakeInput = ensureOffScreenInput();
function handleFocus(event) {
fakeInput.focus();
let last = event.target.getBoundingClientRect().top;
setTimeout(() => {
function detectMovement() {
const now = event.target.getBoundingClientRect().top;
const dist = Math.abs(last - now);
// Once any animations have stabilized, do your thing
if (dist > 0.01) {
requestAnimationFrame(detectMovement);
last = now;
} else {
event.target.focus();
event.target.addEventListener("focus", handleFocus, { once: true });
}
}
requestAnimationFrame(detectMovement);
}, 50);
}
node.addEventListener("focus", handleFocus, { once: true });
我个人在 Svelte 操作中使用此代码,它在我的 Apple Maps 的 Svelte PWA 克隆中运行得非常好。
Video of it working in a PWA clone of Apple Maps
您会在视频中注意到,在视口上半部分的输入动画稳定后,自动完成会发生变化。那就是焦点切换回来了。
此 hack 的唯一缺点是您原始实现的焦点处理程序将 运行 两次,但有一些方法可以用元数据来解决这个问题。
我有一个响应式网页,当您点击一个按钮时,它会打开一个模式。当模式打开时,它被设置为使用固定定位占据页面的整个宽度和高度。模式也有一个输入字段。
在 iOS 设备上,当输入字段获得焦点时,键盘会打开。然而,当它打开时,它实际上将整个文档向上推开,以至于我的页面的一半超出了视口的顶部。我可以确认实际的 html
标签本身已经被推高以补偿键盘并且它没有通过 CSS 或 JavaScript.
有没有人以前见过这个,如果有,有没有办法阻止它,或者在键盘打开后重新定位东西?这是一个问题,因为我需要用户能够看到模式顶部的内容,同时我想自动聚焦输入字段。
IOS8 和 Safari bowsers 对于页面加载后发生的 input.focus() 行为相同。他们都缩放到元素并调出键盘。(不太确定这是否有帮助,但您是否尝试过使用类似的方法?)
HTML IS
<input autofocus>
JS是
for (var i = 0; i < 5; i++) {
document.write("<br><button onclick='alert(this.innerHTML)'>" + i + "</button>");
}
//document.querySelector('input').focus();
CSS
button {
width: 300px;
height: 40px;
}
ALso you will have to use a user-agent workaround, you can use it for all IOS versions
if (!/iPad|iPhone|iPod/g.test(navigator.userAgent)) {
element.focus();
}
first
<script type="text/javascript">
$(document).ready(function() {
document.ontouchmove = function(e){
e.preventDefault();
}
});
then this
input.onfocus = function () {
window.scrollTo(0, 0);
document.body.scrollTop = 0;
}
在某些情况下,可以通过重新聚焦输入元素来缓解此问题。
input.onfocus = function () {
this.blur();
this.focus();
}
对于在 React 中遇到这个问题的任何人,我已经设法通过适应 @ankurJos 解决方案来修复它:
const inputElement = useRef(null);
useEffect(() => {
inputElement.current.onfocus = () => {
window.scrollTo(0, 0);
document.body.scrollTop = 0;
};
});
<input ref={inputElement} />
如果你不想滚动到顶部(0, 0)你也可以这样做
window.scrollBy(0, 0)
我为此苦苦挣扎了一段时间,找不到适合我的东西。
我最后做了一些 JavaScript 黑客来让它工作。
我发现如果输入元素位于屏幕的上半部分,Safari 不会推送视口。这是我的小窍门的关键:
我拦截了输入对象上的焦点事件,而是将焦点重定向到不可见的(通过 transform: translateX(-9999px)
)。然后,一旦键盘出现在屏幕上(通常是 200 毫秒左右),我就会触发原始元素上的焦点事件,该元素已在屏幕上显示动画。
这是一种复杂的交互,但效果很好。
function ensureOffScreenInput() {
let elem = document.querySelector("#__fake_input");
if (!elem) {
elem = document.createElement("input");
elem.style.position = "fixed";
elem.style.top = "0px";
elem.style.opacity = "0.1";
elem.style.width = "10px";
elem.style.height = "10px";
elem.style.transform = "translateX(-1000px)";
elem.type = "text";
elem.id = "__fake_input";
document.body.appendChild(elem);
}
return elem;
}
var node = document.querySelector('#real-input')
var fakeInput = ensureOffScreenInput();
function handleFocus(event) {
fakeInput.focus();
let last = event.target.getBoundingClientRect().top;
setTimeout(() => {
function detectMovement() {
const now = event.target.getBoundingClientRect().top;
const dist = Math.abs(last - now);
// Once any animations have stabilized, do your thing
if (dist > 0.01) {
requestAnimationFrame(detectMovement);
last = now;
} else {
event.target.focus();
event.target.addEventListener("focus", handleFocus, { once: true });
}
}
requestAnimationFrame(detectMovement);
}, 50);
}
node.addEventListener("focus", handleFocus, { once: true });
我个人在 Svelte 操作中使用此代码,它在我的 Apple Maps 的 Svelte PWA 克隆中运行得非常好。
Video of it working in a PWA clone of Apple Maps
您会在视频中注意到,在视口上半部分的输入动画稳定后,自动完成会发生变化。那就是焦点切换回来了。
此 hack 的唯一缺点是您原始实现的焦点处理程序将 运行 两次,但有一些方法可以用元数据来解决这个问题。