ie光标问题

光标问题 :

 

其中重要一步,就是识别当前输入框的当前光标位置,并劫持用户输入,再选择汉字后将汉字插入当前输入位置。

而获取光标位置对于 IE 来说可是相当变态,缘于其复杂的 textrange 特性,而其他浏览器则实现了html5的相关属性,处理很简洁。

 

html5  selectionStart


ie textrange

 

 

TextArea Cursor Position with JavaScript

 

的评论中,Strifer 提出了一个比较好的思路,我对其封装修改了一下 :

 

 

演示@google code

 

核心在于对 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.onmousedown  
  4.                 = textarea.onmouseup  
  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.             }  
 

 

如图所示:

 

 

如果当前选择为 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 和标准浏览器的差别

 

 

代码:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>

<title>
    身份验证
</title>

<meta content="yiminghe@gmail.com" name="author"/>

<script src="../../base/javascript/kissy.js"></script>

</head>
<body>

<h1>身份证号输入即时验证</h1>

<h2>
    除了 keydown 外的 keyup 监控解决方案
</h2>
<h2>
    利用 keyup 监控键入字符,前17位必须为数字,第18位可以是数字或x,X
</h2>
<dl>
    <dt>问题:</dt>
    <dd>
        keyup 时木已成舟,无法阻止,若用户输错只能通过重新设置 input 的值,但是会使得光标随改动移至末尾,而不是保留在输入前的中间位置。
    </dd>
    <dt>解决:</dt>
    <dd>
        通过设置前保存光标位置,设置后重新restore光标位置,注意 ie 和标准浏览器的差别
    </dd>
</dl>
</p>
<label>身份证号:<input id='idd' maxlength="18"/></label>

<script type="text/javascript">

    KISSY.ready(function(S) {
        var idd = S.one("#idd");
        idd.on("keyup", function() {
            var pos1,error = false;
            var range1,range2;
            var o = this[0];
            if (!o.value.match(/^(([0-9]{0,18})|([0-9]{17,17}x))$/i)) error = true;
            //出错处理光标保存逻辑
            if (error) {
                //设置前保存光标位置
                if (!o.selectionStart) {
                    range1 = document.selection.createRange().duplicate();
                    range1.moveEnd('character', o.value.length);
                    pos1 = o.value.lastIndexOf(range1.text);
                } else {
                    pos1 = o.selectionStart;
                }

                //设置新值
                if (o.value.length < 18) {
                    o.value = o.value.replace(/[^0-9]/g, '');
                }
                else if (o.value.length == 18) {
                    o.value = o.value.replace(/[^0-9xX]/g, '');
                }

                //恢复光标位置
                if (!o.selectionStart) {
                    range2 = o.createTextRange();
                    range2.collapse(true);
                    range2.move('character', pos1 - 1);
                    range2.select();
                } else {
                    o.selectionStart = pos1 - 1;
                    o.selectionEnd = pos1 - 1;
                }
            }
        });
    });

</script>

</body>
</html>

 

  • 大小: 14.3 KB
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值