Javascript 屈服拼写检查器

Javascript 屈服拼写检查器,javascript,jquery,autocorrect,Javascript,Jquery,Autocorrect,标题可能有点误导;我的拼写检查器更多地关注格式而不是拼写(大写、标点符号和空格、撇号、将网络俚语转换为完整单词、经常混淆的单词等)。但是,基本原则适用 基本上,我正在构建的JS/jQuery检查器会在键入单词时(在单词后面键入空格或标点符号后)进行更正 然而,就像任何自动更正一样,它肯定会出错。我甚至没有考虑创建功能来确定“its”或“it's”在给定的情况下是否更合适(尽管如果存在这样的插件或代码片段,请给我指出一个) 所以我想让它成为一个“屈服”的自动更正(因为缺乏更好的名字的知识)。基本上

标题可能有点误导;我的拼写检查器更多地关注格式而不是拼写(大写、标点符号和空格、撇号、将网络俚语转换为完整单词、经常混淆的单词等)。但是,基本原则适用

基本上,我正在构建的JS/jQuery检查器会在键入单词时(在单词后面键入空格或标点符号后)进行更正

然而,就像任何自动更正一样,它肯定会出错。我甚至没有考虑创建功能来确定“its”或“it's”在给定的情况下是否更合适(尽管如果存在这样的插件或代码片段,请给我指出一个)

所以我想让它成为一个“屈服”的自动更正(因为缺乏更好的名字的知识)。基本上

  • 用户键入一个会触发检查的单词,然后键入一个 空间
  • 检查者纠正了这个词
  • 用户认为这是一个错误 并更正它(通过将整个单词或部分单词退格, 或者突出显示它,或者以他们觉得舒服的方式编辑它)
  • 用户继续键入,并且检查器不再触摸该单词的实例
  • 现在最简单的方法当然是完全禁用对该单词的检查,但我希望检查程序在将来更正它的实例。我正在寻找的是,检测到一个用户正在编辑一个自动更正的单词(无论是在键入之后还是以后),使其恢复到自动更正之前的状态,然后学习不处理该单词的特定实例

    我甚至不知道从哪里开始。我在想一个contenteditable,每个单词都包装在一个span中,自动更正的单词有一个特殊的类和一个包含原始单词的data-*属性,监听对自动更正单词的编辑,如果它被编辑回与data-*值相等,则添加一个类,将其排除在未来的自动更正循环之外


    我认为这可能是不必要的复杂,或者至少不是一条阻力最小的道路。最聪明的方法是什么?

    假设您只提交插入符号左边的单词,是否可以禁用拼写检查器,直到键入空白字符或移动文本框插入符号

    我不确定这是否是您想要的答案。

    您建议的方法(在
    span
    中分隔每个单词并在其中存储额外数据)乍一看似乎是最明智的方法。在编辑器级别,您只需确保所有文本都在某个
    span
    中,并且每个文本只包含一个单词(如有必要,将其拆分)。在单词级,只需聆听
    span
    s(绑定
    input
    propertyChange
    )中的更改,并根据其类别/数据进行操作

    然而,真正的痛苦是保持插入符号位置的一致性。当您更改
    文本区域
    或具有
    内容可编辑
    的元素的内容时,插入符号的移动相当不可预测,并且没有简单的(跨浏览器)方法跟踪插入符号。我在SO和其他地方寻找解决方案,我找到的最简单的工作解决方案是。不幸的是,它只适用于
    textarea
    ,因此“一个span中的每个单词”解决方案无法使用

    因此,我建议采取以下方法:

    • 数组中保留一个单词列表,每个单词都存储当前值和原始值
      
    • textarea
      的内容发生变化时,保留一组不变的单词,然后重做其余的单词
    • 只有在插入符号刚好位于非单词字符之后(有待改进),并且您没有点击
      退格时,才应用拼写检查
      
    • 如果用户对更正不满意,单击一次
      退格
      将撤消更正,并且除非修改,否则不会再次检查更正。
      • 如果一次做了许多更正(例如,如果复制粘贴了大量文本),则每个
        退格
        将撤消一个更正,直到没有人留下
      • 点击任何其他键都将提交更正,因此如果用户仍然不满意,他将不得不返回并再次更改
      • 注:与OP要求不同,如果用户输入非单词字符,则更改的版本将再次自动更正;他需要点击
        退格一次来“保护”它
    我创建了一个简单的概念证明。详情如下。请注意,您可以将其与其他方法相结合(例如,检测“向下箭头”键并显示带有一些自动更正选项的菜单)等


    详细说明了测试步骤:

    • 数组中保留一个单词列表,每个单词都存储当前值和原始值

      var words = [];
      
      此正则表达式将文本拆分为多个单词(每个单词都有一个
      word
      属性和一个
      sp
      属性;后者紧跟其后存储非单词字符)

    • 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();
      
    • 如果用户对更正不满意,单击一次
      退格
      将撤消更正,并且除非修改,否则不会再次检查更正

      $(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);
      }
      

    我觉得你的方法不错。你是否在寻找一个可编辑的替代方案,或者是什么让你怀疑?我总是怀疑我自己的方法。似乎跟踪这些跨度,特别是当内容被编辑或删除在中间,可能
    $(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 = [];
        }
    });
    
    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) { ... }
    };