1

私は何日もの間、Rangy のすべての Q&A を調査しましたが、このケースには何も適応できませんでした。

私は次のコンテンツ編集可能を持っています

<div id="area" style="width:100%;height:2em;" 
contentEditable="true";
onkeyup="formatText();"
></div>

ユーザーが何かを入力するたびに、コンテンツを解析して特定のトークンをフォーマットする関数を呼び出します。

 function formatText() {

    var el = document.getElementById('area');
    var savedSel = saveSelection(el); // calls Rangy function

    var tokenColor;

        // removes html tags before passing the expression to the parser
    var userInput = document.getElementById('area').innerHTML.replace(/(<([^>]+)>)/g,"").replace(/&amp;/g, "").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/<span[^>]*>+<\/span>/, "");


    var i, newHTML=[];

    tokenType=[]; // [NUMBER,+,(,NUMBER,..]
    tokenArray=[]; // [3,+,(5,...]

    var resultOutput =  parse(userInput); // parser also fills tokenType and tokenArray

    for (i=0; i<tokenArray.length-1; i++){

        newHTML += "<span style='color: " + tokenColor + " '>" +  tokenArray[i] + "</span>";

         } // newHTML looks like <span style='color: red'>3</span><span style='color: black'>+</span> etc.


    el.innerHTML = newHTML; // replaces content of <div> with formatted text    

    restoreSelection(el, savedSel);  // calls Rangy function to restore cursor position
}

このフォーラムの他の投稿で著者が提示した次の Rangy ベースの関数を使用します。

    function saveSelection(containerEl) {
    var charIndex = 0, start = 0, end = 0, foundStart = false, stop = {};
    var sel = rangy.getSelection(), range;

    function traverseTextNodes(node, range) {
        if (node.nodeType == 3) {
            if (!foundStart && node == range.startContainer) {
                start = charIndex + range.startOffset;
                foundStart = true;
            }

            if (foundStart && node == range.endContainer) {
                end = charIndex + range.endOffset;
                throw stop;
            }
            charIndex += node.length;


        }  else {
            for (var i = 0, len = node.childNodes.length; i < len; ++i) {
                traverseTextNodes(node.childNodes[i], range);
            }   
        }
    }

    if (sel.rangeCount) {
        try {
            traverseTextNodes(containerEl, sel.getRangeAt(0));
        } catch (ex) {
            if (ex != stop) {
                throw ex;
            }
        }
    }

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



function restoreSelection(containerEl, savedSel) {
    var charIndex = 0, range = rangy.createRange(), foundStart = false, stop = {};
    range.collapseToPoint(containerEl, 0);

    function traverseTextNodes(node) {
        if (node.nodeType == 3) {
            var nextCharIndex = charIndex + node.length;
            if (!foundStart && savedSel.start >= charIndex && savedSel.start <= nextCharIndex) {
                range.setStart(node, savedSel.start - charIndex);
                foundStart = true;
            }
            if (foundStart && savedSel.end >= charIndex && savedSel.end <= nextCharIndex) {
                range.setEnd(node, savedSel.end - charIndex);
                throw stop;
            }
            charIndex = nextCharIndex;
        }   

        else {
            for (var i = 0, len = node.childNodes.length; i < len; ++i) {
                traverseTextNodes(node.childNodes[i]);
            }
        }
    }

    try {
        traverseTextNodes(containerEl);
    } catch (ex) {
        if (ex == stop) {
            rangy.getSelection().setSingleRange(range);   
        } else {
            throw ex;
        }
    }
}

キャラクターを削除しようとするまで、すべてが完全に機能します。その時点で、カーソルは div の先頭にジャンプします。

なぜこれが起こるのか、何か考えはありますか?

どうもありがとう。

4

2 に答える 2

2

私は Tim Down の助けを借りて問題を解決しました (Tim に感謝します!)。

彼は最近、Rangy に新しい TextRange モジュールを追加しました。これは完全に機能します。このモジュールには、ページに表示されているテキスト内の文字インデックスに基づく選択の保存/復元があるため、innerHTML の変更の影響を受けません。ここでデモを見つけることができます:

http://rangy.googlecode.com/svn/trunk/demos/textrange.html

ドキュメント (暫定版): http://code.google.com/p/rangy/wiki/TextRangeModule

したがって、基本的にコードは次のようになります。

    document.getElementById("area").onkeyup = function() {
    var sel = rangy.getSelection();
    var savedSel = sel.saveCharacterRanges(this);    

    var userInput = this.textContent || this.innerText;

    var userInputLength = userInput.length;  
    var newHTML = [];

    for (var i=0; i<userInputLength; i++) {
        newHTML[i] = "<span style='color: red'>" + userInput.charAt(i)  + "</span>";  
    }

    this.innerHTML =  newHTML.join("");

    sel.restoreCharacterRanges(this, savedSel);
};
​

お役に立てれば。

于 2012-06-07T11:33:19.417 に答える
0

それはうまくいくように私には見えます。解決策は、選択を復元する前focus()に contenteditable のメソッドを呼び出すのと同じくらい簡単です。<div>

于 2012-05-12T00:25:43.267 に答える