亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

中文輸入法與React文本輸入框的問(wèn)題與解決方案

shevy / 1479人閱讀

摘要:本文的目的是希望能針對(duì)這個(gè)問(wèn)題提供一些說(shuō)明現(xiàn)在暫時(shí)性的解決方案。完全不會(huì)有問(wèn)題的只有一個(gè)瀏覽器,就是上面注釋中所說(shuō)的已經(jīng)實(shí)作出的,上可能根本不需要任何解決方案,它的輸入法編輯器是獨(dú)立于瀏覽器上的文本輸入框之外的。

問(wèn)題來(lái)源是來(lái)自這個(gè)React官方存儲(chǔ)庫(kù)的issue #3926,與這個(gè)議題關(guān)聯(lián)的有很多其他的issue,來(lái)自許多項(xiàng)目,有些是與React相關(guān),有些則是vue或其它JS套件。也已經(jīng)有其他的項(xiàng)目是專注于解決這個(gè)問(wèn)題,例如react-composition,不過(guò)它是一個(gè)使用ES5語(yǔ)法的React組件。在其他的討論區(qū)上也有類似的問(wèn)題與解答。本文的目的是希望能針對(duì)這個(gè)問(wèn)題提供一些說(shuō)明、現(xiàn)在暫時(shí)性的解決方案。

下圖為目前解決React中"Controlled"(受控制的)input元件的演示,可以到這里去測(cè)試:

注意事項(xiàng): 目前的解決方案我認(rèn)為是暫時(shí)性的,結(jié)果都放在這個(gè)github庫(kù)上。這要分為"Controlled"(受控制的)與"Uncontrolled"(不受控制的)兩個(gè)種類的組件,影響的主要是input與textarea兩個(gè)組件,輸入法(IME, input method editor)的問(wèn)題,不只會(huì)發(fā)生在中文,同樣的在日文、韓文或其它使用輸入法的語(yǔ)言應(yīng)該都有同樣問(wèn)題。

問(wèn)題何來(lái)

React組件主要使用onChange人造事件,作為文本輸入框(input)或文字輸入?yún)^(qū)(textarea)觸發(fā)文字輸入時(shí)的事件,這個(gè)事件用起來(lái)很直覺,理應(yīng)當(dāng)是如此。但onChange在瀏覽器上,只要在這個(gè)文本輸入框上,有任何的鍵盤動(dòng)作它都會(huì)觸發(fā),也就是如果你是使用了中文、日文、韓文輸入法(IME),不論是哪一種,拼音的、筆劃的還是其他的,只要有按下一個(gè)鍵盤的動(dòng)作,就會(huì)觸發(fā)一次瀏覽器上這個(gè)元素的change事件,對(duì)于原本就使用鍵盤上的英文字符作為輸入的語(yǔ)言來(lái)說(shuō),這沒什么太大的問(wèn)題,但對(duì)于要使用輸入法的語(yǔ)言用戶來(lái)說(shuō),不停的觸發(fā)change事件,可能會(huì)造成程序功能上的運(yùn)行邏輯問(wèn)題。

舉出一個(gè)實(shí)際的應(yīng)用情況,一個(gè)使用React撰寫的搜索計(jì)算機(jī)書籍的功能,用戶可以在文本輸入框里輸入要搜索的書名,程序中是利用onChange事件觸發(fā),進(jìn)行比對(duì)數(shù)據(jù)庫(kù)中的書籍標(biāo)題,當(dāng)你想搜索一本名為"林哥的Java教程",第一個(gè)字為"林",拼音輸入法需要輸入"lin"三個(gè)鍵盤上的字符,在"林"這個(gè)字從輸入法編輯器中加到真正的input元素前,onChange已經(jīng)捕捉到"lin"三個(gè)字符,在列表中已搜索出一大堆有關(guān)"linux"的書籍。細(xì)節(jié)就不說(shuō)了,還有可能對(duì)字符數(shù)量的的檢查之類的問(wèn)題。不過(guò),這是正確的程序運(yùn)作邏輯嗎?很明顯的這是一個(gè)大問(wèn)題。

當(dāng)然,你也可以用對(duì)中文字詞檢查的修正方式,或是干脆不要用change事件,改用其他按鈕觸發(fā)之類的事件來(lái)作這事情,或是不要用React中的"Controlled"(受控制的)input或textare組件,但這會(huì)局限住在程序開發(fā)應(yīng)用上的自由,要如何選擇就看你自己了,是不要使用它還是想辦法正視問(wèn)題來(lái)解決它。

網(wǎng)頁(yè)上的DOM元素與"Uncontrolled"(不受控制的)的組件

這個(gè)問(wèn)題在瀏覽器中,早就已經(jīng)有了可應(yīng)對(duì)的解決方法,DOM事件中有一組額外的CompositionEvent(組成事件)可以輔助開發(fā)者,它可以在可編輯的DOM元素上觸發(fā),主要是input與textarea上,所以可以用來(lái)輔助解決change事件的輸入法問(wèn)題。CompositionEvent(組成事件)共有三個(gè)事件,分別為compositionstartcompositionupdatecompositionend,它們代表的是開始進(jìn)行字的組成、刷新與結(jié)束,也就是代表開始以輸入法編輯器來(lái)組合鍵盤上的英文字符,選字或刷新字的組合,到最后輸出字到真實(shí)DOM中的文本輸入框中,實(shí)務(wù)上每個(gè)中文字在輸入時(shí),compositionstartcompositionend都只會(huì)會(huì)被觸發(fā)一次,而compositionupdate則是有可能多次觸發(fā)。

藉由CompositionEvent的輔助來(lái)解決的方式,也就是說(shuō)在網(wǎng)頁(yè)上的input元素,可以利用CompositionEvent作為一個(gè)信號(hào),如果正在使用IME輸入中文時(shí),change事件中的代碼就先不要運(yùn)行,等compositionend觸發(fā)時(shí),接著的change事件才可以運(yùn)行其中的代碼,運(yùn)作的原理就是這樣簡(jiǎn)單而已。

在React應(yīng)用中,如果是一個(gè)"Uncontrolled"(不受控制的)的input組件,它與網(wǎng)頁(yè)上真實(shí)DOM中的input元素的事件行為無(wú)差異,也就是說(shuō),直接使用CompositionEvent的解決方式,就可以解決這個(gè)輸入法的問(wèn)題,以下面的代碼為例子:

// @flow
import React from "react"

const Cinput = (props: Object) => {
  // record if is on Composition
  let isOnComposition: boolean = false

  const handleComposition = (e: KeyboardEvent) => {
    if (e.type === "compositionend") {
      // composition is end
      isOnComposition = false
    } else {
      // in composition
      isOnComposition = true
    }
  }

  const handleChange = (e: KeyboardEvent) => {
    // only when onComposition===false to fire onChange
    if (e.target instanceof HTMLInputElement && !isOnComposition) {
      props.onChange(e)
    }
  }

  return (
    
  )
}

export default Cinput

上面這是一個(gè)典型的"Uncontrolled"(不受控制的)input組件,主要是它不用value這個(gè)屬性。但如果它有來(lái)自上層組件的value屬性與值,也就是上層組件用props傳遞給它value屬性的值,就成了"Controlled"(受控制的)組件,它的事件整個(gè)模式就會(huì)與網(wǎng)頁(yè)上的真實(shí)DOM中的input元素不一樣,這后面再說(shuō)明。

這個(gè)解決方案在幾乎所有能支持CompositionEvent的瀏覽器(IE9以上)都可以運(yùn)行得很好,不過(guò)在Google Chrome瀏覽器在2016年的版本53之后,更動(dòng)了changecompositionend的觸發(fā)順序,所以需要針對(duì)Chrome瀏覽器調(diào)整一下,如果是在Chrome瀏覽器中觸發(fā)compositionend時(shí),也要運(yùn)行一次在原本在change要運(yùn)行的代碼,就改成這樣而已。下面在上個(gè)代碼中的handleComposition函數(shù)中,多加了偵測(cè)是否為Chrome瀏覽器,與觸發(fā)原本的onChange方法代碼,修改過(guò)的代碼如下:

// detect it is Chrome browser?
const isChrome = !!window.chrome && !!window.chrome.webstore

const handleComposition = (e: KeyboardEvent) => {
  if (e.type === "compositionend") {
    // composition is end
    isOnComposition = false

    // fixed for Chrome v53+ and detect all Chrome
    // https://chromium.googlesource.com/chromium/src/
    // +/afce9d93e76f2ff81baaa088a4ea25f67d1a76b3%5E%21/
    if (e.target instanceof HTMLInputElement && !isOnComposition && isChrome) {
      // fire onChange
      props.onChange(e)
    }
  } else {
    // in composition
    isOnComposition = true
  }
}

"Uncontrolled"(不受控制的)input或textarea組件,解決方式就是這么簡(jiǎn)單而已,利用CompositionEvent過(guò)濾掉不必要的change事件。

注: 其它的解決方式還有,像InputEvent中有一個(gè)isComposing屬性,它也可以作為偵測(cè)目前是否正在進(jìn)行輸入法的組字工作,但I(xiàn)nputEvent事件目前只有Firefox中可以用,看起來(lái)沒什么前景。另外,W3C新提出的IME API或許是一個(gè)未來(lái)較佳的解決方案,但目前只有IE11 有實(shí)作,其他瀏覽器品牌都沒有。

"Controlled"(受控制的)的組件

在React應(yīng)用中,使用"Controlled"(受控制的)的input或textarea組件是另一回事,它會(huì)開始復(fù)雜起來(lái)。

"Controlled"(受控制的)的組件并不是只有加上value這個(gè)屬性這么簡(jiǎn)單,input或textarea組件所呈現(xiàn)的值,主要會(huì)來(lái)自state,state有可能是上層組件的,利用props一層層傳遞過(guò)來(lái)的,或是這個(gè)組件中本身就有的state,直接賦給在這個(gè)組件中的render中的input或textarea組件。也就是說(shuō),input最后呈現(xiàn)的文字如果要進(jìn)行改變,就需要改變到組件(不論在何處)的state,要改變state只有透過(guò)setState方法,而setState方法有可能是個(gè)異步(延時(shí))運(yùn)行的情況。

把這整個(gè)流程串接在一起后,我相信事件觸發(fā)的不連續(xù)情況會(huì)變得很嚴(yán)重,需要對(duì)不同情況下作測(cè)試與評(píng)估。目前我所作的測(cè)試還只是最基本的組件運(yùn)用而已,復(fù)雜的組件情況還沒有開始進(jìn)行。因?yàn)閟tate有很多種用途,有時(shí)候內(nèi)部使用,有時(shí)候要對(duì)外部用戶輸入介面的事件,或是有時(shí)候要對(duì)服務(wù)器端的數(shù)據(jù)接收或傳送,不論是不是要使用Redux、MobX或Flux之類的state容器函數(shù)庫(kù)或框架,最終要進(jìn)行重新渲染的工作,還是得調(diào)用React中的setState方法才行。

在基本的測(cè)試時(shí),我發(fā)現(xiàn)"Controlled"(受控制的)的input組件,它不僅事件觸發(fā)不連續(xù)的情況嚴(yán)重,而且有可能在不同瀏覽器上會(huì)有不同的結(jié)果。完全不會(huì)有問(wèn)題的只有一個(gè)瀏覽器,就是上面注釋中所說(shuō)的已經(jīng)實(shí)作出IME API的IE11,IE11上可能根本不需要任何解決方案,它的輸入法編輯器是獨(dú)立于瀏覽器上的文本輸入框之外的。

目前已測(cè)試的結(jié)果是有三種情況,"Chrome, Opera, IE, Edge"為一種,"Firefox"為一種,"Safari"為一種。我為這三種情況分別寫了不同的解決方式的代碼,但這個(gè)事件觸發(fā)的不連續(xù)情況,現(xiàn)在無(wú)法有一致性的解決方案,我只能推測(cè)這大概可能是React內(nèi)部設(shè)計(jì)的問(wèn)題。

不論是三種的那一種解決方案,有一個(gè)重點(diǎn)是你不能像上面的一般性解決方案,阻擋change事件時(shí)要運(yùn)行的代碼,也就是阻擋setState變動(dòng)state值,因?yàn)橹灰唤?jīng)阻擋,input組件的value值就賦不到值,而且也不會(huì)觸發(fā)重新渲染。所以你只能讓change事件不斷觸發(fā),就像往常一樣。

那么要如何解決程序邏輯運(yùn)作的問(wèn)題?

我使用了另一個(gè)內(nèi)部的state對(duì)象中的值,稱為innerValue,它是對(duì)比在input組件上不斷因觸發(fā)change事件而輸入的值,稱為inputValue。innerValue是個(gè)會(huì)經(jīng)過(guò)CompositionEvent修正過(guò)的值,所以它永遠(yuǎn)不會(huì)帶有在輸入法組字過(guò)程的字符串值。

這個(gè)解決方案,是一個(gè)"掛羊頭賣狗肉"的用法,不論用戶在input組件如何輸入,輸入的過(guò)程都會(huì)改變inputValue而已,inputValue是一個(gè)暫存與呈現(xiàn)用的值,最終用來(lái)進(jìn)行程序邏輯運(yùn)算的是innerValue。以最一開始的例子來(lái)說(shuō),用戶輸入"林哥的Java教程",在一開始的"林"字輸入時(shí),inputValue是從"lin"到輸入完成變?yōu)?林",而innerValue是在輸入期間是空字符串值,輸入完成才會(huì)變?yōu)?林"。所以,搜索功能可以用innerValue來(lái)作為運(yùn)算的依據(jù),用這個(gè)值來(lái)搜索對(duì)應(yīng)的數(shù)據(jù),這才是正確的運(yùn)算邏輯,因?yàn)?b>innerValue才是真正的不帶輸入法組字過(guò)程的值。

大致上說(shuō)明一下解決方式的代碼,首先它有兩個(gè)在這個(gè)模塊作用域中的全局變量,一個(gè)用來(lái)記錄是否在輸入法的組字過(guò)程中,另一個(gè)是給專給Safari瀏覽器用的:

// if now is in composition session
let isOnComposition = false

// for safari use only, innervalue can"t setState when compositionend occurred
let isInnerChangeFromOnChange = false

在專門處理change事件的handleChange方法中,判斷isInnerChangeFromOnChange這一段是專門為了解決Safari瀏覽器的問(wèn)題所寫,Safari瀏覽器的行為是CompositionEvent在觸發(fā)時(shí),其中的event.target.value居然是組字過(guò)程中的英文字符,而不是觸發(fā)這個(gè)事件的input元素的所有字符串,這也是特別怪異的地方,所以才會(huì)利用在compositionend后會(huì)再觸發(fā)一次change的特性,在這里刷新innerValue

后面的代碼,是代表在輸入法的組字過(guò)程中,setState方法使用的差異,在組字過(guò)程中(isOnComposition === true)的話,只會(huì)更動(dòng)inputValue值,而不會(huì)更動(dòng)到innerValue的值,這對(duì)應(yīng)了上述所說(shuō)的一個(gè)運(yùn)作過(guò)程,一般的輸入鍵盤上的字符時(shí)不會(huì)有輸入法的問(wèn)題,則是兩個(gè)值一并更動(dòng)。代碼如下:

handleChange = (e: Event) => {
   // console.log("change type ", e.type, ", target ", e.target, ", target.value ", e.target.value)

  // Flow check
  if (!(e.target instanceof HTMLInputElement)) return

  if (isInnerChangeFromOnChange) {
    this.setState({ inputValue: e.target.value, innerValue: e.target.value })
    isInnerChangeFromOnChange = false
    return
  }

  // when is on composition, change inputValue only
  // when not in composition change inputValue and innerValue both
  if (!isOnComposition) {
    this.setState({
      inputValue: e.target.value,
      innerValue: e.target.value,
    })
  } else {
    this.setState({ inputValue: e.target.value })
  }
}

在專門處理composition事件的handleComposition方法中,主要是為了在compositionend觸發(fā)時(shí),進(jìn)行刷新innerValue所撰寫的一些代碼。在第一種情況時(shí),也就是在Chrome, IE, Edge, Opera瀏覽器時(shí),只需要直接用e.target.value刷新innerValue即可。在第二種情況是Firefox,它不知道為什么會(huì)掉值,所以還需要幫它再一并刷新innerValue一次。第三種情況,上面有說(shuō)過(guò)了,特別的怪異情況,所以對(duì)innerValue的刷新改到compositionend之后的那個(gè)change事件去作了。代碼如下:

handleComposition = (e: Event) => {
   // console.log("type ", e.type, ", target ", e.target, ",target.value ", e.target.value, ", data", e.data)

   // Flow check
  if (!(e.target instanceof HTMLInputElement)) return

  if (e.type === "compositionend") {
    // Chrome is ok for only setState innerValue
    // Opera, IE and Edge is like Chrome
    if (isChrome || isIE || isEdge || isOpera) {
      this.setState({ innerValue: e.target.value })
    }

    // Firefox need to setState inputValue again...
    if (isFirefox) {
      this.setState({ innerValue: e.target.value, inputValue: e.target.value })
    }

    // Safari think e.target.value in composition event is keyboard char,
    //  but it will fire another change after compositionend
    if (isSafari) {
       // do change in the next change event
      isInnerChangeFromOnChange = true
    }

    isOnComposition = false
  } else {
    isOnComposition = true
  }
}

注: 目前這個(gè)暫時(shí)的解決方式,其方式并不是參考自react-composition項(xiàng)目,解決方式雖然有些類似,但react-composition用的是ES5的React工廠樣式組件語(yǔ)法,我對(duì)這種語(yǔ)法并不熟悉。在寫這篇文檔時(shí),才仔細(xì)看了一下react-composition的代碼,只能說(shuō)它的作者實(shí)際上也有測(cè)試過(guò)這個(gè)問(wèn)題,也知道只有用另一個(gè)state中的值才能解決這問(wèn)題。

總結(jié)

如果你是使用"Uncontrolled"(不受控制的)的組件,那么解決方法很簡(jiǎn)單,就如同上面所說(shuō)的,像一般的網(wǎng)頁(yè)上的DOM元素的解決方式即可。

但對(duì)于"Controlled"(受控制的)的組件來(lái)說(shuō),目前的解決方案是一種try-and-error(試誤法)的暫時(shí)性解決方案,我目前只能按照已測(cè)試的平臺(tái)與瀏覽器去修正,沒測(cè)過(guò)的瀏覽器與平臺(tái),就不得而知了。

關(guān)于這個(gè)"Controlled"(受控制的)的組件的事件觸發(fā),目前看到有在不同瀏覽器上的事件觸發(fā)不連續(xù)情況,我也有發(fā)一個(gè)議題(Issue)給React官方?;蛟S比較好的治本方案,是需要從state更動(dòng)方式的內(nèi)部代碼,或是人造事件觸發(fā)的順序,進(jìn)行一些調(diào)整,這超出我的能力范圍,就有待開發(fā)團(tuán)隊(duì)的回應(yīng)了。

最后,如果你正好有需要到這個(gè)功能,或是你認(rèn)為這個(gè)功能有需要,你可以幫忙測(cè)試看看或是提供一些建議。我已經(jīng)把所有的代碼、演示、線上測(cè)試、解決方案都集中到這個(gè)Github庫(kù)的react-compositionevent中?;蛟S你現(xiàn)在需要一個(gè)解決方案,你可以用里面目前的暫時(shí)性解決方式試試也可以。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/88070.html

相關(guān)文章

  • 學(xué)習(xí)React系列2-[解讀]Thinking in React

    摘要:擴(kuò)展單一職責(zé)原則又稱單一功能原則,面向?qū)ο笪鍌€(gè)基本原則之一。馬丁表示此原則是基于湯姆狄馬克和的著作中的內(nèi)聚性原則發(fā)展出的。 [解讀]Thinking in React 原文:http://facebook.github.io/react/docs/thinking-in-react.html 前言 Thought is the seed of action 這是放置在官方的QUICK ...

    tomorrowwu 評(píng)論0 收藏0
  • 翻譯 | 玩轉(zhuǎn) React 表單 —— 受控組件詳解

    摘要:受控輸入框只會(huì)顯示通過(guò)傳入的數(shù)據(jù)。例如,數(shù)組中的元素將會(huì)渲染三個(gè)單選框或復(fù)選框。屬性接收一個(gè)布爾值,用來(lái)表示組件是否應(yīng)該被渲染成選中狀態(tài)。 原文地址:React.js Forms: Controlled Components 原文作者:Loren Stewart 譯者:小 B0Y 校對(duì)者:珂珂君 本文涵蓋以下受控組件: 文本輸入框 數(shù)字輸入框 單選框 復(fù)選框 文本域 下拉...

    big_cat 評(píng)論0 收藏0
  • [ 一起學(xué)React系列 -- 5 ] 如何優(yōu)雅得使用表單控件

    摘要:假如我們從后臺(tái)拉取一個(gè)數(shù)據(jù)要填入輸入框,那么必須得使用受控組件,因?yàn)榉鞘芸亟M件只能被用戶輸入。不影響正常輸入填充該輸入框的默認(rèn)值,此時(shí)不顯示內(nèi)容。 網(wǎng)頁(yè)中使用的form表單大家肯定都再熟悉不過(guò)了,它主要作用是用來(lái)收集和提交信息。React中的表單組件與我們普通的Html中的表單及其表現(xiàn)形式?jīng)]有什么不同,所以如何使用表單我覺得再拿出來(lái)說(shuō)可能是畫蛇添足、毫無(wú)意義。不過(guò)再怎么樣也不能辜負(fù)大家...

    Charlie_Jade 評(píng)論0 收藏0
  • 基于 Vue 的移動(dòng)端富文本編輯器 vue-quill-editor 實(shí)戰(zhàn)

    摘要:優(yōu)秀的富文本編輯器有很多,比如,等,但并不是每個(gè)都能在移動(dòng)端有很好的表現(xiàn)。是百度的老牌富文本編輯器,但界面有一股上世紀(jì)的感覺,官網(wǎng)最新的一條動(dòng)態(tài)停留在。優(yōu)秀的富文本編輯器有很多,比如:UEditor,wangEditor 等,但并不是每個(gè)都能在移動(dòng)端有很好的表現(xiàn)。 我們暫且不討論移動(dòng)端是否真的需要富文本,既然有這需求,就把它實(shí)現(xiàn)出來(lái)。 失敗的嘗試 正確的選擇是成功的開始,開發(fā)之前肯定要做一些...

    wing324 評(píng)論0 收藏0
  • 用Canvas實(shí)現(xiàn)文本編輯器(支持藝術(shù)字渲染動(dòng)畫)

    摘要:項(xiàng)目中文字由進(jìn)行渲染。待觸發(fā)時(shí),取消中文輸入標(biāo)記,將文字渲染到上。而其中一些有趣的細(xì)節(jié)實(shí)現(xiàn)如文本渲染,對(duì)中文筆畫分割實(shí)現(xiàn)有趣的動(dòng)畫等并沒有描寫。 導(dǎo)言 目前富文本編輯器的實(shí)現(xiàn)主要有兩種技術(shù)方案:一個(gè)是利用contenteditable屬性直接對(duì)html元素進(jìn)行編輯,如draft.js;另一種是代理textarea + 自定義div + 模擬光標(biāo)實(shí)現(xiàn)。對(duì)于類似word的經(jīng)典富文本編輯器,...

    OldPanda 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<