Html: Textarea defaultValue handling doesn't really match UAs (who don't match each other) when there are child elements

Created on 12 Jun 2017  路  14Comments  路  Source: whatwg/html

Consider this testcase:

<!DOCTYPE html>
<pre><script>
    var t = document.createElement("textarea");
    t.appendChild(document.createElement("span")).appendChild(document.createTextNode("TEXT"));
    document.body.appendChild(t);
    document.writeln("textContent: " + t.textContent);
    document.writeln("defaultValue: " + t.defaultValue);
    document.writeln("value: " + t.value);
  </script>
</pre>

Per spec, https://html.spec.whatwg.org/multipage/form-elements.html#dom-textarea-defaultvalue is supposed to return the value of the textContent IDL attr, and per https://html.spec.whatwg.org/multipage/form-elements.html#the-textarea-element:textcontent the raw value is supposed to match the textContent if the dirty flag is false. So per spec as currently written, all three of textContent, defaultValue, value should be "TEXT", and I would think that "TEXT" should appear in the textarea.

Actual observed behavior is:

  • Gecko: textContent is "TEXT", defaultValue/value are "", textarea is empty.
  • WebKit: textContent/defaultValue are "TEXT", value is "", textarea is empty.
  • Blink: textContent is "TEXT", defaultValue/value are "", textarea is empty.
  • Edge: textContent/value are "TEXT", defaultValue is "", textarea shows "TEXT"

Live testcase: https://jsfiddle.net/t9dz1zyj/1/

It looks like Edge is generally pretty buggy on this defaultValue business; the testcase at https://jsfiddle.net/t9dz1zyj/2/ shows "TEXT" for all three properties in Gecko/Blink/WebKit, and should per current spec, but still shows empty defaultValue in Edge...

Anyway, it looks like Edge is trying to do what the spec says, modulo its defaultValue bug. I know for a fact (code inspection) Gecko only considers direct child textnodes for its default value for textarea. Looks like Blink does the same. I'm really not sure what WebKit is doing.

//cc @domenic @travisleithead

interop forms

All 14 comments

the testcase at https://jsfiddle.net/t9dz1zyj/2/

That's

<!DOCTYPE html>
<pre><script>
    var t = document.createElement("textarea");
    t.textContent = "TEXT";    
    document.body.appendChild(t);
    document.writeln("textContent: " + t.textContent);
    document.writeln("defaultValue: " + t.defaultValue);
    document.writeln("value: " + t.value);
  </script>
</pre>

Combined with https://github.com/whatwg/html/issues/2750 and the related selection bugs, I'm becoming convinced that everything is pretty broken around the textarea spec.

Do you have a suggestion for a reasonable model we could try to converge everyone on, ideally solving both this issue and #2750? And maybe https://github.com/whatwg/html/issues/2424 as well (cf. https://github.com/whatwg/html/issues/2424#issuecomment-286580627)

Well, the good news is that Gecko and Blink seem to have interop for purposes of this issue.

The conceptual model in Gecko is as follows:

  • There is an internal slot that stores the default value.
  • There is an internal algorithm to update the value of that slot; this algorithm (let's call it UpdateDefaultValue) sets the slot to the concatenation of the immediate child Text nodes of the textarea. It also updates the raw value if the slot value is changing and the "dirty value" flag is not set.
  • The defaultValue setter sets .textContent.
  • Mutations that would potentially affect .textContent trigger UpdateDefaultValue via an internal hook mechanism, not document observers. This internal hook mechanism doesn't have an equivalent in the DOM spec right now, but I suspect exists in most or all implementations.

There are some complications in terms of what happens if you do these things before the parser is actually done parsing the textarea, though I'm not 100% sure whether that's even a situation you can get into in practice (do we ever insert the textarea into the DOM before we finish parsing its content in a way such that we can end up waiting on network for a bit there and run user script?).

The actual implementation of the Gecko behavior is as follows:

So in practice we're not storing the "default value" in any form other than the actual DOM subtree under the textarea.

OK, great. It sounds like the majority of spec work here will be specifying the "something that would affect textContent". I think my plan would be:

  • Put a placeholder hook into the DOM spec, e.g. "text content changed steps", which is defined lamely (e.g. "whenever something happens that would affect the value returned by textContent") with a TODO to formalize it later.
  • Implement your above model in the spec (with either an explicit default value slot, or not)
  • Also use that model to build a solution for the selection issue, since IIRC we landed on that being the crux of specifying that as well
  • Write lots of tests, and in the process, maybe figure out how to spec the lamely-specced DOM hook.

I think Gecko and Blink behavior, concatenating Text children, is reasonable. If TEXTAREA needs to react to any textContent change, DOM mutation operations need to be notified to all of TEXTAREA ancestors, and it would slow down DOM mutation without TEXTAREA too.

WebKit's behavior is almost same as Gecko and Blink internally. It just returns textContent for defaultValue IDL attribute getter.

Hmm, I skimmed over the difference between textContent and contenation of immediately child text nodes. The spec currently leans hard on the connection to textContent, e.g.

The reset algorithm for textarea elements is to set the dirty value flag back to false, and set the raw value of element to the value of the element's textContent IDL attribute.

It sounds like we can match Firefox/Chrome by converging away from textContent to the concatenation of child text nodes.


Here is my proposed spec text, replacing a few existing paragraphs (Ctrl+F for "textContent" in the textarea section of the spec):

A textarea element has the following child text changed steps:

  1. If the textarea element's dirty value flag is true, then return.
  2. Set the textarea element's raw value to its child text.

[child text changed and child text are defined as you would expect, in DOM I guess.]

The reset algorithm for textarea elements is to set the dirty value flag to false, and set the raw value of element to its child text.

If the textarea element has a maximum allowed value length, then the element's children must be such that the JavaScript string length of the value of the element's textContent IDL attribute with the textarea line break normalization transformation applied is equal to or less than the element's maximum allowed value length. [no change; this is a conformance requirement on authors who should not be inserting child elements into textarea elements]

The defaultValue IDL attribute must return the element's child text.


Does this sound good to you, @bzbarsky and @tkent-google? @cdumez might enjoy this as well, as even though Safari doesn't match the proposal/Firefox/Blink, it also doesn't match the current spec.

That sounds reasonable to me. We should really check with Microsoft, since they're the closest to what the current spec says, though still not matching it.

https://html.spec.whatwg.org/#child-text-content is a concept the spec has already for a bunch of things, FWIW.

Is there a web compat need to flip the dirty flag if textContent changes but "child text content" doesn't? Is that hook used for anything other than textarea? I'm wondering if it's possible (and if there's interest) to make this simpler and basically only consider the children of textarea.

cc @travisleithead

Is there a web compat need to flip the dirty flag if textContent changes but "child text content" doesn't?

I don't follow. The dirty flag is never changed by changes to either "textContent" or "child text content". What those changes do is, if the dirty flag is _not_ set, set the raw value to the "child text content" (or "textContent", in current spec). They do NOT affect the dirty flag.

Ok, sorry, I was clearly confused.

So if the dirty flag is not set, can we not set the raw value when non-child descendants change?

That is the proposal, yes.

Though as long as we set it to "child text content", it doesn't matter whether we do it when only kids change or when non-child descendants change, right? The observables are the same.

I seem to have lost a step in my above set, which does nothing if the dirty value flag is true. I edited to add that. That might be causing some of the confusion.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

NE-SmallTown picture NE-SmallTown  路  4Comments

NE-SmallTown picture NE-SmallTown  路  3Comments

lazarljubenovic picture lazarljubenovic  路  4Comments

domenic picture domenic  路  4Comments

empijei picture empijei  路  3Comments