TAT.yana 移动端输入框填坑系列(一)
In 未分类 on 2017年03月21日 by view: 34,206
25

输入在移动端是一个很常用的功能,那么输入框必然是一个很重要的部分。然而,移动端输入框总会遇到各种各样的问题,无论是样式还是 ios 和 android 两端体验不一致都是很让我们头疼的问题,那么如何使移动 web 的输入框体验更贴近原生也成了一个需要我们多多思考和研究的问题。

一、文字输入限制问题

我们拿最多可输入 16 个字为例。当输入字数(注意,不是字符长度)超过 16 字时,会触发 tips 提示,并且不能继续输入。
办法一:
textarea 可以使用 maxlength 进行输入字数限制。
但是这个办法只能单纯的限制 length,有时并不能真正的结局问题。
办法二:
在将第二个办法之前先来讲讲下面的几种情况:
1、非直接的文字输入
什么叫做非直接的文字输入呢?

当输入汉字时必然会是非直接输入,需要我们点选才能正式输入。
当我们字数限制为 16 个字,需要实时检查是否到 16 字。输入文字时,当有非直接的文字输入时,监听 keydown 事件和 input 事件都会直接触发判断字数逻辑,会截断我们正在输入的文字。
解决办法:
监听 compositionend(当直接的文字输入时触发)这时,当没选中中文的时候不会进行字数判断。

2、emoji 表情的输入
当输入 emoji 的时候,但是,当输入 emoji 表情的时候,js 中判断 emoji 表情的 length 为 2,因此 emoji 正常应该最多只能输入 8 个,但是 ios 端却把 emoji 的 length 算为 1,可以输入 16 个 emoji。这样就导致了两端的体验不同。因此需要在 js 中来进行字数限制。
再加上汉字输入问题,那么就加入一个标记位,来判断是否是直接的文字输入。然后监听 input,限制字数,当超过字数限制的时候,把前 16 个字截断显示出来就 ok 了。

二、textarea 置底展示问题

ios 中的输入体验永远伴随着一个问题,就是当唤起键盘后,整个页面会被键盘压缩,也就是说页面的高度变小,并且所有的 fixed 全部变为了 absolute。
android 效果:

使用 fixed 定位   
可见 android 中唤起键盘是覆盖在页面上,不会压缩页面
在 ios 上的效果:

那么如果我们需要将输入框固定在屏幕下方,而当键盘被唤起同时输入框固定在键盘上方(如下图样式)该如何解决呢?

首先我们来看下 ios 的表现

可以看出,键盘会将页面顶上去。那么如果希望可以将输入框和键盘完全贴合,我们可以使用 div 模拟一个假的输入框,使用定位将真正的输入框隐藏掉,当点击假的输入框的时候,将真正的输入框定位到键盘上方,并且手动获取输入框焦点。

在实现过程中需要注意下面几个问题:
1、真正的输入框的位置计算
首先记录无键盘时的 window.innerHeight,当键盘弹出后再获取当前的 window.innerHeight,两者的差值即为键盘的高度,那么定位真输入框自然就很容易了
2、在 ios 下手动获取焦点不可以用 click 事件,需要使用 tap 事件才可以手动触发

3、当键盘收起的时候我们需要将真输入框再次隐藏掉,除了使用失去焦点(blur)方法,还有什么方法可以判断键盘是否收起呢?
这里可以使用 setInterval 监听,当当前 window.innerHeight 和整屏高度相等的时候判断为键盘收起。
注意:键盘弹起需要一点时间,所以计算当前屏幕高度也需要使用 setInterval
4、因为 textarea 中的文字不能置底显示,当输入超过一行 textarea 需要自动调整高度,因此将 scrollHeight 赋值给 textarea 的 height。当删除文字的时候需要 height 也有变化,因此每次 input 都先将 height 置 0,然后再赋值。

    

未完待续...

原创文章转载请注明:

转载自AlloyTeam:http://www.alloyteam.com/2017/03/moves-the-input-box-fill-series-a/

  1. godaangel 2018 年 4 月 24 日

    赶脚可以用 flex 做 input 框的吸底,这样在纵向 flex 就不会出现 fixed 那种飘忽的问题~~

  2. tony 2018 年 2 月 5 日

    textarea 置底展示问题 这个没有完整的源码和案例演示网址么 ios11 上试了很多方法还是没效果

  3. 小邓 2017 年 10 月 12 日

    博主,你好!文中提到 js 中判断 emoji 表情的 length 为 2,这个应该是不严谨的,刚好前不久探究过 emoji 字符长度的问题,可以参考:https://objcer.com/2017/07/20/explore-emoji-length/

  4. helvinliao 2017 年 4 月 23 日

    :mrgreen: 不愧是见过最漂亮 web 前端…

  5. 小海胆 2017 年 4 月 6 日

    大妹子,window.innerHeight 在 Android 下会变,IOS 不变,只是盖在上面,而 input/textarea 获得焦点后,会尽可能顶到 window 的顶部。
    ios 真是奇葩

  6. 斩月 2017 年 4 月 5 日

    如楼下所说,ios 键盘唤起 innerHeight 并没有改变,而是安卓改变了,但 ios 的确是压缩了页面

  7. Brian 2017 年 3 月 31 日

    在 ios 下,键盘出现和没出现 window.innerHeight 是没变化的。android 就不清楚。

  8. kid 2017 年 3 月 28 日

    input 只能输入数字 怎么很好的兼容 ios 和安卓呢?

    • kidney 2017 年 3 月 30 日

  9. hank 2017 年 3 月 28 日

    帮我了大忙

  10. 旧城Cafe 2017 年 3 月 27 日

    虽然这篇文章说的是移动端的事情,但我还是想说一下一段代码在桌面端的问题。就是上面用 cpLock 来进行中英文混合输入然后可以判断长度的代码。这段代码在 Chrome 最新版本下(其他版本没有求证)是有问题的,因为 input 事件在 compositionend 事件之前触发,虽然在移动端上是没有问题(测试是使用移动端的 Chrome),但是如果改成桌面移动端通用的代码会更好。

  11. sRect 2017 年 3 月 24 日

    上面文字输入限制问题,不用 jQuery,下面这样写,在 ios 里输入中文时,就不起作用了,哪里出问题了?var input = document.querySelector(“#input”); var cpLock; input.addEventListener(“compositionstart”,function(e){ cpLock = true; });input.addEventListener(“compositionend”,function(e){ cpLock = true; });input.addEventListener(“input”,function(e){ e.stopPropagation(); e.preventDefault(); if (!cpLock) { if (e.target.value.length – 5 >=0) { var str = e.target.value.substring(0, 4); e.target.value = str; console.log(“ 超过字数”); } } });

  12. 请求支援 2017 年 3 月 24 日

    请求支援:上面的文字输入限制问题 var input = document.querySelector(“#input”);var cpLock;input.addEventListener(“compositionstart”,function(e){ cpLock = true;});input.addEventListener(“compositionend”,function(e){ cpLock = true;});input.addEventListener(“input”,function(e){ e.stopPropagation(); e.preventDefault(); if (!cpLock) { if (e.target.value.length – 5 >=0) { var str = e.target.value.substring(0, 4); e.target.value = str; console.log(“ 超过字数”); } }}); 在 IOS 下输入中文就不能限制了,而用 jQuery 没问题,究竟是哪出问题了?

  13. lusyn 2017 年 3 月 23 日

    对于 “在 ios 下手动获取焦点不可以用 click 事件,需要使用 tap 事件才可以手动触发”,我经过测试发现 trigger 无论是 tap 还是 click 都是不行的,只有真正 click 在回调中 focus 唤起软键盘,而 tap 仍然是不行的。请确认一下。

    • 武汉大魔王 2017 年 5 月 20 日

      input 外部加个 div 什么的可以间接实现 input 聚焦

  14. lusyn 2017 年 3 月 23 日

    经过测试,发现在 ios 下手动触发 tap 是没办法唤起软键盘的,反而 click 是可以唤起的。我说的手动触发 tap、click 是指真实的 tap、click 行为不是 trigger(‘click’)。请确认

  15. sRect 2017 年 3 月 23 日

    喜欢这样的干货,期待 (二)

  16. 可口渴了 2017 年 3 月 22 日

    能否抽象出一个 until,似的体验,兼容性得到最大满足

  17. 超级木木 2017 年 3 月 21 日

    我的疑问似乎和题目不相关了,但还是想问问一下,如此这样限制用户输入,必然会带来一个交互体验的巨大问题,很多时候用户用 input 做粘贴框,粘贴之后编辑一下,比如删除一些信息,那么这种情况怎么解决呢?虽然,这和程序设计本身并不相关了,但我仍然感觉非常困扰。。。。其实日常的开发中很多程序的设计问题都是和需求、体验息息相关的,实难拆分

    • 邓映山 2017 年 3 月 23 日

      这个其实交互设计的问题,可以使用颜色和下划线标注超出的文案,以便用户进行调整。

    • TAT.yana

      TAT.yana 2017 年 3 月 23 日

      我这里用的是 substring 截取前十六个字的方法,所以如果是粘贴,会只保留前面限制的字数。如果需要粘贴以后编辑再来限制字数这样的交互体验只能靠禁止进行下一步操作来提示超限,实时监控字数和粘贴编辑的体验放在一起还是有些难操作

      • 小邓 2017 年 10 月 12 日

        关于这个问题,我实现了一个类似推特发推的输入框,超出数字限制的文字会高亮显示 @超级木木 @TAT.yana

      • 小邓 2017 年 10 月 12 日

        忘记贴链接了:https://objcer.com/2017/10/10/implement-a-word-limit-input-box/ https://codepen.io/yingshandeng/pen/boRNLx

  18. 热血洒红尘 2017 年 3 月 21 日

    这里有个问题,当用户在输入状态滚动页面怎么办,定位还是会被滚动上去的

    • TAT.yana

      TAT.yana 2017 年 3 月 27 日

      这个问题我研究下,后面会写文章~

发表评论到 TAT.yana