`
yiminghe
  • 浏览: 1432749 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Get cursor position and coordinates from textarea

阅读更多

最近需要从 textarea 中获取当前输入的光标位置以及绝对坐标,希望达到 github 以及 weibo 输入自动提示的效果,例如:

 

如今网络上都是零散的技巧(包括曾今的一篇),没有完善可靠的解决方案,于是就直接把这些功能实现兼容到 KISSY 的 DOM 操作中。

 

 

兼容正规化

 

cursor position

 

光标位置(cursor position) w3c 已经提供了简洁的 api ,selectionStart 与 selectionEnd 两个读写属性,那么对于 ie 只需要利用其私有的 range api 重新实现这两个属性的读写操作即可。

 

注意点

 

1. textarea 的换行 ie 中是两个字符 "\r\n"

 

2. range move 的单位是 character,而一次move又会越过 "\r\n" 两个字符。

 

3. 当光标位于 "\r\n" 后时,range.text 属性不包括最后的两个换行字符.

 

4. 当移动光标开始位置到结束位置后面时需要将光标的结束位置设为和光标开始位置相同

    当移动光标结束位置到开始位置前面时需要将光标的开始位置设为和光标结束位置相同

 

最终即可统一到 w3c 的读写属性 api:

 

 

textarea.prop("selectionStart") // => 获取光标开始位置
textarea.prop("selectionEnd") // => 获取光标结束位置
textarea.prop("selectionStart",value) // => 设置光标开始位置
textarea.prop("selectionEnd",value) // => 获取光标结束位置
 

 

cursor coordinates

 

 ie 可通过 boundingLeft/Top 加上滚动距离直接获得当前光标的绝对坐标,而对于标准浏览器则需要

 

1. 通过构建 fake div 使得其具备和 textarea 一样的 fontSize/line-height/fontWeight 等影响文字大小的属性以及同 textarea 一样的 width/height 盒子大小属性

 

2. 然后设置 fake div 的内容为 textarea 当前光标以前的所有内容,并在最后位置插入 marker (span) 元素

 

3. 最终取得 marker 的绝对坐标即是当前光标的绝对坐标。

 

注意点

 

1.  textarea 浏览器一般都默认有 padding 和 margin,需要同时取过来,否则会造成误差。

 

2.  fake div 需要设置样式 word-wrap:break-word 以及 white-space:pre-wrap, 达到当输入连续字母时和 textarea 一样强制换行的效果.

 

其中 white-space:pre-wrap 是 chrome 的默认样式,而 firefox 虽然默认为 normal,但是普通 div 要想达到和 textarea 一样的输入换行,仍需要强制设置为 pre-wrap

 

 

 

3.  最诡异的一点: textarea 的空白需要转换为 <span style="white-space:pre-wrap;"> </span>


    如果只是简单的 &nbsp; , 因为 chrome  textarea 连续输入的空格不会导致 textarea 换行,转换为 &nbsp; 后会导致 fake div 换行,造成后续光标位置误差.


    而如果转换成  <span style="white-space:pre-wrap;"> </span> 则不会有该问题,查询 white-space 规范后原因仍不明

 

3. textarea 的内容需要经过 escapeHTML 后填入到 fake div 中,防止用户输入 < > 等 html 特殊标记。

 

4.  需要注意 textarea 的滚动,textarea 会随着用户的输入进而产生 scroll,这时需要在取得 marker 的绝对坐标后减去 scroll 的距离。

 

5. marker 需要有文字内容,否则空元素的 marker 在换行后得不到正确坐标,也是为了下面的注意点 6

 

6. ie boundingTop 取得的是光标下边界坐标,marker 取的则是光标上边界距离,这时就需要减去 marker 自身的高度(因此要求 marker 不为空元素,需要有文字)

 

known issue

 

即使考虑了上述所有注意点,在特定输入下,仍然会有位置误差,例如:

 

chrome:

 

 

firefox:

 

 

注:上为原始 textarea ,下为模拟的 div

 

测试地址

 

 

updated : 将原始 textarea 设置为 margin:0; padding:0; 后 firefox 完全一致,但 chrome 仍有少许误差。

 

updated : 将 fake div 设置为 pre-wrap ,则位置完全没有误差了!

api

 

最后此功能也可以封装到私有属性中,模仿浏览器加个 Ks 前缀即可

 

 

 

textarea.prop("KsCursorCoordinates") // => {left:xx,top:yy}

 

demo

 

get cursor position and coordinates

 

 

Code

 

code @ KISSY

 

 

 

 

  • 大小: 7.2 KB
  • 大小: 2.3 KB
  • 大小: 2.7 KB
  • 大小: 9.1 KB
分享到:
评论
3 楼 yiminghe 2012-11-09  
你好,能否转移到

https://github.com/kissyteam/kissy/issues?state=open

具体讨论,如果能提供 pull request 就更好了

hunter3721 写道
@yiminghe:
根据demo,我认为这个获取光标位置的接口,预期是:
  A.获取该光标,相对textarea所在的frame (0,0)点的坐标
而不是:
  B.获取该光标,相对最顶级frame (0,0)点的坐标

但实际测试发现:
  1.firefox16,这个接口是符合预期A的。(内部路径走的是fakeDiv的分支)
  2.IE8,使用兼容模式,也是符合预期A的。(内部路径走的是doc.selection的分支)
  3.IE8,使用IE7模式或IE7兼容模式,就不符合预期了。变成了B.
具体可见这个地址的测试:
  http://jsfiddle.net/hunter3721/3LQyS/3/embedded/result/

此外,请关注一下另外1个bug:
  firefox16下(其他版本未测试),宽度为297时,textarea一行的字符数和fakediv不同,导致偏移。
2 楼 hunter3721 2012-11-08  
@yiminghe:
根据demo,我认为这个获取光标位置的接口,预期是:
  A.获取该光标,相对textarea所在的frame (0,0)点的坐标
而不是:
  B.获取该光标,相对最顶级frame (0,0)点的坐标

但实际测试发现:
  1.firefox16,这个接口是符合预期A的。(内部路径走的是fakeDiv的分支)
  2.IE8,使用兼容模式,也是符合预期A的。(内部路径走的是doc.selection的分支)
  3.IE8,使用IE7模式或IE7兼容模式,就不符合预期了。变成了B.
具体可见这个地址的测试:
  http://jsfiddle.net/hunter3721/3LQyS/3/embedded/result/

此外,请关注一下另外1个bug:
  firefox16下(其他版本未测试),宽度为297时,textarea一行的字符数和fakediv不同,导致偏移。
1 楼 十三‘’ 2012-04-25  
学习

相关推荐

Global site tag (gtag.js) - Google Analytics