11

タイトルは少し誤解を招くかもしれません。私のスペルチェッカーは、スペルよりもフォーマットに重点を置いています(大文字、句読点、スペース、アポストロフィ、インターネットスラングの完全な単語への変換、頻繁にスクランブルされた単語など)。ただし、基本原則が適用されます。

基本的に、私が構築しているJS / jQueryチェッカーは、入力時に単語を修正します(単語の後にスペースまたは句読点が入力された後)。

ただし、他の自動修正と同様に、間違いが発生する可能性があります。特定の場合に「その」または「それ」がより適切であるかどうかを判断する機能を作成することも検討していません(ただし、そのようなプラグインまたはコードスニペットが存在する場合は、1つを指定してください)。

だから私はそれを「譲歩する」オートコレクトにしたい(より良い名前の知識がないため)。基本的に;

  1. ユーザーは、チェッカーを引き立たせる単語を入力し、スペースを入力します。
  2. チェッカーが単語を修正します。
  3. ユーザーはこれを間違いと見なし、修正し直します(単語全体または一部をバックスペースするか、強調表示するか、編集しても問題ありません)。
  4. ユーザーは入力を続け、チェッカーはその単語のそのインスタンスに再び触れることはありません。

もちろん、その単語のチェックを完全に無効にするのが最も簡単ですが、チェッカーにその単語の将来のインスタンスを修正してもらいたいと思います。私が探しているのは、ユーザーが自動修正された単語を(入力直後か後でかに関わらず)自動修正前の状態に編集し、その単語の特定のインスタンスをそのままにしておくことを学習するユーザーを検出することです。

どこから始めればいいのかわからない。各単語をスパンでラップしたコンテンツ編集可能、特別なクラスとデータを含む自動修正された単語-*元の単語を含む属性、自動修正された単語の編集をリッスンし、データと同じになるように編集された場合-*値、将来のオートコレクトラウンドから除外するクラスを追加します。

これは不必要に複雑かもしれないし、少なくとも抵抗が最も少ない道ではないかもしれないと私は思っています。これを行う最も賢い方法は何でしょうか?

4

2 に答える 2

7

span一見したところ、提案されたアプローチ (各単語を a に区切り、追加のデータを格納する) は、最も賢明なアプローチのようです。エディター レベルでは、すべてのテキストが some 内spanにあり、それぞれに 1 つの単語のみが含まれていることを確認する必要があります (必要に応じて分割します)。span単語レベルでは、 s (バインディングinputと) の変更をリッスンし、propertyChangeそのクラス/データに従って動作します。

ただし、本当の問題は、キャレットの位置を一定に保つことです。textareaaまたは要素の内容を で変更するとcontentEditable、キャレットが予想外に動き、キャレットを追跡する簡単な (クロスブラウザー) 方法がありません。ここ SO と他の場所の両方で解決策を探しましたが、見つけた最も簡単な解決策はこのブログ投稿でした。残念ながら、それは にしか適用されなかっtextareaたため、「スパン内の各単語」ソリューションは使用できませんでした。

そこで、次のアプローチを提案します。

  • Array各単語が現在の値と元の値の両方を格納する に単語のリストを保持します。
  • 変更の内容がtextarea変更された場合、変更されていない単語のセットを保持し、残りをやり直します。
  • キャレットが単語以外の文字の直後にあり (改善の余地)、ヒットしていない場合にのみスペル チェックを適用しますbackspace
  • ユーザーが修正に満足していない場合は、backspace一度押すと元に戻り、変更されない限り再度チェックされることはありません。
    • 一度に多くの修正を行った場合 (たとえば、大量のテキストをコピーして貼り付けた場合)、backspace誰もいなくなるまで、それぞれが 1 つの修正を元に戻します。
    • 他のキーを押すと修正がコミットされるため、ユーザーがまだ満足していない場合は、戻ってもう一度変更する必要があります。
    • 注: OP 要件とは異なり、ユーザーが単語以外の文字を入力すると、変更されたバージョン再度自動修正されます。彼はbackspaceそれを「保護」するために一度攻撃する必要があります。

jsFiddleで簡単な概念実証を作成しました。詳細は以下。他のアプローチと組み合わせることができることに注意してください(たとえば、「下矢印」キーを検出し、いくつかの自動修正オプションを含むメニューを表示します)。


詳細に説明された概念実証の手順:

  • Array各単語が現在の値と元の値の両方を格納する に単語のリストを保持します。

    var words = [];
    

    この正規表現は、テキストを単語に分割します (各単語にはwordプロパティとプロパティがありspます。後者は、直後に単語以外の文字を格納します)。

    delimiter:/^(\w+)(\W+)(.*)$/,
    ...
    regexSplit:function(regex,text) {
        var ret = [];
        for ( var match = regex.exec(text) ; match ; match = regex.exec(text) ) {
            ret.push({
                word:match[1],
                sp:match[2],
                length:match[1].length + match[2].length
            });
            text = match[3];
        }
        if ( text )
            ret.push({word:text, sp:'', length:text.length});
         return ret;
    }
    
  • 変更の内容がtextarea変更された場合、変更されていない単語のセットを保持し、残りをやり直します。

        // Split all the text
        var split = $.autocorrect.regexSplit(options.delimiter, $this.val());
        // Find unchanged words in the beginning of the field
        var start = 0;
        while ( start < words.length && start < split.length ) {
            if ( !words[start].equals(split[start]) )
                break;
            start++;
        }
        // Find unchanged words in the end of the field
        var end = 0;
        while ( 0 < words.length - end && 0 < split.length - end ) {
            if ( !words[words.length-end-1].equals(split[split.length-end-1]) ||
                 words.length-end-1 < start )
                break;
            end++;
        }
        // Autocorrects words in-between
        var toSplice = [start, words.length-end - start];
        for ( var i = start ; i < split.length-end ; i++ )
            toSplice.push({
                word:check(split[i], i),
                sp:split[i].sp,
                original:split[i].word,
                equals:function(w) {
                    return this.word == w.word && this.sp == w.sp;
                }
            });
        words.splice.apply(words, toSplice);
        // Updates the text, preserving the caret position
        updateText();
    
  • キャレットが単語以外の文字の直後にあり (改善の余地)、ヒットしていない場合にのみスペル チェックを適用しますbackspace

    var caret = doGetCaretPosition(this);
    var atFirstSpace = caret >= 2 &&
                       /\w\W/.test($this.val().substring(caret-2,caret));
    function check(word, index) {
        var w = (atFirstSpace && !backtracking ) ?
                options.checker(word.word) :
                word.word;
        if ( w != word.word )
            stack.push(index); // stack stores a list of auto-corrections
        return w;
    }
    
  • ユーザーが修正に満足していない場合は、backspace一度押すと元に戻り、変更されない限り再度チェックされることはありません。

    $(this).keydown(function(e) {
        if ( e.which == 8 ) {
            if ( stack.length > 0 ) {
                var last = stack.pop();
                words[last].word = words[last].original;
                updateText(last);
                return false;
            }
            else
                backtracking = true;
            stack = [];
        }
    });
    
  • のコードは、updateText単にすべての単語を再び文字列に結合し、値をtextarea. テキストの長さの変更を考慮して、何も変更されていない場合、または最後の自動修正が行われた/元に戻された直後にキャレットが配置された場合、キャレットは保持されます。

    function updateText(undone) {
        var caret = doGetCaretPosition(element);
        var text = "";
        for ( var i = 0 ; i < words.length ; i++ )
            text += words[i].word + words[i].sp;
        $this.val(text);
        // If a word was autocorrected, put the caret right after it
        if ( stack.length > 0 || undone !== undefined ) {
            var last = undone !== undefined ? undone : stack[stack.length-1];
            caret = 0;
            for ( var i = 0 ; i < last ; i++ )
                caret += words[i].word.length + words[i].sp.length;
            caret += words[last].word.length + 1;
        }
        setCaretPosition(element,caret);
    }
    
  • 最終的なプラグイン構造:

    $.fn.autocorrect = function(options) {
        options = $.extend({
            delimiter:/^(\w+)(\W+)(.*)$/,
            checker:function(x) { return x; }
        }, options);
        return this.each(function() {
            var element = this, $this = $(this);
            var words = [];
            var stack = [];
            var backtracking = false;
            function updateText(undone) { ... }
            $this.bind("input propertyChange", function() {
                stack = [];
                // * Only apply the spell check if the caret...
                // * When the contents of the `textarea` changes...
                backtracking = false;
            });
            // * If the user was unsatisfied with the correction...
        });
    };
    $.autocorrect = {
        regexSplit:function(regex,text) { ... }
    };
    
于 2012-11-30T21:07:12.587 に答える
0

キャレットの左側の単語のみを送信すると仮定すると、空白文字が入力されるかテキストボックスのキャレットが移動されるまで、スペルチェッカーを無効にできますか?

それがあなたが望んでいた種類の答えかどうかはわかりません。

于 2012-11-30T20:55:10.840 に答える