jQuery.Maxlength icon indicating copy to clipboard operation
jQuery.Maxlength copied to clipboard

Fix length calculation for Mac new lines /r/n

Open mjvestal opened this issue 10 years ago • 3 comments

The count is incorrect when entering new lines in a textarea on a Mac. Each new line should count as 2 characters, but jQuery's .length counts /r/n as 1 character.

mjvestal avatar Aug 20 '14 22:08 mjvestal

Hey, thanks for that PR!

I've tried putting a CRLF (\r\n) inside a textarea on Mac (Mavericks 10.9.4), and both the browser (latest Chrome) and the plugin understood it as a single newline character, which is what I was expecting of them. Same behavior when simply hitting Enter.

I'm thus not sure what the fix you're proposing is for. Does the CR+LF combination usually lead to two newlines on Mac (I don't own one)? Should hitting Enter once create two newlines?

philippe-git avatar Aug 21 '14 11:08 philippe-git

CR+LF lead to 1 new line, but browsers count it as 2 characters. To reproduce, go to your jsfiddle http://jsfiddle.net/pioul/ysC7L/ and type "line" and Enter into the maxlength=50 text area . This should be 5 characters if its just a LF, so you'd expect to be able to enter 10 rows + 1 empty row. On a Mac however the textarea stops taking input on the 9th row, after "li" but your plugin still says "8 characters left"

Here is a video of the issue on the iPad simulator so you can see the that I'm trying to enter characters on the last line when the text still says "8 characters left" but cannot - http://screencast.com/t/xYozYWvL

I'd expect the text to say "0 characters left" when the user has reached the limit.

mjvestal avatar Aug 25 '14 15:08 mjvestal

My apologies for the late reply, I've been extremely busy during the last few months.

Also, thanks for bringing this issue to my attention. Interestingly, I wasn't aware of this discrepancy before, even though it now seems ubiquitous. It might have to do with the fact that the maxlength attribute is a "recent" addition to textareas, contrarily to regular inputs.

I've run your manual test (type "line", press Enter, repeat until you reach the max) on a small range of browsers I had at hand:

  • Chrome (Windows and OS X) counts a line break as 2 distinct characters
  • Firefox (Windows and OS X) counts a line break as 1 single character
  • IE (Windows) counts a line break as 1 single character
  • Safari (OS X) counts a line break as 2 distinct characters

You'll notice I said these browsers were counting line breaks as 1 or 2 characters, without explicitly naming CR+LF as the pair of characters used to represent line breaks; that's because I couldn't make sure they're in practice encoding line breaks as CR+LF.

In fact, the W3C HTML 5 spec says that line breaks should be represented as single LF characters inside a form element's API value, which is the one retrieved using JS.

For historical reasons, the element's value is normalised in three different ways for three different purposes. The raw value is the value as it was originally set. It is not normalized. The API value is the value used in the value IDL attribute. It is normalized so that line breaks use "LF" (U+000A) characters. Finally, there is the form submission value. It is normalized so that line breaks use U+000D CARRIAGE RETURN "CRLF" (U+000A) character pairs [...]

However, it also says that such line breaks should be represented as CR+LF pairs in the value used for submission. The W3C HTML 4.01 spec also touches on this.

So, I'm not sure what behavior should be implied from these rules – and browser vendors don't seem sure either. If the spec was to be followed thoroughly, all browsers would behave along the following lines:

  • Line breaks in textareas are normalized to a single LF character
  • When a form is submitted, those same line breaks should be normalized to a CR+LF pair

However, even when following these two rules scrupulously, one question remains regarding a textarea's maxlength: Should a line break in a textarea's value count as one or two characters?

The HTML 5 spec once again comes to the rescue, stating that the maxlength attribute's constraint should be evaluated based on "the code-unit length of the element's value". Since a line break in the current value of the textarea should be encoded as a single LF character, it should only count towards 1 character.

To sum it all up, here's the current state of affairs:

  • Line breaks aren't represented by the same set of characters when the form is being filled and when it's submitted: they're respectively represented as single LF characters, and then CR+LF pairs.
  • The correct behavior for a browser is to count a line break as 1 character when limiting the form control's value to a maximum length (that's what Firefox and IE seem to be doing).
  • Some browsers choose to count a line break as 2 characters for their maxlength calculations (Chrome and Safari). I'd like to speculate and say they're doing this to take into account the fact that line breaks will, in fact, be represented as 2 characters when the form will be submitted.

What we're really left with, is another question: What does really matter to us and our users? The current value of the textarea (where line breaks are LFs), or its future value, the one that'll be submitted with the form (where line breaks are CR+LFs)?

For us developers, I'd say it depends on how these values are treated, and what the system they're sent to expects in terms of constraint validation.
For users, I don't see any of them expecting line breaks to be counted as 2 characters.

Hence, I'd like every browser to count line breaks as a single character for constraint validation:

  • That's what's best for users
  • That's what the W3C recommends, and normalization is what's best for us developers

In the meantime, I'm not sure how to make jQuery.maxlength report accurate results when the browsers themselves are wrong: the plugin only monitors the number of characters entered, and leaves constraint validation to the browser, mostly for progressive enhancement purposes.

A solution such as the one you proposed in your PR fixes the issue with Chrome and Safari, which both count line breaks as two characters for constraint validation: entering 10 alphanumeric characters and 2 line breaks will count as 14 characters in the eyes of these browsers, so forcing the plugin to count 2 characters for each line break makes sense.
It doesn't, however, take into account browsers like Firefox and IE, which already got things right. 10 alphanumeric characters and 2 line breaks will be counted as 12 characters, and a regular textarea.value.length will report the same, accurate length. Your fix would incorrectly make the counter display the number 14 in these browsers.

So, yeah, not sure what can be done. I've starred the Chromium issue, even though someone in there has a different interpretation of the spec than mine. I'd be interested in hearing yours.

If this issue is really important to your use-case, I'd consider using browser sniffing and applying your proposed fix on non-compliant browsers, with all the risks such a thing entails. If not, I'd just wait for this whole situation to be fixed by browsers. In the meantime, since I can't think of any bulletproof way of fixing this at the plugin's level, it'll be left as is. (Unless, of course, someone comes up with a worthwhile solution, which would be incredibly welcome!) I'll add a note about this in the plugin's docs, though.

philippe-git avatar Oct 11 '14 19:10 philippe-git