Задача: разработать проверку орфографии для пользовательского словаря (в моем случае это был словарь медицинских терминов).

Prelude: конечно, Google Chrome имеет встроенную в браузер проверку орфографии, но проблема в том, что нельзя настроить правила проверки и добавить в нее внешние словари. И это индивидуально для каждого пользователя.

Еще одна проблема заключается в том, что вы не можете стилизовать входной контент, чтобы добавить волнистое подчеркивание под неправильным словом.

Решение: создайте теневой div позади ввода с прозрачным текстом, но с красным подчеркиванием под ним.

<div id=”spellCheckerContainer” style=”position: relative; display: inline-block; width: 100%;”> 
      @Html.TextBoxFor(m => m.DiseaseName, new { @class = “k-input k-textbox”, spellcheck = “false”, style = “width: 100%;text-align:left;position: absolute;outline: 0;z-index: 1;background: transparent;top: -1.8em;” }) 
      <div id=”diseaseNameSpellChecker” spellcheck=”false” style=”width: 100%;position: absolute;top: -1.8em;z-index: 0;padding-left: 0.7em;color:transparent”>
       </div> 
</div>

Вот MVC с Razor, поэтому @Html.TextBoxFor — это просто ввод.

И CSS:

.err { 
      display: inline-block; 
      position: relative; 
} 
.err:before { 
      content: “~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~”; 
      font-size: 0.6em; 
      color: red; 
      width: 100%; 
      position: absolute; 
      top: 2.4em; 
      overflow: hidden; 
}

И джаваскрипт:

$(“input#DiseaseName”).keyup(function (event) {  
      spellCheck(event.currentTarget.value);
}); 
function spellCheck(source) { 
      var shadowDiv = $(“div#diseaseNameSpellChecker”); 
      var index = 0; 
      shadowDiv.empty(); 
      while (index < source.length) { 
            if (source[index] === “ “) {     
                 shadowDiv.append(“<span>&nbsp</span>”); 
                 index++; 
            } else { 
                 var wordEndIndex = 
                     source.substring(index).indexOf(“ “); 
                 if (wordEndIndex === -1) { 
                     wordEndIndex = source.length — index; 
                 } 
                 var word = source.substring(index, index + 
                            wordEndIndex); 
                 var wordSpan = $(“<span>” + word + “</span>”); 
                 checkIsWordHasTypos(word, wordSpan);  
                 shadowDiv.append(wordSpan); 
                 index += wordEndIndex; 
            } 
      }
} 
function checkIsWordHasTypos(word, wordContainer) { 
     var resultInLocalStorageIsHasTypo = localStorage.getItem(word);  
     if (resultInLocalStorageIsHasTypo !== null) {   
          setContainerIfItHasTypo(wordContainer, 
              resultInLocalStorageIsHasTypo === “true”); 
          return; 
     } 
     $.ajax({ 
          url: “api/SpellChecking/CheckIsWordInDictionary”, 
          data: {word: word}, 
          success: function (msg) { 
               var isHasTypo = !msg; 
               setContainerIfItHasTypo(wordContainer, isHasTypo); 
               localStorage.setItem(word, isHasTypo); 
          }, error: function(e) { 
               console.log(“error” + e); } });} 
function setContainerIfItHasTypo(wordContainer, isHasTypo) { 
      if (isHasTypo) { 
           wordContainer.addClass(“err”); 
      }
}

Здесь используется localStarage для хранения результатов проверки, я не хочу бомбить сервер каждым словом после каждого нажатия клавиши. Мы предполагаем, что правила проверки никогда не изменятся.