10

contenteditable divに数字のハイライトを実装する必要があります(将来的にはより複雑なルールを追加します)。問題は、JavaScriptを置き換えて新しいコンテンツを挿入すると、DOMが変更され、contenteditabledivがフォーカスを失ったことです。私が必要としているのは、現在の位置に注意を払いながらdivに焦点を合わせ続けることです。これにより、ユーザーは問題なく入力でき、私の関数は数字を強調表示するだけです。グーグルで調べてみると、 Rangyライブラリが最善の解決策であると判断しました。私は次のコードを持っています:

function formatText() {
         
              var savedSel = rangy.saveSelection();
              el = document.getElementById('pad');
              el.innerHTML = el.innerHTML.replace(/(<([^>]+)>)/ig,"");
              el.innerHTML = el.innerHTML.replace(/([0-9])/ig,"<font color='red'>$1</font>");
              rangy.restoreSelection(savedSel);
          }

<div contenteditable="true" id="pad" onkeyup="formatText();"></div>

問題は、関数の終了後に作業の焦点がdivに戻った後ですが、注意は常にdivの開始を指し、divの開始を除いてどこにでも入力できます。また、次のconsole.logタイプRangy warning: Module SaveRestore: Marker element has been removed. Cannot restore selection. この機能の実装を手伝ってください。怒りっぽいライブラリだけでなく、別の解決策を開いています。ありがとう!

http://jsfiddle.net/2rTA5/これはjsfiddleですが、正しく機能しません(divに数値を入力しても何も起こりません)、おそらく私(jsfiddle経由で初めてコードを投稿する)またはリソースがcontenteditableをサポートしていません。UPD *スタックオーバーフローで同様の問題を読みましたが、解決策は私の場合には適していません:(

4

2 に答える 2

24

問題は、Rangyの保存/復元選択モジュールが、選択境界があるDOMに非表示のマーカー要素を挿入することによって機能し、コードがRangyのマーカー要素を含むすべてのHTMLタグを削除することです(エラーメッセージが示すように)。2つのオプションがあります。

  1. ではなく数値に色を付けるためのDOMトラバーサルソリューションに移動しますinnerHTML。これはより信頼性が高くなりますが、より複雑になります。
  2. 代替の文字インデックスベースの選択の保存と復元を実装します。これは一般的に壊れやすいでしょうが、この場合あなたが望むことをします。

アップデート

Rangy(上記のオプション2)の文字インデックスベースの選択の保存/復元をノックアップしました。少しラフですが、この場合はうまくいきます。これは、テキストノードをトラバースすることによって機能します。これをなんらかの形でRangyに追加するかもしれません。(2012年6月5日更新: Rangyのために、より信頼性の高い方法でこれを実装しました。

jsFiddle: http: //jsfiddle.net/2rTA5/2/

コード:

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;
        }
    }
}

function formatText() {
    var el = document.getElementById('pad');
    var savedSel = saveSelection(el);
    el.innerHTML = el.innerHTML.replace(/(<([^>]+)>)/ig,"");
    el.innerHTML = el.innerHTML.replace(/([0-9])/ig,"<font color='red'>$1</font>");

    // Restore the original selection
    restoreSelection(el, savedSel);
}
于 2011-04-08T14:36:05.327 に答える
0

ティムがここで私たちと共有してくれた機能に感謝したいと思います。それは私が取り組んでいるプロジェクトにとって非常に重要でした。私は彼の関数をここにアクセスできる小さなjQueryプラグインを埋め込みました:https ://jsfiddle.net/sh5tboL8/

$.fn.get_selection_start = function(){
    var result = this.get(0).selectionStart;
    if (typeof(result) == 'undefined') result = this.get_selection_range().selection_start;
    return result;
}

$.fn.get_selection_end = function(){
    var result = this.get(0).selectionEnd;
    if (typeof(result) == 'undefined') result = this.get_selection_range().selection_end;
    return result;
}

$.fn_get_selected_text = function(){
    var value = this.get(0).value;
    if (typeof(value) == 'undefined'){
        var result = this.get_selection_range().selected_text;
    }else{
        var result = value.substring(this.selectionStart, this.selectionEnd);
    }
    return result;
}

$.fn.get_selection_range = function(){

    var range = window.getSelection().getRangeAt(0);
    var cloned_range = range.cloneRange();
    cloned_range.selectNodeContents(this.get(0));
    cloned_range.setEnd(range.startContainer, range.startOffset);
    var selection_start = cloned_range.toString().length;
    var selected_text = range.toString();
    var selection_end = selection_start + selected_text.length;
    var result = {
        selection_start: selection_start,
        selection_end: selection_end,
        selected_text: selected_text
    }
    return result;
}

$.fn.set_selection = function(selection_start, selection_end){
    var target_element = this.get(0);
    selection_start = selection_start || 0;
    if (typeof(target_element.selectionStart) == 'undefined'){
        if (typeof(selection_end) == 'undefined') selection_end = target_element.innerHTML.length;

        var character_index = 0;
        var range = document.createRange();
        range.setStart(target_element, 0);
        range.collapse(true);
        var node_stack = [target_element];
        var node = null;
        var start_found = false;
        var stop = false;

        while (!stop && (node = node_stack.pop())) {
            if (node.nodeType == 3){
                var next_character_index = character_index + node.length;
                if (!start_found && selection_start >= character_index && selection_start <= next_character_index){
                    range.setStart(node, selection_start - character_index);
                    start_found = true;
                }

                if (start_found && selection_end >= character_index && selection_end <= next_character_index){
                    range.setEnd(node, selection_end - character_index);
                    stop = true;
                }
                character_index = next_character_index;
            }else{
                var child_counter = node.childNodes.length;
                while (child_counter --){
                    node_stack.push(node.childNodes[child_counter]);
                }
            }
        }

        var selection = window.getSelection();
        selection.removeAllRanges();
        selection.addRange(range);
    }else{
        if (typeof(selection_end) == 'undefined') selection_end = target_element.value.length;
        target_element.focus();
        target_element.selectionStart = selection_start;
        target_element.selectionEnd = selection_end;
    }
}

プラグインは、必要なことだけを実行し、選択されたテキストを取得し、カスタムテキスト選択を設定します。また、テキストボックスとcontentEditabledivでも機能します。

于 2017-06-27T20:28:00.683 に答える