填充IOS下输入框的两个坑

2018-10-23 admin

前言

IOS的坑向来就不会少,这不,最近在重构组件库的时候,就发现了两个坑,然后也找到解决办法去把坑给填上,这里记录一下,以备后面回忆。

坑一:Input输入框配合系统输入法在实时搜索的应用中会出现错误的行为

该坑的示例代码如下:

特意录制了以下ios手机的效果:

<video src=/“https_/blogimages2016.oss-cn-hangzhou.aliyuncs.com/javascript/ios-input-not-composition.mp4”>Your browser does not support the video tag.</video>

从视频我们看到在我们不断的从键盘中输入一直会触发onChange事件,导致Toast一直在变化。

想要填坑,需要先了解一下:compositionstartcompositionend事件

compositionstart事件:

compositionstart 事件触发于一段文字的输入之前(类似于 keydown 事件,但是该事件仅在若干可见字符的输入之前,而这些可见字符的输入可能需要一连串的键盘操作、语音识别或者点击输入法的备选词)

compositionend事件:

compositionend事件触发于一段文字的输入完成或者取消()

如果不使用上述两个事件,那么每次输入的时候,都会触发onChange事件,从而出现了上面gif图片的那种错误的行为。

现在我们使用这两个事件来修复这个问题:

给input标签加入下面的监听事件:(react版本)

this._isCompositing = false
changeHandler = (event: any) => {
  event.persist()
  const {
    onChange,
  } = this.props
  let value = event.target.value
  this.setState({ value }, () => {
    // composition 的事件触发顺序一般如下:
    // compositionStart -> input * n -> compositionEnd
    // 但是在 firefox 中变成这样子:
    // compositionStart -> input * n -> compositionEnd -> input
    // 这里我们在 compositionEnd 之后统一触发一次
    // 此处留个坑,在 firefox 下会连续触发两次onChange, 不过可以用去抖来处理,配合Input组件的debounce属性
    // 如果需要在汉字或者语音等连续输入的时候不触发onChange
    // 这时候如果还在连续输入的话,不作任何处理
    if (this._isCompositing) {
      return
    }
    if (debounce) {
      this._debounceChangeHandler(event)
    } else if (onChange) {
      onChange(event)
    }
  })
}
compositionStartHandler = (event: any) => {
  this._isCompositing = true
  const { onCompositionStart } = this.props
  if (onCompositionStart) {
    onCompositionStart(event)
  }
}
compositionEndHandler = (event: any) => {
  const { onCompositionEnd } = this.props
  this._isCompositing = false
  this.changeHandler(event)
  if (onCompositionEnd) {
    onCompositionEnd(event)
  }
}
render() {
  return (
    <input
      ref={n => this._input = n}
      onChange={this.changeHandler}
      onCompositionStart={this.compositionStartHandler}
      onCompositionEnd={this.compositionEndHandler}
    />
  )
}

效果如下:

<video src=/“https_/blogimages2016.oss-cn-hangzhou.aliyuncs.com/javascript/ios-input-composition.mp4”>Your browser does not support the video tag.</video>

从视频可以看到使用了composition事件之后,只有等我们输入完整的中文点击确定后才会触发onChange事件。

坑二:Input输入框focus之后,点击输入框外部的任何区域都没法blur掉

这个行为在安卓是没问题的,但是牛逼的ios就是这么设计的,无奈的我们就只能去给这种设计做一个workaround的解决办法。因为safari不会主动blur,所以就需要我们去主动blur。

解决代码如下:(react版本, 非完整,仅供参考)

this._input = null
componentDidMount() {
  this._isIOS = !!navigator.userAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/)
  if (this._isIOS) {
    document.addEventListener('touchend', this.documentClickHandler)
  }
}
documentClickHandler = (e: any) => {
  if (this._input) {
    const activeElement = document.activeElement
    if (activeElement.tagName === 'INPUT' && this._input === activeElement && e.target !== this._input) {
      activeElement.blur()
    }
  }
}
render() {
  return (
    <input
      ref={n => this._input = n}
    />)
}

上述的解决办法用到了document.activeElement,该元素的定义如下:

activeElementDocumentShadownRoot接口中是只读的属性,它返回那些在DOM或者Shadow树中被聚焦的节点。大部分时候如果输入框有文本输入的时候activeElement会返回一个<input>或者<textarea>节点,如果是的话我们可以使用selectionStartselectionEnd属性获取更多的详情。另外一些情况可能返回一个<select>元素(在menu中)或者是buttoncheckboxradio类型的input元素。

如果没有选中任何节点,那么返回的是body或者null

另外提到的ShadowRootShadow DOM的根节点,Shadwon DOM是一颗DOM树,一般附着在正常页面DOM节点中,正常页面DOM附着的节点叫做shadow host,其层次结构是:

shadow DOM中,所有的标签和CSS样式都是在shadown host节点内部生效,也就是说定义在Shadow Root的CSS样式不会影响到它的父文档,定义在Shadow Root之外的CSS样式不会影响到主页面。

举个例子:

从示例图上可以看到video标签内部其实实现了一套自己的shadow dom

打开查看shadow dom的配置如下:

参考:

  1. shadow-dom
  2. compositionend
  3. activeElement

原文链接:https://blog.5udou.cn/blog/Tian-Chong-IOSXia-Shu-Ru-Kuang-De-Liang-Ge-Kang-99

本站文章除注明转载外,均为本站原创或编译。欢迎任何形式的转载,但请务必注明出处。

转载请注明:文章转载自 JavaScript中文网 [https://www.javascriptcn.com]

本文地址:https://www.javascriptcn.com/read-43467.html

文章标题:填充IOS下输入框的两个坑

相关文章
JavaScript常用特效chm下载
下载地址:JavaScript常用特效chm下载 对了,如果打开空白,在手册上右键属性解除锁定即可。 ...
2015-11-12
从2014年的发展来展望JS的未来将会如何
&lt;font face=&quot;寰杞, Arial, sans-serif &quot;&gt;2014骞达杞浠惰杩瑷灞涓┓锛互婊¤冻ㄦ涓姹浜...
2015-11-12
12个你未必知道的CSS小知识
虽然CSS并不是一种很复杂的技术,但就算你是一个使用CSS多年的高手,仍然会有很多CSS用法/属性/属性值你从来没使用过,甚至从来没听说过。 1.CSS的color属性并非只能用于文本显示 对于CSS的color属性,相信所有Web开发人员...
2015-11-12
ajax为什么令人惊异?ajax的优缺点
使用Ajax的最大优点,就是能在不更新整个页面的前提下维护数据。这使得Web应用程序更为迅捷地回应用户动作,并避免了在网络上发送那些没有改变的信息。 Ajax不需要任何浏览器插件,但需要用户允许JavaScript在浏览器上执行。就像DHT...
2015-11-12
HTML5的5个不错的开发工具推荐
HTML5规范终于在今年正式定稿,对于从事多年HTML5开发的人员来说绝对是一个重大新闻。数字天堂董事长,DCloud CEO王安也发表了文章,从开发者和用户两个角度分析了HTML对两个人群的优势。其实,关于HTML5的开发工具,我们以往的...
2015-11-12
JavaScript教程:JS中的原型
Keith Peters 几年前发表的一篇博文,关于学习没有“new”的世界,其中解释了使用原型继承代替构造函数。两者都是纯粹的原型编码。 标准方法(The Standard Way) 一直以来,我们学习的在 JavaScript 里创建对...
2015-11-12
AJAX的浏览器支持
AJAX 的要点是 XMLHttpRequest 对象。 不同的浏览器创建 XMLHttpRequest 对象的方法是有差异的。 IE 浏览器使用 ActiveXObject,而其他的浏览器使用名为 XMLHttpRequest 的 Jav...
2015-11-12
JavaScript教程 iis7站长之家
React is a JavaScript library for building user interfaces. Just the UI: Lots of people use React as the V in MVC. Since...
2015-11-12
Riot.js:不足1KB的MVP客户端框架
Riot.js是一款MVP(模型-视图-呈现)开源客户端框架,其最大的特点就是体积非常小,不足1KB,虽然体积小,但它可以帮助用户构建大规模的Web应用程序。 Riot.js是由Moot公司开发,目前最新版本为v0.9.2,遵循MIT开源许...
2016-03-11
JavaScript的组成
一个完整的JavaScript由3个部分组成:核心(ECMAScript) 文档对象模型(DOM) 浏览器对象模型(BOM) ECMAScript 描述了该语言的语法和基本对象 ; DOM 描述了处理网页内容的方法和接口 ; BOM 描...
2015-11-12
回到顶部