Javascript 如何防止iOS键盘使用CSS或JS将视图推离屏幕

Javascript 如何防止iOS键盘使用CSS或JS将视图推离屏幕,javascript,css,responsive,Javascript,Css,Responsive,我有一个响应性网页,当你点击一个按钮时,它会打开一个模式。当模式打开时,它被设置为使用固定定位占据页面的全宽和全高。模态中还有一个输入字段 在iOS设备上,当输入字段聚焦时,键盘打开。但是,当它打开时,它实际上会将整个文档向上推,使我的页面的一半超出视口的顶部。我可以确认实际的html标记本身已经被推上以补偿键盘,并且它没有通过CSS或JavaScript发生 以前有人见过这种情况吗?如果有,有没有办法防止这种情况发生,或者在键盘打开后重新定位?这是一个问题,因为我需要用户能够看到模式顶部的内容

我有一个响应性网页,当你点击一个按钮时,它会打开一个模式。当模式打开时,它被设置为使用固定定位占据页面的全宽和全高。模态中还有一个输入字段

在iOS设备上,当输入字段聚焦时,键盘打开。但是,当它打开时,它实际上会将整个文档向上推,使我的页面的一半超出视口的顶部。我可以确认实际的
html
标记本身已经被推上以补偿键盘,并且它没有通过CSS或JavaScript发生


以前有人见过这种情况吗?如果有,有没有办法防止这种情况发生,或者在键盘打开后重新定位?这是一个问题,因为我需要用户能够看到模式顶部的内容,同时,我希望自动聚焦输入字段。

IOS8和Safari Bowser在输入时的行为相同。页面加载后出现focus()。它们都会缩放元素并打开键盘。(不太确定这是否有帮助,但您是否尝试过使用类似的东西?)

HTML是

<input autofocus>
此外,您还必须使用用户代理解决方案,您可以将其用于所有IOS版本

首先


在某些情况下,可以通过重新聚焦输入元素来缓解此问题

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} />
constInputElement=useRef(null);
useffect(()=>{
inputElement.current.onfocus=()=>{
滚动到(0,0);
document.body.scrollTop=0;
};
});

如果不想滚动到顶部(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 });
就我个人而言,我在苗条的动作中使用了这段代码,它在我苗条的苹果地图PWA克隆中运行得非常好

您将在视频中注意到,在视口上半部分的输入动画稳定后,“自动完成”会发生更改。这就是焦点重新切换的发生


这种攻击的唯一缺点是,原始实现上的焦点处理程序将运行两次,但有一些方法可以用元数据解释这一点。

您看到了吗:@Iceman不,我没有。我试图寻找类似的问题,但没有找到。我试试看。我不确定这是否会有帮助,因为这个解决方案是针对IOS8的,因为我在IOS8和Safari上遇到了类似的问题。@Iceman我的环境不是phonegap,它只是一个普通的网站。所以我不确定phonegap是否有不同之处,但这对我来说不起作用。新版本可能会出现问题。我会删除我的答案。对不起,也许我不明白。按钮和警报会有什么帮助?我想我理解错了。我在想,也许你是想在触摸页面后显示虚拟键盘和滚动。啊,我明白了。不,我正在尝试阻止虚拟键盘将DOM推到外部viewport@rescuecreative只是一个提示,您是否尝试过使用下面提到的这两个选项?谢谢!它工作得很好。顺便说一句,我只使用
document.body.scrollTop=0
;我花了4个小时试图解决一个问题,直到遇到你的答案。非常感谢你。尽管只需要
input.onfocus=function(){window.scrollTo(0,0);document.body.scrollTop=0;}
。第一部分冻结页面。这种方法将挂起设备。想象一下,在无限的时间里,模糊并重新聚焦在同一个元素上。
<script type="text/javascript">
 $(document).ready(function() {
     document.ontouchmove = function(e){
          e.preventDefault();
          }
 });
input.onfocus = function () {
    window.scrollTo(0, 0);
    document.body.scrollTop = 0;
}
input.onfocus = function () {
  this.blur();
  this.focus();
}
const inputElement = useRef(null);

useEffect(() => {
  inputElement.current.onfocus = () => {
    window.scrollTo(0, 0);
    document.body.scrollTop = 0;
  };
});

<input ref={inputElement} />
window.scrollBy(0, 0)
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 });