在任何编辑器中,获取光标位置都是非常重要的,很多人可能认为较难,其实只要处理好浏览器的兼容,还是比较容易实现的。

下面我们一起来看看如何获取到 Textarea 元素中的光标位置(测试地址)。

首先,我们用 rangeData 对象作为数据存储,并获得焦点:

var rangeData = {start: 0, end: 0, text: "" };
textarea.focus();

对于非 IE 浏览器获取选区的起始和末尾位置其实非常容易:

rangeData.start= el.selectionStart;
rangeData.end = el.selectionEnd;

通过截取我们可以得到光标的选区内容:

rangeData.text = (rangeData.start != rangeData.end) ? el.value.substring(rangeData.start, rangeData.end): "";

而对于 IE 浏览器处理起来就比较麻烦了,但我们依旧可以获取到选区:

oS = document.selection.createRange();

同时还可获取 Textarea 元素的选区:

// 为了使 oR 与 oS 在同一等级上比较,请勿使用:oR = textarea.createTextRange()
oR = document.body.createTextRange();
oR.moveToElementText(textarea);

如果光标在 Textarea 元素内,很自然 oS.text 就是我们需要的选区内容:

rangeData.text = oS.text;

并且我们可以通过 oS.getBookmark() 方法获取到选区的位置数据,该位置数据可以通过 moveToBookmark() 方法设置回去。

getBookmark: Retrieves a bookmark (opaque string) that can be used with moveToBookmark to return to the same range.

moveToBookmark: Moves to a bookmark.

我们用 rangeData.bookmark 来记录该位置数据:

rangeData.bookmark = oS.getBookmark();

下面是最重要的步骤:我们比较 oR 与 oS 的选区起始位置(使用 object.compareEndPoints(sType, oRange) 方法比较),如果 oR 的起始位置在 oS 之前,我们向前移动 oS 的起始位置1个字符(使用 object.moveStart(sUnit [, iCount]) 方法移动),一直当 oS 的起始位置在 oR 之前停止,移动的位置,则是选区的起始位置。

compareEndPoints: Compares an end point of a TextRange object with an end point of another range.

moveStart: Changes the start position of the range.

for (i = 0; oR.compareEndPoints('StartToStart', oS) < 0 && oS.moveStart("character", -1) !== 0; i ++) {}
rangeData.start = i;

但由于在 IE 中,Textarea 元素中的所有换行符都占 1 个字符,可以通过 alert(textarea.value.length) 查看,故要对上面的代码做部分处理:

for (i = 0; oR.compareEndPoints('StartToStart', oS) < 0 && oS.moveStart("character", -1) !== 0; i ++) {
    // Why? You can alert(textarea.value.length)
    if (textarea.value.charAt(i) == '\n') {
        i ++;
    }
}
rangeData.start = i;

既然得到了选区的起始位置和选区字符串的字符,很自然我们可以计算得到选区的末尾位置:

rangeData.end = rangeData.text.length + rangeData.start;

获取 Textarea 的光标位置的 getCursorPosition 函数方法整理如下:

/**
* getCursorPosition Method
*
* Created by Blank Zheng on 2010/11/12.
* Copyright (c) 2010 PlanABC.net. All rights reserved.
* 
* The copyrights embodied in the content of this file are licensed under the BSD (revised) open source license.
*/
function getCursorPosition(textarea) {
    var rangeData = {text: "", start: 0, end: 0 };
	textarea.focus();
    if (textarea.setSelectionRange) { // W3C
        rangeData.start= textarea.selectionStart;
        rangeData.end = textarea.selectionEnd;
        rangeData.text = (rangeData.start != rangeData.end) ? textarea.value.substring(rangeData.start, rangeData.end): "";
    } else if (document.selection) { // IE
        var i,
            oS = document.selection.createRange(),
            // Don't: oR = textarea.createTextRange()
            oR = document.body.createTextRange();
        oR.moveToElementText(textarea);
      
        rangeData.text = oS.text;
        rangeData.bookmark = oS.getBookmark();
        
        // object.moveStart(sUnit [, iCount]) 
        // Return Value: Integer that returns the number of units moved.
        for (i = 0; oR.compareEndPoints('StartToStart', oS) < 0 && oS.moveStart("character", -1) !== 0; i ++) {
            // Why? You can alert(textarea.value.length)
            if (textarea.value.charAt(i) == '\n') {
                i ++;
            }
        }
        rangeData.start = i;
        rangeData.end = rangeData.text.length + rangeData.start;
    }
	
    return rangeData;
}

得到 Textarea 元素光标位置,当Textarea 中的光标丢失了,再设置回来就简单多了:

/**
* setCursorPosition Method
*
* Created by Blank Zheng on 2010/11/12.
* Copyright (c) 2010 PlanABC.net. All rights reserved.
* 
* The copyrights embodied in the content of this file are licensed under the BSD (revised) open source license.
*/
function setCursorPosition(textarea, rangeData) {
    if(!rangeData) {
        alert("You must get cursor position first.")
    }
    if (textarea.setSelectionRange) { // W3C
        textarea.focus();
        textarea.setSelectionRange(rangeData.start, rangeData.end);
    } else if (textarea.createTextRange) { // IE
        var oR = textarea.createTextRange();
        // Fixbug :
        // In IE, if cursor position at the end of textarea, the setCursorPosition function don't work
        if(textarea.value.length === rangeData.start) {
            oR.collapse(false)
            oR.select();
        } else {
            oR.moveToBookmark(rangeData.bookmark);
            oR.select();    
        }
    }
}

测试地址:http://www.planabc.net/demo/range/textarea-cursor-position.html

扩展阅读:



共有16 条评论

  1. 1. 头像 52web.com

    代码还是有点问题,我在demo中在光标后插入字符,光标显示是移动到了插入的字符后,但再次插入字符却又是插入在之前的位置。

  2. 2. 头像 cssrain

    http://www.cssrain.cn/demo/cursor.html
    之前也搞过。 IE下 input 和 Textarea 有点区别。

  3. 3. 头像 longbill

    关于在光标位置插入字符,IE的方法比w3c方法插入效率很高,不用读取和操作整个字符串最后再写回去。 这种由效率引起的延迟在textarea文本达到上千行的时候很明显。 不知道怎么解决。。。。

  4. 4. 头像 怿飞

    @52web.com 所有的插入、设置,都先要用 getCursorPosition 得到光标位置,步骤是分开的

  5. 5. 头像 怿飞

    @cssrain 我看了一下你的Demo,input IE 主要的处理代码是:
    var Sel = document.selection.createRange();
    Sel.moveStart('character', -ctrl.value.length);
    CaretPos = Sel.text.length;

    很巧的思路,不过当input中选中的是一段文字时,此方法就出错了。

  6. 6. 头像 song

    在ie下获得textarea里鼠标光标的位置应该有更好的实现:
    function getCursorPosition ( ctrl ) {
    var CaretPos = 0;

    if ( document.selection ) {
    ctrl.focus ();
    var range = document.selection.createRange(),
    dupRange = range.duplicate();

    dupRange.moveToElementText( ctrl );
    dupRange.setEndPoint( “EndToEnd”, range );

    CaretPos = dupRange.text.length – range.text.length;

    } else if (ctrl.selectionStart || ctrl.selectionStart == ‘0’) {
    CaretPos = ctrl.selectionStart;
    }

    return CaretPos;
    }

  7. 7. 头像 laoshu133

    经测试,发现在chrome 下测试 在光标后插入文本 不正常

  8. 8. 头像 怿飞

    chrome 下我测试可以的,方法是分开处理的,先获得光标位置,再执行插入操作,呵呵

  9. 9. 头像 Ray

    OK 受教了 一直对这一块迷迷糊糊的。。。

  10. 10. 头像 ecigs

    chrome 下测试时正常的

  11. 11. 头像 Yvon

    自己测试了下,用来在聊天窗口中插入图片,chrome和safari都是可以的

  12. 12. 头像 realfex

    十分感谢!
    正好项目里要用,困扰多时,终于解决了!

  13. 13. 头像 gary

    请问楼主,如果我想在一个可编辑的div(即其contentEditable设为true后)中,想获取其光标的位置,应该如何做呢,盼能回复,谢谢!

  14. 14. textarea光标定位问题

    […] 获取 Textarea 的光标位置 This entry was posted in 前端. Bookmark the permalink. Post a comment or leave a trackback: Trackback URL. « CSS filter滤镜详解 […]

  15. 15. 头像 Sam

    谢谢!受益良多!

  16. 16. 请问在可编辑div中,如何获取光标位置 - web标准 - 开发者问答

    […] Textarea 的光标位置 http://www.planabc.net/2010/11/17/get_textarea_cursor_position/ 本条目发布于 2011 年 8 月 31 日。属于 web前端 分类,被贴了 web标准 […]

发表评论

(必填)

(必填,会为您保密)

评论仅支持“a、abbr、strong、em、blockquote、code”几个简单的标签