如何让固定内容显示在iOS键盘上方?

问题描述 投票:0回答:7

我只能找到人们有相反问题的问题。

我希望我的固定内容位于 iOS 键盘上方。 问题图片:

我希望 iOS 表现得像 Android。

有没有简单的方法可以实现这一点?

父元素CSS:

.parent{
    position:fixed;
    top: 0;
    left 0;
    right: 0;
    bottom: 0;
    width: 100%;
    height: 100%;
}

按钮CSS:

.button{
    position:fixed;
    left 0;
    right: 0;
    bottom: 0;
    width: 100%;
    height: 5rem;
}
ios css iphone google-chrome safari
7个回答
21
投票

我们可以使用VisualViewport来计算键盘高度。这样我们就可以正确设置固定内容 pos 了。

小演示:https://whatwg6.github.io/pos-above-keyboard/index.html

代码片段:

const button = document.getElementById("button");
const input = document.getElementById("input");
const height = window.visualViewport.height;
const viewport = window.visualViewport;

window.addEventListener("scroll", () => input.blur());
window.visualViewport.addEventListener("resize", resizeHandler);

function resizeHandler() {
    if (!/iPhone|iPad|iPod/.test(window.navigator.userAgent)) {
      height = viewport.height;
    }
    button.style.bottom = `${height - viewport.height + 10}px`;
  }

function blurHandler() {
  button.style.bottom = "10px";
}
html,
body {
  margin: 0;
  padding: 0;
}

#button {
  position: fixed;
  width: 100%;
  bottom: 10px;
  background-color: rebeccapurple;
  line-height: 40px;
  text-align: center;
}
<input type="text" inputmode="decimal" value="0.99" id="input" onblur="blurHandler()" />
<div id="button">Button</div>

问题:https://developers.google.com/web/updates/2017/09/visual-viewport-api#the_event_rate_is_slow

为什么不使用innerHeight?:Iphone safari 无法在键盘打开时调整视口大小


13
投票

Mobile Safari 不支持位置:在输入焦点和显示虚拟键盘时修复。

要强制它以与移动 Chrome 相同的方式工作,您必须对整个页面或伪固定元素的容器使用position:absolute,height:100%,拦截滚动,touchend,焦点和模糊事件。

技巧是在激活焦点之前将点击的输入控件放在屏幕底部。在这种情况下,iOS Safari 总是可预测地滚动视口,并且 window.innerHeight 变为完全可见的高度。

在 Mobile Safari 中打开 https://avesus.github.io/docs/ios-keep-fixed-on-input-focus.html 查看其工作原理。

请避免使用具有多个可聚焦元素的表单,因为需要更多的固定位置技巧,这些技巧只是为了演示目的而添加的。

请注意,对于旋转和横向模式,需要额外的技巧。我正在开发一个名为 Tuff.js 的框架,它将提供一个全屏容器,帮助移动 Web 开发人员更快地构建 Web 应用程序。我花了将近一年的时间进行研究。

顺便说一下,为了防止虚拟键盘激活时整个窗口滚动,您可以使用这个超级简单的技巧

var hack = document.getElementById('scroll-hack');

function addScrollPixel() {
  if (hack.scrollTop === 0) {
    // element is at the top of its scroll position, so scroll 1 pixel down
    hack.scrollTop = 1;
  }

  if (hack.scrollHeight - hack.scrollTop === hack.clientHeight) {
    // element is at the bottom of its scroll position, so scroll 1 pixel up
    hack.scrollTop -= 1;
  }
}

if (window.addEventListener) {
  // Avoid just launching a function on every scroll event as it could affect performance. 
  // You should add a "debounce" to limit how many times the function is fired
  hack.addEventListener('scroll', addScrollPixel, true);
} else if (window.attachEvent) {
  hack.attachEvent('scroll', addScrollPixel);
}
body {
  margin: 0 auto;
  padding: 10px;
  max-width: 800px;
}

h1>small {
  font-size: 50%;
}

.container {
  display: flex;
  align-items: top;
  justify-content: space-between;
}

.container>div {
  border: #000 1px solid;
  height: 200px;
  overflow: auto;
  width: 48%;
  -webkit-overflow-scrolling: touch;
}
<h1>iOS Scroll Hack</h1>
<p>Elements with overflow:scroll have a slightly irritating behaviour on iOS, where when the contents of the element are scrolled to the top or bottom and another scroll is attempted, the browser window is scrolled instead. I hacked up a fix using minimal,
  native JavaScript.</p>
<p>Both lists have standard scrolling CSS applied (<code>overflow: auto; -webkit-overflow-scrolling: touch;</code>), but the list on the right has the hack applied. You'll notice you can't trigger the browser to scroll whilst attempting to scroll the list
  on the right.</p>
<p>The only very slight drawback to this is the slight "jump" that occurs when at the top or bottom of the list in the hack.</p>
<div class='container'>
  <div id='scroll-orig'>
    <ul>
      <li>1</li>
      <li>2</li>
      <li>3</li>
      <li>4</li>
      <li>5</li>
      <li>6</li>
      <li>7</li>
      <li>8</li>
      <li>9</li>
      <li>10</li>
      <li>11</li>
      <li>12</li>
      <li>13</li>
      <li>14</li>
      <li>15</li>
      <li>16</li>
      <li>17</li>
      <li>18</li>
      <li>19</li>
      <li>20</li>
    </ul>
  </div>
  <div id='scroll-hack'>
    <ul>
      <li>1</li>
      <li>2</li>
      <li>3</li>
      <li>4</li>
      <li>5</li>
      <li>6</li>
      <li>7</li>
      <li>8</li>
      <li>9</li>
      <li>10</li>
      <li>11</li>
      <li>12</li>
      <li>13</li>
      <li>14</li>
      <li>15</li>
      <li>16</li>
      <li>17</li>
      <li>18</li>
      <li>19</li>
      <li>20</li>
    </ul>
  </div>
</div>

这里

得到这个答案

7
投票

这是一个众所周知的问题,不幸的是,人们必须诉诸像现在接受的答案这样的黑客技巧。然而,W3C 正在指定VirtualKeyboard API

注意:在撰写本文时,此答案尚未准备好迎接黄金时段。重要的是要了解该规范还必须具有前瞻性,以适应未来无数可能的虚拟键盘。可能需要几年时间,可靠的跨平台浏览器支持才会开始出现,而这个答案将成为正确的答案。


1
投票
我改编了whatwg的答案,因为它不适用于我的网站(我不知道为什么)。 我使用属性 top 而不是bottom 作为绝对 div。 我的页面是一个聊天页面,我的 div 包含一个输入。

这是我的解决方案:

// Only for Safari on iOS // (use interactive-widget=resizes-content to fix Chrome) if (/iPad|iPhone|iPod/.test(navigator.userAgent)) { if (navigator.userAgent.indexOf('Chrome') === -1 && navigator.userAgent.indexOf('Safari') > -1) { // Put the body relative document.body.style.position = 'relative'; let marginTop = parseInt(window.getComputedStyle(document.body).marginTop); // My toolbar (in my case, a div with an input inside to make a chat) myBottomDiv.style.position = 'absolute'; // Events (touchmove on mobile, because the scroll event doesn't work well) window.addEventListener("scroll", resizeHandler); window.addEventListener("touchmove", resizeHandler); window.visualViewport.addEventListener("resize", resizeHandler); function resizeHandler() { myBottomDiv.style.top = (window.scrollY + window.visualViewport.height - marginTop - myBottomDiv.offsetHeight) + 'px'; } } }
    

0
投票
我发现了这个问题的一个有趣的解决方案。

解决方案是创建一个隐藏输入并将其焦点放在 touchstart 事件上。

<input id="backinput" style="position:absolute;top:0;opacity:0;pointer-events: none;"> <input id="input" style="position:absolute;bottom:0;">
使用 JQuery:

$('#backinput').on('focus',function(e) { e.preventDefault(); e.stopPropagation(); const input = document.getElementById('input'); input.focus({ preventScroll: true }); }) $('#input').on("touchstart", function (event) { if(!$(this).is(":focus")) { event.stopPropagation(); event.preventDefault(); $('#backinput').focus(); } })
最后,调整视口大小,使底部输入移动到键盘上方(如果需要)

window.visualViewport.addEventListener("resize", (event) => { $('body').height(parseInt(visualViewport.height)); });
对我来说它非常完美。我正在构建一个信使。


0
投票
当显示键盘或滚动屏幕时,底部 css 属性会发生变化。这不是你想要的,但这是我能实现的最好的)

下面你可以看到我的 React hook 及其用法。

import _ from 'lodash' import { useRef, useEffect } from 'react' export const useFixedPositionWithOpenedIOSKeyboard = (extraBottomOffset = 10) => { const elementRef = useRef(null) useEffect(() => { if (/iPhone|iPad|iPod/.test(window.navigator.userAgent)) { const setElementOffsetBottom = () => { const screenHeight = document.documentElement.clientHeight const screenHeightWithoutKeyboard = visualViewport?.height ?? 0 const offsetTop = visualViewport?.offsetTop ?? 0 if (elementRef?.current) { const elementStyles = (elementRef.current as HTMLElement).style if (Math.round(screenHeightWithoutKeyboard) < screenHeight) { elementStyles.bottom = `${ screenHeight - screenHeightWithoutKeyboard - offsetTop + extraBottomOffset }px` } else { elementStyles.bottom = '' } } } const debounceElementOffsetBottom = _.debounce(setElementOffsetBottom, 150) const showElement = () => debounceElementOffsetBottom() window.addEventListener('scroll', showElement) return () => window.removeEventListener('scroll', showElement) } }, []) return elementRef }
...

export const Component = () => { const buttonRef = useFixedPositionWithOpenedIOSKeyboard(); return ( <> <input type='text' /> <button type='submit' ref={button} style=' position: fixed; bottom: 40px; left: 50%; transform: translate(-50%); transition: bottom 0.5s cubic-bezier(0.4, 0, 0.2, 1); '> This is Button </button> </> ); }
    

0
投票
我觉得

whatwg的答案指向了visualViewport

的正确方向,但我想构建一些
带有滚动功能的东西(这有一些额外的挑战)和更多的代码清晰度。

在此处查看解决方案的简单演示:

https://nnrh69.csb.app/

本质上,它在 iOS 上将元素的

position: fixed

 更改为 
absolute
,然后在 
scrollviewport-resize 事件中调整其位置(同时捕获移动 Safari 的额外滚动问题)。

这是 JavaScript 的本质:

const fixedElement = document.querySelector('#fixedElement'); const fixedElementHeight = fixedElement.offsetHeight; const docHeight = document.documentElement.scrollHeight + fixedElementHeight; // Total height of the document (padding to be added manually) let fixedElementBottom; // Distance from top of the document to the BOTTOM of the fixedElement. Will change through scrolling and touch keyboard. function adjustFixedPos() { fixedElementBottom = document.documentElement.scrollTop + window.visualViewport.height; // Limit the position to the end of the document, otherwise Safari lets the user to scroll without end if (fixedElementBottom > docHeight) { fixedElementBottom = docHeight; } fixedElement.style.top = (fixedElementBottom - fixedElementHeight) + "px"; } // On iOS, switch to from fixed to absolute positioning which gets updated when needed. if (/iPhone|iPad|iPod/.test(window.navigator.userAgent)) { fixedElement.style.position = "absolute"; fixedElement.style.bottom = "auto"; adjustFixedPos(); document.addEventListener('scroll', adjustFixedPos, {passive: true}); window.visualViewport.addEventListener('resize', adjustFixedPos, {passive: true}); // Fires on touch keyboard extension and collapse }
您可以在

演示中看到完整的代码,包括 HTML 和 CSS。在移动版 Safari 中打开以查看实际的解决方法。在任何其他浏览器上,本机固定定位都已到位。

我希望它有帮助,并且有人在这方面花费的时间比我少(几天):)

© www.soinside.com 2019 - 2024. All rights reserved.