簡體   English   中英

IE的document.selection.createRange不包括前導或尾隨空行

[英]IE's document.selection.createRange doesn't include leading or trailing blank lines

我正在嘗試從textarea中提取確切的選擇和光標位置。 像往常一樣,大多數瀏覽器中的簡單操作都不在IE中。

我正在使用這個:

var sel=document.selection.createRange();
var temp=sel.duplicate();
temp.moveToElementText(textarea);
temp.setEndPoint("EndToEnd", sel);
selectionEnd = temp.text.length;
selectionStart = selectionEnd - sel.text.length;

其中99%的時間都有效。 問題是TextRange.text不返回前導或尾隨換行符。 因此,當光標在段落之后是幾個空行時,它會在前一段的末尾產生一個位置 - 而不是實際的光標位置。

例如:

the quick brown fox|    <- above code thinks the cursor is here

|    <- when really it's here

我能想到的唯一解決方法是在選擇之前和之后臨時插入一個字符,抓取實際選擇,然后再次刪除那些臨時字符。 這是一個黑客,但在一個快速實驗看起來它會起作用。

但首先,我想確定沒有更簡單的方法。

我正在添加另一個答案,因為我之前的答案已經有點史詩了。

這是我認為最好的版本:它需要bobince的方法(在我的第一個答案的評論中提到)並修復了我不喜歡它的兩件事,首先它依賴於雜散在textarea之外的TextRanges (從而損害了性能),其次是為了移動范圍邊界而必須為字符數挑選一個巨大數字的骯臟。

function getSelection(el) {
    var start = 0, end = 0, normalizedValue, range,
        textInputRange, len, endRange;

    if (typeof el.selectionStart == "number" && typeof el.selectionEnd == "number") {
        start = el.selectionStart;
        end = el.selectionEnd;
    } else {
        range = document.selection.createRange();

        if (range && range.parentElement() == el) {
            len = el.value.length;
            normalizedValue = el.value.replace(/\r\n/g, "\n");

            // Create a working TextRange that lives only in the input
            textInputRange = el.createTextRange();
            textInputRange.moveToBookmark(range.getBookmark());

            // Check if the start and end of the selection are at the very end
            // of the input, since moveStart/moveEnd doesn't return what we want
            // in those cases
            endRange = el.createTextRange();
            endRange.collapse(false);

            if (textInputRange.compareEndPoints("StartToEnd", endRange) > -1) {
                start = end = len;
            } else {
                start = -textInputRange.moveStart("character", -len);
                start += normalizedValue.slice(0, start).split("\n").length - 1;

                if (textInputRange.compareEndPoints("EndToEnd", endRange) > -1) {
                    end = len;
                } else {
                    end = -textInputRange.moveEnd("character", -len);
                    end += normalizedValue.slice(0, end).split("\n").length - 1;
                }
            }
        }
    }

    return {
        start: start,
        end: end
    };
}

var el = document.getElementById("your_textarea");
var sel = getSelection(el);
alert(sel.start + ", " + sel.end);

一個jquery插件,用於在文本區域中獲取選擇索引的開始和結束。 上面的javascript代碼對IE7和IE8沒有用,並且給出了非常不一致的結果,所以我編寫了這個小的jquery插件。 允許臨時保存選擇的開始和結束索引,並在以后高亮顯示選擇。

這里有一個工作示例和簡要版本: http//jsfiddle.net/hYuzk/3/

有評論等的更詳細的版本在這里: http//jsfiddle.net/hYuzk/4/

        // Cross browser plugins to set or get selection/caret position in textarea, input fields etc for IE7,IE8,IE9, FF, Chrome, Safari etc 
        $.fn.extend({ 
            // Gets or sets a selection or caret position in textarea, input field etc. 
            // Usage Example: select text from index 2 to 5 --> $('#myTextArea').caretSelection({start: 2, end: 5}); 
            //                get selected text or caret position --> $('#myTextArea').caretSelection(); 
            //                if start and end positions are the same, caret position will be set instead o fmaking a selection 
            caretSelection : function(options) 
            { 
            if(options && !isNaN(options.start) && !isNaN(options.end)) 
            { 
            this.setCaretSelection(options); 
            } 
            else 
            { 
            return this.getCaretSelection(); 
            } 
            }, 
            setCaretSelection : function(options) 
            { 
            var inp = this[0]; 
            if(inp.createTextRange) 
            { 
            var selRange = inp.createTextRange(); 
            selRange.collapse(true); 
            selRange.moveStart('character', options.start); 
            selRange.moveEnd('character',options.end - options.start); 
            selRange.select(); 
            } 
            else if(inp.setSelectionRange) 
            { 
            inp.focus(); 
            inp.setSelectionRange(options.start, options.end); 
            } 
            }, 
            getCaretSelection: function() 
            { 
            var inp = this[0], start = 0, end = 0; 
            if(!isNaN(inp.selectionStart)) 
            { 
            start = inp.selectionStart; 
            end = inp.selectionEnd; 
            } 
            else if( inp.createTextRange ) 
            { 
            var inpTxtLen = inp.value.length, jqueryTxtLen = this.val().length; 
            var inpRange = inp.createTextRange(), collapsedRange = inp.createTextRange(); 

            inpRange.moveToBookmark(document.selection.createRange().getBookmark()); 
            collapsedRange.collapse(false); 

            start = inpRange.compareEndPoints('StartToEnd', collapsedRange) > -1 ? jqueryTxtLen : inpRange.moveStart('character', -inpTxtLen); 
            end = inpRange.compareEndPoints('EndToEnd', collapsedRange) > -1 ? jqueryTxtLen : inpRange.moveEnd('character', -inpTxtLen); 
            } 
            return {start: Math.abs(start), end: Math.abs(end)}; 

            }, 
            // Usage: $('#txtArea').replaceCaretSelection({start: startIndex, end: endIndex, text: 'text to replace with', insPos: 'before|after|select'}) 
            // Options     start: start index of the text to be replaced 
            //               end: end index of the text to be replaced 
            //              text: text to replace the selection with 
            //            insPos: indicates whether to place the caret 'before' or 'after' the replacement text, 'select' will select the replacement text 

            replaceCaretSelection: function(options) 
            { 
            var pos = this.caretSelection(); 
            this.val( this.val().substring(0,pos.start) + options.text + this.val().substring(pos.end) ); 
            if(options.insPos == 'before') 
            { 
            this.caretSelection({start: pos.start, end: pos.start}); 
            } 
            else if( options.insPos == 'after' ) 
            { 
            this.caretSelection({start: pos.start + options.text.length, end: pos.start + options.text.length}); 
            } 
            else if( options.insPos == 'select' ) 
            { 
            this.caretSelection({start: pos.start, end: pos.start + options.text.length}); 
            } 
            } 
        }); 

NB請參閱我的其他答案 ,以獲得我能提供的最佳解決方案。 我要留在這里作為背景。

我遇到過這個問題,並編寫了適用於所有情況的以下內容。 在IE中它確實使用您建議的方法在選擇邊界臨時插入一個字符,然后使用document.execCommand("undo")刪除插入的字符並防止插入保留在撤消堆棧上。 我很確定沒有更簡單的方法。 令人高興的是,IE 9將支持selectionStartselectionEnd屬性。

function getSelectionBoundary(el, isStart) {
    var property = isStart ? "selectionStart" : "selectionEnd";
    var originalValue, textInputRange, precedingRange, pos, bookmark;

    if (typeof el[property] == "number") {
        return el[property];
    } else if (document.selection && document.selection.createRange) {
        el.focus();
        var range = document.selection.createRange();

        if (range) {
            range.collapse(!!isStart);

            originalValue = el.value;
            textInputRange = el.createTextRange();
            precedingRange = textInputRange.duplicate();
            pos = 0;

            if (originalValue.indexOf("\r\n") > -1) {
                // Trickier case where input value contains line breaks

                // Insert a character in the text input range and use that as
                // a marker
                range.text = " ";
                bookmark = range.getBookmark();
                textInputRange.moveToBookmark(bookmark);
                precedingRange.setEndPoint("EndToStart", textInputRange);
                pos = precedingRange.text.length - 1;

                // Executing an undo command to delete the character inserted
                // prevents this method adding to the undo stack. This trick
                // came from a user called Trenda on MSDN:
                // http://msdn.microsoft.com/en-us/library/ms534676%28VS.85%29.aspx
                document.execCommand("undo");
            } else {
                // Easier case where input value contains no line breaks
                bookmark = range.getBookmark();
                textInputRange.moveToBookmark(bookmark);
                precedingRange.setEndPoint("EndToStart", textInputRange);
                pos = precedingRange.text.length;
            }
            return pos;
        }
    }
    return 0;
}

var el = document.getElementById("your_textarea");
var startPos = getSelectionBoundary(el, true);
var endPos = getSelectionBoundary(el, false);
alert(startPos + ", " + endPos);

UPDATE

基於bobince在評論中建議的方法,我創建了以下內容,它似乎運行良好。 一些說明:

  1. bobince的方法更簡單,更短。
  2. 我的方法是侵入性的:它會在恢復這些更改之前更改輸入的值,盡管這沒有明顯的效果。
  3. 我的方法的優點是可以將所有操作保持在輸入中。 bobince的方法依賴於創建從身體開始到當前選擇的范圍。
  4. 3.的結果是,bobince的性能隨着文檔中輸入的位置而變化,而我的則不然。 我的簡單測試表明,當輸入接近文檔的開頭時,bobince的方法明顯更快。 當輸入在大量HTML之后,我的方法更快。

function getSelection(el) {
    var start = 0, end = 0, normalizedValue, textInputRange, elStart;
    var range = document.selection.createRange();
    var bigNum = -1e8;

    if (range && range.parentElement() == el) {
        normalizedValue = el.value.replace(/\r\n/g, "\n");

        start = -range.moveStart("character", bigNum);
        end = -range.moveEnd("character", bigNum);

        textInputRange = el.createTextRange();
        range.moveToBookmark(textInputRange.getBookmark());
        elStart = range.moveStart("character", bigNum);

        // Adjust the position to be relative to the start of the input
        start += elStart;
        end += elStart;

        // Correct for line breaks so that offsets are relative to the
        // actual value of the input
        start += normalizedValue.slice(0, start).split("\n").length - 1;
        end += normalizedValue.slice(0, end).split("\n").length - 1;
    }
    return {
        start: start,
        end: end
    };
}

var el = document.getElementById("your_textarea");
var sel = getSelection(el);
alert(sel.start + ", " + sel.end);

負數百萬美元的舉動似乎完美無缺。

這是我最終得到的:

var sel=document.selection.createRange();
var temp=sel.duplicate();
temp.moveToElementText(textarea);
var basepos=-temp.moveStart('character', -10000000);

this.m_selectionStart = -sel.moveStart('character', -10000000)-basepos;
this.m_selectionEnd = -sel.moveEnd('character', -10000000)-basepos;
this.m_text=textarea.value.replace(/\r\n/gm,"\n");

謝謝bobince - 當它只是一個評論時,我怎么能投票給你答案:(

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM