获得输入框光标位置

http://yiminghe.iteye.com/blog/508999

搜狗发布了很有意思的javascript输入法 : 搜狗云输入法
基本原理即是:将一段js代码当作书签,当选择时执行,执行时动态插入一个script文件,来监控页面所有框并根据输入拼音异步请求服务器获得中文文字。
目前来看,尚存在一点问题:
1。':' 冒号mac下输不出来,看来是组合键处理上存问题
2。没有处理选择一段文本输入后替换原文字,目前是仍然插入输入区域后面
 
光标问题 :
 
其中重要一步,就是识别当前输入框的当前光标位置,并劫持用户输入,再选择汉字后将汉字插入当前输入位置。
而获取光标位置对于 IE 来说可是相当变态,缘于其复杂的 textrange 特性,而其他浏览器则实现了html5的相关属性,处理很简洁。
 

 
 
 
的评论中,Strifer 提出了一个比较好的思路,我对其封装修改了一下 :
 
获得输入框光标位置 _光标
 
 
核心在于对 ie textarea 编辑方面的封转 ,使得其和标准浏览器表现一致。

PS : 注意这里的选择区域在规范中和页面的选择区域 并不同,参见规范:
 
Mostly for historical reasons, in addition to the browsing context 's selection , each textarea and input element has an independent selection. These are the text field selections.
 
PS2 : 由于 ie 中选择区域只有一块,那么当 textarea 失去焦点时,下次再 focus textarea,在 ie 下光标每次都会跑到前面了,记不住曾经textare的选择区域了,而由于规范区分 textarea 与 input 的选择区域,则没这个问题,在 iframe 下也有类似问题。解决方案为每次 textarea 内的点击都要记录选择区域,当 textarea focusin 时立即恢复:
 
Js代码 复制代码 收藏代码
  1. if (IE) {   
  2.             var savedRange;   
  3.             textarea.   
  4.                 = textarea.   
  5.                 = textarea.onkeydown   
  6.                 = textarea.onkeyup = function() {   
  7.                 savedRange = document.selection.createRange();   
  8.             };   
  9.             textarea.onfocusin = function() {   
  10.                 savedRange && savedRange.select();   
  11.             };   
  12.         }  
 
PS3 : moveStart 注意
 
在 ie 下 textarea 内容中的换行表示为 "\r\n" 长度为 2 ,而 range moveStart 当参数为 "character" 每次移动并指定数值为 1 时,会一次性掠过 \r\n,而会导致获得位置与实际的 textarea.value 字符串中位置不符。另外 range 的某一端(开始或结束)一定不会停留在\r与\n之间,于是当遇到计数光标位置的前方是 "\n",可加一跳过:
 
 
Js代码 复制代码 收藏代码
  1. for (var sel_end = 0;   
  2.                  (flag = range_all.compareEndPoints('StartToEnd', range)) < 0;   
  3.                  sel_end++) {   
  4.   
  5.                 if (textarea.value.charAt(sel_end) == '\n') {   
  6.                     sel_end++;   
  7.                 }   
  8.                 range_all.moveStart('character', 1);   
  9.             }   
  10.             //光标不可能停在\r,\n之间   
  11.             if (textarea.value.charAt(sel_end) == '\n') {   
  12.                 sel_end++;   
  13.             }  
 
 
如图所示:
 
获得输入框光标位置 _光标_08
 
如果当前选择为 x\r\n ,则当实际计数位置从 1 经过 ++ 循环到 2 时,range 位置也经过 move 跳过 2 进入 3 ,导致 if 失败,从而计数位置到达 2 ,而实际应返回 3 ,这时可经过判断 2 位置的下一个字符为 “\n” 而执行 ++。
 
 
应用示例
×××号输入即时验证,除了 keydown 外的 keyup 监控解决方案,利用 keyup 监控键入字符,前17位必须为数字,第18位可以是数字或x,X。
 

问题:

keyup 时木已成舟,无法阻止,若用户输错只能通过重新设置 input 的值,但是会使得光标随改动移至末尾,而不是保留在输入前的中间位置。
 

解决:

通过设置前保存光标位置,设置后重新restore光标位置,注意 ie 和标准浏览器的差别
 
 

demo: