4

I have a application that runs in the browser that compares strings. String compares with quotes fail using iOS11 because it is defaulting to smart quotes being on. can’t does not equal can't

I know smart quotes can be disabled under Setting for the whole device but I would like to handle it at the textarea level.

I thought I would be able to catch the smart quote on the keypress event, but iOS is making the switch later and under the covers.

textareaText is the text in my textarea field
39 is the character code for single quote
8216 is the character code for the left single smart quote
222 is the key code for the quote key
e is the event object passed to the keyboard event

keydown 
    e.keycode -> 222, e.key -> ', key.charCodeAt(0) -> 39 
    textareaText -> empty
keypress
    e.keycode -> 39, e.key -> ', key.charCodeAt(0) -> 39 
    textareaText -> empty
—> character is put in textarea here
keyup (iPad onscreen keyboard)
    e.keycode -> 222, e.key -> ', key.charCodeAt(0) -> 39 
    textareaText -> ’, textareaText.charCodeAt(0) -> 8216 
keyup (iPad external keyboard)
    e.keycode -> 0, e.key -> ', key.charCodeAt(0) -> 39 
    textareaText -> ’, textareaText.charCodeAt(0) -> 8216 

The keyup event thinks the character is the regular quote while the textarea contains the smart quote.

I tried:

textarea.innerHTML = textarea.innerHTML.replace(/’/,'\'')

but the cursor gets reset to the beginning of the string and I don't want to have to get and set the cursor position.

Is there any way to swap the smart quote before it is entered into the textarea? Or some way to disable smart quotes at the textarea level? Is any other solution to this problem?

My last resort is to replace the smart quotes in the string right before I compare it but I would prefer the browser to display the regular quotes to be consistent with what they entered, what they see, and what they are checked against.

Emily
  • 9,926
  • 4
  • 31
  • 39
  • How are you comparing the strings? If this were me, I'd be tempted to just re-write my comparison and replace quotes in the variables there if it's an option. – Nikki9696 Aug 04 '17 at 15:10
  • @Nikki9696 Replacing the quotes before my string compare is my last resort option. The issue that introduces is that the strings can be very long and if the answer is incorrect, I display the student answer along with the correct answer. If I am displaying the student answer with smart quotes, they may think that is what is wrong and not notice what the real problem is. – Emily Aug 04 '17 at 16:28

2 Answers2

8

The simplest way is to pass spellcheck="false" to the input element. This is based on how WebKit works: https://github.com/WebKit/webkit/blob/master/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm#L4852

Erik
  • 227
  • 3
  • 5
  • Seems to be new and relevant. – Sean Halls Jul 13 '20 at 05:07
  • 1
    This approach is subtle and not obvious (I suggest leaving a comment in the code explaining why it's there), but importantly it doesn't prevent a smart quote IF it was explicitly intended. For instance, if a user had used a smart quote in their username, and you changed your username field to replace smart quotes, that user would now be unable to successfully input their username and thus unable to login. For text fields where the characters matter more than just display, you should be very cautious of forcing replacing. Setting the `spellcheck="false"` prevents that situation. – Erik Jul 15 '20 at 00:07
1

I ended up using the document.execCommand on the keypress event. This solution also works for contenteditable divs.

function checkInput(e) {
  if ((e.which == 39) || (e.which == 8216) || (e.which == 8217)) {
    e.preventDefault();
    document.execCommand('insertText', 0, "'");
  }
}   

var el = document.getElementById('txtIn');
el.addEventListener('keypress', checkInput, false);
Emily
  • 9,926
  • 4
  • 31
  • 39