Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Autocapitalization and autocorrect not supported on iOS #7009

Open
josephdpurcell opened this issue Dec 7, 2022 · 21 comments · Fixed by #7010
Open

Autocapitalization and autocorrect not supported on iOS #7009

josephdpurcell opened this issue Dec 7, 2022 · 21 comments · Fixed by #7010

Comments

@josephdpurcell
Copy link
Contributor

josephdpurcell commented Dec 7, 2022

NOTE: CodeMirror6 does not experience this issue, see codemirror/dev#1020 (comment).

Using Safari on iOS 15.6.1 I am unable to get autocapitalization and autocorrect to work using the configuration specified in the manual here https://codemirror.net/5/doc/manual.html#config, with CodeMirror 5.65.10 seen here https://cdnjs.com/libraries/codemirror/5.65.10 (also tested master branch).

The basic code is:

var myTextarea = document.getElementById("myCode");
editor = CodeMirror.fromTextArea(myTextarea, {
    lineNumbers: true,
    autocorrect: true,
    spellcheck: true,
    autocapitalize: true,
    inputStyle: 'textarea'
});

For reference here are code pens showing behavior of plain textarea and contenteditable:

A PASS is given if the following are true:

  • Autocapitalization: The first character ever typed, a character after a punctuation, a character at the beginning of a new line each have the caps key enabled so the character is typed as an uppercase character.
  • Autocorrect: Any phrase in the input is a candidate for being autocorrected.
  • Autopunctuation: A double-space after the characters [a-zA-Z0-9] turns into a period followed by 1 space.

Here are code pens I've created to test different scenarios, I've put them in a collection https://codepen.io/collection/NqEdwO. And note testing results below.

Legend for cases:

  • textarea: The HTML element is a textarea.
  • contenteditable: The HTML element is a contenteditable.
  • inputStyle: The input style was forced to textarea or contenteditable, based on which HTML element was used.
  • options: The autocorrect: true and autocapitalization: true arguments were passed as options to CodeMirror.
  • setOption: The autocorrect and autocapitalization arguments were passed using setoption.
  • setAttribute: The JavaScript setAttribute function was used to "manually" set the autocorrect and autocapitalize options, either using getInputField or querySelectorAll to get the elements.
  • setTimeout + X: The X was wrapped in a setTimeout.
  • default: No options were given; the CodeMirror defaults will be used.
Case Autocapitalization Autocorrect Autopunctuation
CASE 1: textarea, inputStyle, options
CASE 2: textarea, inputStyle, options, setOption
CASE 3: textarea, inputStype, options, setTimeout + setOption
CASE 4: contenteditable, inputStyle, options
CASE 5: contenteditable, inputStyle, setAttribute w/getInputField 1
CASE 6: textarea, inputStyle, default
CASE 7: contenteditable, inputStyle, default
CASE 8: textarea, inputStyle, options, setTimeout + setAttribute w/querySelectorAll 2
CASE 9: textarea, inputStyle, options, setAttribute w/getInputField 3
CASE 10: textarea, options 4
CASE 11: contenteditable, options 5
CASE 12: contenteditable using div, options 6

1 Only the first letter of the first word typed was capitalized, but never again.
2 Autocapitalization did not happen on new lines unless capitalization was already on, eg type "W-space-return-w" and you will get an upperacse W on the first line and a lowercase w on the second line but if you type "W-space-space-return-w" you get an uppercase W with a period on the first line and an uppercase W on the second line.
3 Same as note 2, autocapitalization is a little finicky.
4 Autocapitalization did not happen on new lines, nor after punctuation.
5 Autocapitalization did not happen on new lines, nor after punctuation.
6 Autocapitalization did not happen on new lines, nor after punctuation.

Note that CASE 12 is the only case that does not use the CodeMirror.fromTextArea pattern.

Recording of testing CASE 1-3 and CASE 8 at 2x speed (note the video has it mis-labeled):

Expand to view video
FullSizeRender.MOV

Recording does not include cases 4, 5, 6, 7, nor 9.

Edit 12/7 3p ET: Clarified that autopunctuation does work.
Edit 12/7 6p ET: Added case 4, 5, and pass 2.
Edit 12/8 6a ET: Added case 6, 7, demos, and summary. Reflect discovery that all scenarios fail.
Edit 12/8 945a ET: Added case 10, 11, 12.
Edit 12/8 115p ET: Noted that CodeMirror6 does not have this issue.
Edit 12/8 315p ET: Modified to use a table for readability, and used the word CASE instead of pass/fail in the label.

@josephdpurcell josephdpurcell changed the title Autocapitalization, autopunctuation, and autocorrect not supported on iOS Autocapitalization and autocorrect not supported on iOS Dec 7, 2022
@josephdpurcell
Copy link
Contributor Author

This suggestion #3403 (comment) tipped me to what I think is the answer:

window.onload = function() {
  var myTextarea = document.getElementById("code");
  editor = CodeMirror.fromTextArea(myTextarea, {
    lineNumbers: true,
    spellcheck: true,
    inputStyle: 'textarea'
  });
  var input = editor.getInputField();
  input.setAttribute('autocorrect', 'on');
  input.setAttribute('autocapitalize', 'on');
};

Note that we are manually setting the attribute.

And, I suspect it's because of this bug: #3403 (comment)

It looks like there was a commit to set the value to an empty string? 97f6acc

And here is another related ticket #4865

It looks like Apple's docs say the values should be on or off not an empty string or off. See:

The resolution to this ticket, then, is to use the value on instead of an empty string.

@josephdpurcell
Copy link
Contributor Author

Here's a diff I tested:

diff --git a/src/input/input.js b/src/input/input.js
index e7401062..d737c172 100644
--- a/src/input/input.js
+++ b/src/input/input.js
@@ -114,8 +114,8 @@ export function copyableRanges(cm) {
 }
 
 export function disableBrowserMagic(field, spellcheck, autocorrect, autocapitalize) {
-  field.setAttribute("autocorrect", autocorrect ? "" : "off")
-  field.setAttribute("autocapitalize", autocapitalize ? "" : "off")
+  field.setAttribute("autocorrect", autocorrect ? "on" : "off")
+  field.setAttribute("autocapitalize", autocapitalize ? "on" : "off")
   field.setAttribute("spellcheck", !!spellcheck)
 }

However, this only takes effect on contenteditable! And, with contenteditable the experience sets autocapitalize on the first character but none others, and autocorrect doesn't work.

The way I tested this was to apply this change to master branch, then:

npm i
npm run build
# create an index.html that loads contents like below

index.html

<!doctype html>
<html lang="en-US">
  <head>
      <meta charset="UTF-8">
      <title>CodeMirror5 - contenteditable</title>
      <script src="lib/codemirror.js"></script>
      <link rel="stylesheet" href="lib/codemirror.css">
      <script src="mode/javascript/javascript.js"></script>
  </head>
  <body>
    <h1>CodeMirror5 - contenteditable</h1>

    <input type="text" id="myCode"></input>

    <script type="text/javascript">
      window.onload = function() {
        var myTextarea = document.getElementById("myCode");
        editor = CodeMirror.fromTextArea(myTextarea, {
          lineNumbers: true,
          autocorrect: true,
          autocapitalize: true,
          spellcheck: true,
          inputStyle: 'contenteditable'
        });
      };
    </script>
  </body>
</html>

or with textarea like

index.html

<!doctype html>
<html lang="en-US">
  <head>
      <meta charset="UTF-8">
      <title>CodeMirror5 - textarea</title>
      <script src="lib/codemirror.js"></script>
      <link rel="stylesheet" href="lib/codemirror.css">
      <script src="mode/javascript/javascript.js"></script>
  </head>
  <body>
    <h1>CodeMirror5 - textarea</h1>

    <input type="text" id="myCode"></input>

    <script type="text/javascript">
      window.onload = function() {
        var myTextarea = document.getElementById("myCode");
        editor = CodeMirror.fromTextArea(myTextarea, {
          lineNumbers: true,
          autocorrect: true,
          autocapitalize: true,
          spellcheck: true,
          inputStyle: 'contenteditable'
        });
      };
    </script>
  </body>
</html>

So... back to the drawing board. I'm curious why CodeMirror only applies autocorrect and autocapitalize to contenteditable.

@josephdpurcell
Copy link
Contributor Author

I see this comment that affirms only contenteditable gets the privilege of being autocorrect https://discuss.codemirror.net/t/keeping-autocorrect-on-in-mobile-safari/1037/4.

@josephdpurcell
Copy link
Contributor Author

Based on my testing, what iOS is needing is a textarea that has autocorrect="on" and autocapitalization="on" and from what I'm seeing CodeMirror5 does not support this scenario using the options shown in the manual. Can anyone confirm this statement is true?

If it is true, then in order to support these functions on iOS you need to manually set them like so:

<!doctype html>
<html lang="en-US">
  <head>
      <meta charset="UTF-8">
      <title>CodeMirror5 - textarea</title>
      <script src="lib/codemirror.js"></script>
      <link rel="stylesheet" href="lib/codemirror.css">
      <script src="mode/javascript/javascript.js"></script>
  </head>
  <body>
    <h1>CodeMirror5 - textarea</h1>

    <textarea id="myCode"></textarea>

    <script type="text/javascript">
      window.onload = function() {
        var myTextarea = document.getElementById("myCode");
        editor = CodeMirror.fromTextArea(myTextarea, {
          lineNumbers: true,
          spellcheck: true,
          inputStyle: 'textarea'
        });
        var input = editor.getInputField();
        input.setAttribute('autocorrect', 'on');
        input.setAttribute('autocapitalize', 'on');
      };
    </script>
  </body>
</html>

@marijnh
Copy link
Member

marijnh commented Dec 8, 2022

Based on my testing, what iOS is needing is a textarea that has autocorrect="on" and autocapitalization="on"

They changed the default from on to off? From the code, it looks like the editor is just not adding the autocorrect=off attribute when you enable autocorrect. Can you confirm that an editable field without the attribute no longer does autocorrection?

@josephdpurcell
Copy link
Contributor Author

josephdpurcell commented Dec 8, 2022

Videos are at 4x speed.

Here is a video of what a plain textarea field looks like on iOS with these two attributes in various states using this codepen https://codepen.io/josephdpurcell/pen/xxzBgjz.

Expand to view video
FullSizeRender.MOV

Here is the same but for contenteditable using this codepen https://codepen.io/josephdpurcell/pen/NWzJdEr.

Expand to view video
FullSizeRender.MOV

@josephdpurcell
Copy link
Contributor Author

@marijnh I created two new codepens titled FAIL 6 and FAIL 7:

They show the behavior of CodeMirror5 having given no options for autocapitalize and autocorrect.

FAIL 6 is a textarea and worked for autopunctuation, but not for autocorrect nor autocapitalize.

FAIL 7 is a contenteditable and did not work for any of those features: autopunctuation, autocorrect, nor autocapitalize.

I hope that answers your question? A contenteditable field with no options supplied does not work for autocorrection. And, if you were meaning what a "plain" contenteditable behavior is see my previous comment where I did some testing.

@josephdpurcell
Copy link
Contributor Author

I just updated the description. PASS 1 is now FAIL 8 -- it does not autocapitalize on new lines. PASS 2 is now FAIL 9 -- it does not support autocorrect and it does not autocapitalize on new lines.

@marijnh
Copy link
Member

marijnh commented Dec 8, 2022

They show the behavior of CodeMirror5 having given no options for autocapitalize and autocorrect.

Not enabling autocorrect and autocapitalize is the intended behavior when no options are given.

I'm sorry, but I don't see through the flurry or messages and videos (for something that could have been a table? and seems to show the browser behaving as expected?) in this issue. Can you try to provide a single, focused example of something that is not working as you'd expect it to?

@josephdpurcell
Copy link
Contributor Author

josephdpurcell commented Dec 8, 2022

@marijnh Sorry for the confusion, I was attempting to address your question:

Can you confirm that an editable field without the attribute no longer does autocorrection?

But in the process of answering it I discovered that there is no scenario in which autocorrect, autocapitalization, and autopunctuation all function on iOS with CodeMirror5.

From the description on this ticket, this is the code that does not work:

var myTextarea = document.getElementById("myCode");
editor = CodeMirror.fromTextArea(myTextarea, {
    lineNumbers: true,
    autocorrect: true,
    spellcheck: true,
    autocapitalize: true,
    inputStyle: 'textarea'
});

Here is the code pen: https://codepen.io/josephdpurcell/pen/xxzMLQw

Here is a recording showing how it does not work:

IMG_8963.MOV

Edit 12/8 930a ET: Put code in code block.

@josephdpurcell
Copy link
Contributor Author

To see how autocorrect, autopunctuation, and autocapitalization are intended to work on iOS you can use the plain textarea input on this code pen: https://codepen.io/josephdpurcell/pen/xxzBgjz

Here is a recording:

IMG_8964.MOV

@marijnh
Copy link
Member

marijnh commented Dec 8, 2022

Okay, I see what you mean now. Those options are only used for contenteditable input style, not textareas. It seems likely that the autocorrect suggestions will be invisible or misplaced in that configuration anyway. Why are you forcing inputStyle to textarea on mobile?

@josephdpurcell
Copy link
Contributor Author

Ultimately, I'm trying to achieve autocorrect, autocapitalization, and autopunctuation on the Markdown field for Directus, here is an explanation.

My assumption was that I had to set the inputStyle. I'll attempt some more testing without that setting.

I tried these settings on contenteditable too, see https://codepen.io/josephdpurcell/pen/KKeEwWo. It did not work.

@marijnh
Copy link
Member

marijnh commented Dec 8, 2022

The editable element in that example has autocorrect="" autocapitalize="" spellcheck="true" attributes which, according to your tests above should work.

@josephdpurcell
Copy link
Contributor Author

Yes, based on my testing those attributes should result in an input that supports autocorrect, autocapitalization, and autopunctuation. However, it does not.

Here is a recording of https://codepen.io/josephdpurcell/pen/KKeEwWo (FAIL 4):

IMG_8967.MOV

I'm happy to try something different.

@josephdpurcell
Copy link
Contributor Author

I tried the scenario where I do not included the inputStyle option both for textarea and for contenteditable and both fail (see description FAIL 10 and 11). In both cases the first character gets capitalized, but no other characters ever. Autocorrect and autopunctuation do not work.

@josephdpurcell
Copy link
Contributor Author

I noticed in my contenteditable tests that I use an input element. I didn't know if that would cause different behavior so I did a test using a div element and it did not work in the same way as 10 and 11. I added it to the description as FAIL 12.

@josephdpurcell
Copy link
Contributor Author

@marijnh Regarding "something that could have been a table", I updated the description to use a table. I took about 30 mins, it was a little tedious. But, I hope it's more readable and helpful.

CASE 8 is what most closely matched the desired behavior, but it uses a setTimeout hack.

@marijnh
Copy link
Member

marijnh commented Dec 9, 2022

There's an iOS Safari bug (at least six years old at this point, they don't seem to care a lot about this stuff) where putting the cursor somewhere via a script does not update the autocapitalization state of the virtual keyboard. That may be what's breaking autocapitalize.

This version is not under active development, so it's unlikely that anything big will be overhauled to make this work.

I've merged #7010, but you already mention in the comments that that doesn't actually make a difference.

@marijnh marijnh reopened this Dec 9, 2022
@josephdpurcell
Copy link
Contributor Author

Regarding:

putting the cursor somewhere via a script does not update the autocapitalization state of the virtual keyboard

I did some searching too and ran across this issue: https://dev.ckeditor.com/ticket/12142 for CKEditor. It says:

I did notice that the RETURN key is not firing the 'keydown' event. It does fire the 'keyup' event.

And I also found ckeditor/ckeditor4#4180 for CKEditor4 which says:

I was able to kind-of work around this by focusing an offscreen element and then re-focusing the editor when the user presses enter, but it's pretty noticeably slower

Followed by a comment saying that workaround stopped working in iOS 14.3.

And I see ckeditor/ckeditor5#1111 for CKEditor5.

I'm not sure what to conclude here. I am only vaguely aware of just how complex it is to write a WYSIWYG/editor, I suspect there isn't an easy fix. It is interesting that CodeMirror6 does not experience this issue (though it does seem to show both CM6 autocorrect and iOS autocorrect UIs which is odd).

Going back to my own personal motivation on this ticket, I am researching what I think is a best-fit Markdown editor over here: directus/directus#16762 (comment). What I have found is that there are Markdown-specific editors that do support autocorrect, autopunctuation, and autocapitalization in the way I'm desiring. GitHub and StackExchange sites seem to also support these features well. I suspect that the more robust WYSIWYG-style editors like CKEditor and CodeMirror run into these OS-intereference issues? It's an interesting thing to observe.

CodeMirror is truly impressive, and thank you for commenting to help validate that this is an issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants