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

Missing reportValidity() hook #9878

Open
jpzwarte opened this issue Oct 24, 2023 · 11 comments
Open

Missing reportValidity() hook #9878

jpzwarte opened this issue Oct 24, 2023 · 11 comments
Labels
addition/proposal New features or enhancements needs concrete proposal Moving the issue forward requires someone to figure out a detailed plan needs implementer interest Moving the issue forward requires implementers to express interest topic: custom elements Relates to custom elements (as defined in DOM and HTML) topic: forms

Comments

@jpzwarte
Copy link

What is the issue with the HTML Standard?

Given a form containing both native elements (<input>s) and Form Associated Custom Elements, there are several ways to trigger the browser to show any validation messages:

  • form.requestSubmit()
  • form.reportValidity()
  • input.reportValidity()

Calling this when the input is :invalid or after calling setCustomValidity(message) on it, this will trigger a native "popover" with the built-in or custom validation message.
CleanShot 2023-10-24 at 17 58 17@2x

Each vendor has its own styling for these "popovers". So I'm trying provide my own styling which is the same across vendors. The easy part is to call event.preventDefault() on the invalid event. That event is dispatched when calling checkValidity() or reportValidity(). By doing that, all vendors no longer show the native popover.

The problem starts after that: there is no way to detect when a Form Associated Custom Element (or a web component wrapping an <input> for that matter) should display a validation message. The only event dispatched when calling reportValidity() is invalid, but since that is also dispatched when calling checkValidity(), it cannot be used for that. Also, the event has no additional info to distinguish between checkValidity() or reportValidity().

Is there a reason such a "reportValidity() hook" is missing from the standard? What's the idea behind checkValidity() triggering an invalid event? (if it didn't, then we could just use the invalid event for this)

In #5016 (comment) this was also mentioned and acknowledged that this is not possible.

@jpzwarte
Copy link
Author

jpzwarte commented Oct 24, 2023

Also, wrt FACE, the spec mentions a 3rd argument to internals.setValidity() called anchor. That is referred to as the "validation anchor" in the context of FACE. It is unclear however what it does/how it works. See https://html.spec.whatwg.org/multipage/custom-elements.html#form-associated-custom-elements

@jpzwarte
Copy link
Author

There is also a use case where a custom element might want to report a valid validity. That would not be possible with only the invalid event.

@sorvell
Copy link

sorvell commented Oct 24, 2023

I think the core issue here is that there does not appear to be a way to know when the browser will interactively validate the constraints of a form element. The invalid event is inadequate because it is called when the browser statically validates the contraints.

@jpzwarte
Copy link
Author

Here's a proposal: in https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#interactively-validate-the-constraints, insert the following steps between 2 and 3:

  1. Statically validate the constraints of form, and let unhandled invalid controls be the list of elements returned if the result was negative.
  2. If the result was positive, then return that result.
  3. Let unhandled reportable controls be an initially empty list of elements.
  4. For each element field in invalid controls, if any, in tree order:
    4.1. Let notCanceled be the result of firing an event named [report] at field, with the cancelable attribute initialized to true.
    4.2. If notCanceled is true, then add field to unhandled reportable controls.
  5. (was step 3) Report the problems with the constraints of at least one of the elements given in unhandled reportable controls to the user.
    etc.

That way a control can cancel the report event which will prevent the user agent from displaying the native popover. This is also backwards compatible with the current way client validation works (invalid event remains exactly the way it is).
When a control receives the report event, it knows it is time to display the validation message.

@keithamus keithamus added addition/proposal New features or enhancements needs implementer interest Moving the issue forward requires implementers to express interest topic: forms needs concrete proposal Moving the issue forward requires someone to figure out a detailed plan labels Oct 26, 2023
@keithamus
Copy link
Contributor

This looks like a good idea to me, and this is a bit of a sticking point around form validation; there isn't a great way to correctly suppress the browser native UI popovers.

@jimmyfrasche
Copy link

So as I understand it and to oversimplify:

  • the invalid event only fires when the element transitions from valid to invalid
  • the report event fires whenever the element enters an invalid state it was not previously in

I would also like to know when an element leaves a previously held invalid state.

Stopping the popovers is good but you also need to display the error message to the user. A common way is a list below the input. Knowing when there's a new kind of constraint violation would let you know when to add an item to the list but not when to remove it. If any change to the set of constraints violations fires an event you could add and remove and decouple the "display errors" component from the input element. (There is still the annoyance of only being able to set a single custom constraint violation per element but that should be a separate issue).

@jpzwarte
Copy link
Author

Not quite; there are 2 parts to client-side validation:

  1. https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#statically-validate-the-constraints
  2. https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#interactively-validate-the-constraints

The first step is the checkValidity() method. The second one is the reportValidity() method. When you call reportValidity(), then checkValidity() is run as well (behind the scenes probably). The spec describes that as part of the first step, the invalid event is dispatched. This problem is that the second step does not fire any type of event. This issue aims to solve that by firing a report event just before the element reports the invalid validity.

Once the report event has been received, then the recipient could enter a "report state" and directly show any new constraint violations. It would not have to wait for a new report event (but this behavior is up to you as component author). For example: say a component has a required and minlength constraint. If you enter nothing and you try to submit the form, then it would show the required validation message. Once you start typing, the required constraint would be satisfied and it would display the minlength validation message. Until you've satisfied that constraint, and then the component would just be valid.

@jpzwarte
Copy link
Author

I would also like to know when an element leaves a previously held invalid state.

That is up to you as component author. If you use ElementInternals, it is your job to set the validity by calling setValidity(). If you're wrapping an <input> element, then listening for the input event is normally the time to check if the validity has changed.

@jimmyfrasche
Copy link

I want to be able to have a component that only exists to report error messages and whose only coupling to the element (custom or otherwise) it reports on is the changes in the constraint validations which it uses to show/hide the corresponding error message.

For that to happen the error report element needs to know both when new constraint violations are triggered and when old constraint violations no longer apply. So if an element has 3 items and a minlength=4 there needs to be an event when the 4th item is added saying that tooShort went from true to false so that the corresponding error message may be hidden.

I'm not sufficiently skilled at reading specs to tell if this change would allow that or not.

@jpzwarte
Copy link
Author

@annevk @dominic any input/feedback on this perhaps?

@annevk annevk added the topic: custom elements Relates to custom elements (as defined in DOM and HTML) label Feb 13, 2024
@annevk
Copy link
Member

annevk commented Feb 13, 2024

@jpzwarte it sounds pretty reasonable to me to expose this. Another way of doing it might be that we don't make the other invalid event suggest it's cancelable, although that may be too subtle an API. However, that would be a pretty easy and likely web compatible change to make quickly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
addition/proposal New features or enhancements needs concrete proposal Moving the issue forward requires someone to figure out a detailed plan needs implementer interest Moving the issue forward requires implementers to express interest topic: custom elements Relates to custom elements (as defined in DOM and HTML) topic: forms
Development

No branches or pull requests

5 participants