移动端input开发问题

做为前端开发,工做中不可避免的要接触input,要基于input作一些定制化开发,但由于安卓ios系统的差别性,宿主app的webview选择(特指ios),app的历史遗留问题等等,会出现大量的兼容性问题css

下面,对我开发过程当中遇到的问题以及解决方案进行记录,但愿能帮到大家html

1. ios webview类型

ios下不一样的app内置的webview可能不一样,致使input行为不一致,因此咱们先大体了解一下,目前ios系统下有两种webview:WK和UI前端

ios8以前,一直使用的是UI,可是从ios8开始,新增了WK类型,相比UI,WK加载速度更快,消耗内存更少,而且支持更多h5的特性,因此各大公司基本都会对此进行升级,但不排除有些app由于各类历史缘由还在使用UI,好比咱们,目前咱们的h5页面运行在多个app内,有UI也有WK,天然在开发的时候作一些兼容判断ios

判断webview类型git

// 暂没发现好的方法来判断webview的类型,除非ios开发告诉你
// 下面这个方式是经过某些只有wk支持的h5新特性来判断
function getIosWebviewType() {
    if (navigator.platform.substr(0, 2) === 'iP') {
        // iOS (iPhone, iPod or iPad)
        const lte9 = /constructor/i.test(window.HTMLElement);
        const nav = window.navigator; const ua = nav.userAgent;
        const idb = !!window.indexedDB;
        if (ua.indexOf('Safari') !== -1 && ua.indexOf('Version') !== -1 && !nav.standalone) {
            return 'Safari'
        } else if ((!idb && lte9) || !window.statusbar.visible) {
            return 'UI'
        } else if ((window.webkit && window.webkit.messageHandlers) || !lte9 || idb) {
            return 'WK'
        }
    }
    return 'unknown'
}

2. autofocus失效

不论是h5规范仍是在caniuse上面查,这个属性都是支持的,但你在真机上面测试的时候,不少状况下,并非你想要的效果github

目前我测试状况以下:web

  • iosapi

    • UIWebview,能够聚焦,有光标,能呼起键盘
    • WKWebview,能够聚焦,有光标,<u>但呼不起键盘</u>
  • 安卓浏览器

    • 暂时没发现问题(但不绝对)

网上了解到的缘由是,苹果但愿由用户来触发,输入这个操做是用户的意愿,而不是强制的,因此在新版本系统中禁止了这个属性,老版本的UI上面尚未这个限制app

但庆幸的是,不论是安卓仍是ios,都支持在touch或者click事件中<u>同步</u>的执行input.focus()来聚焦并呼起键盘,

若是业务场景符合这种状况,那能够经过这种方式来作

onClick = () => {
  this.input.focus()
}

但若是在点击事件中有异步操做时,同autofocus属性,只能聚焦,不能呼起键盘

onClick = () => {
  setTimeout(() => {
    this.input.focus()
  }, 1000)
  
  // 或者
fetch('/api/get').then(() => {
    this.input.focus()
  })
}

降级处理

若是业务场景不符合上面的状况,但又非要这个功能,有一种方案是,聚焦以后,强化一下聚焦的效果(由于原生的光标展现毕竟不明显)

  • 设置一个聚焦的样式,在input上也好,在页面其余元素也好,由本身产品特点以及UE决定
  • 适当的滚动页面,使其到视图中间

可参考

3. 聚焦问题

3.1 点击没法聚焦或者聚焦以后又失焦

大部分状况是移动端300ms问题引发的

一种是:app使用UIWebview可是h5页面没引入fastclick

另外一种是:引入了fastclick,但须要对fastclick的focus进行优化,改成

FastClick.prototype.focus = function (targetElement) {
    targetElement.focus();
};

3.2 光标没法聚焦到点击位置

这个大几率也是fastclick致使的,可参考:连接

3.3 已有聚焦input的状况下点击其余input,会跳动

这个大几率也是fastclick致使,可参考上面那个连接

或者这样改fastclick源码,在onTouchEnd中新增判断,是否须要走原生聚焦逻辑

FastClick.prototype.onTouchEnd = function (event) {
    // ...
    if (targetTagName === 'label') {
        // ...
    } else if (this.needsFocus(targetElement)) {
          // 新增
        if (!this.needsFocusInput()) return false

          // ...
    }

    // ...
};

// 新增
// 已有聚焦元素的状况下,直接走原生input聚焦逻辑
FastClick.prototype.needsFocusInput = function () {
    const focusInput = document.querySelector('input:focus')
    return !focusInput
}

3.4 建议

上面这几个问题,大部分是fastclick致使的,但fastclick所解决的问题(300ms),目前大部分浏览器已经解决了,因此确认一下,若是大家的app不用UIWebview的话,那能够直接去掉fastclick了

fastclick github说明:

Note: As of late 2015 most mobile browsers - notably Chrome and Safari - no longer have a 300ms touch delay, so fastclick offers no benefit on newer browsers, and risks introducing bugs into your application. Consider carefully whether you really need to use it.

4. 失焦问题

4.1 但愿保持聚焦

有时候,但愿点击页面其余地方的时候,input保持聚焦状态,但浏览器默认行为是将input失焦

解决方案:在点击事件中,阻止默认行为

function onClick(e) {
    // 你的事件处理代码
    ...
    
    e.preventDefault();
    // iphone有的机型下,没有阻止掉默认行为,主动再聚焦一下
    input.focus();
}

4.2 但愿主动失焦

安卓某些机型有如下问题

  • 聚焦状况下,点击页面其余地方,不主动失焦
  • 某些状况下,主动隐藏键盘以后,input并无失焦,可是用户触摸页面其余地方时,由于input还处于聚焦状态,会呼起键盘

针对性的解决方案也分两种

第一种:监听用户行为,主动失焦

const autoBlur = (e) => {
  const target = e.target
  const { tagName, className } = target
  // 点击非input区域
  if (tagName.toUpperCase() !== 'INPUT') {
    this.input.blur()
  }
}
document.body.addEventListener('touchstart', autoBlur)

第二种:监听键盘高度变化,主动失焦

const onKeyboardChange = (resize) => {
  // 有时候,好比number变成text,或者系统自动在键盘上面加一些装饰,键盘并无隐藏,可是触发了resize
  // 测试大部分机型,全部的键盘确定大于120高度了,因此加一个限制
  if (Math.abs(resize) < 120) return

  const show = resize > 0
  if (!show) {
    this.input.blur()
  }
}

function getClientHeight() {
    return document.documentElement.clientHeight || document.body.clientHeight;
}

// 记录原始高度
let originHeight = getClientHeight()

// 监听键盘变化
window.addEventListener('resize', () => {
  const resizeHeight = getClientHeight()

  const resize = originHeight - resizeHeight
  onKeyboardChange(resize);
  originHeight = resizeHeight;
}, false)

5. 聚焦以后滚动到视图内

大部分状况下,系统会帮你将input滚动到视图内,少数状况下,须要咱们本身设置

  • 一种方案是,直接调用scrollIntoViewIfNeeded api
  • 另外一种是,计算聚焦先后文档高度的变化差值,而后将body高度相应的调大,而且设置scrollTop,<u>这种状况应该是出如今UIWebview下</u>
// 正常处理
input.addEventListener('focus', () => {
  setTimeout(() => {
    this.input.scrollIntoViewIfNeeded();
  }, 300)
})
// UIWebview下的处理
function getClientHeight() {
    return document.documentElement.clientHeight || document.body.clientHeight;
}
const bodyHeight = getClientHeight()
const bodyOverflow = getComputedStyle(document.body).overflow

input.addEventListener('focus', () => {
  document.body.style.overflow = 'auto'
  setTimeout(() => {
    // alert(getClientHeight())
    let height = bodyHeight - getClientHeight()
    if (height < 0) height = height * -2
    document.body.style.height = `${bodyHeight + height}px`;
    document.body.scrollTop = height;
  }, 300)
})

// 若是设置了高度,在blur时要设置回来
input.addEventListener('blur', () => {
  document.body.style.height = `${bodyHeight}px`;
  document.body.style.overflow = bodyOverflow
})
加setTimeout是由于唤起键盘会有一个动画,咱们须要在动画结束以后再滚动页面,不然计算出的高度不许

大部分状况下,键盘动画时间小于300

6. 强制光标到末尾

input.addEventListener('touchend', e => {
  const length = e.target.value.length
  e.target.setSelectionRange(length, length);
  e.preventDefault()
  e.target.focus()
})

7. 其余的一些问题

7.1 光标隐藏

某些场景下,须要自定义输入框,自定义光标等

安卓能够经过opacity: 0; color: transparent等将原生光标隐藏

但ios没法隐藏,只能将input框移动到视图外,先将input宽度设置足够大,而后左移:

input {
  width: 1000px;
  margin-left: -200px;
}

7.2 复制粘贴

安卓和ios都支持长按复制粘贴,但ios的前提是,光标必须在视图内,像上面那样将input左移以后,粘贴按钮不在视图内,没法粘贴

7.3 短信验证码(或其余数字)展现在系统键盘

新版ios(应该是12+,不肯定)系统收到短信之后会直接将短信展现在键盘上面,用户一点就能够输入到input,但老版不支持,须要用户主动去短信复制,而后再粘贴到input,若是input是自定义的,须要注意上面问题

部分安卓机器(好比小米6+)收到短信,可直接点通知栏进行复制,一样会出如今键盘上,其余机型须要用户长按粘贴

8. 结语

上面基本是我开发过程当中遇到的问题了,随着系统的迭代升级,不少问题会获得官方的解决,可是咱们没法决定app是否升级,没法决定用户是否升级

因此,若是大家也遇到这些问题,但愿能够给予大家启发,帮助

相关文章
相关标签/搜索