Skip to content

The showSuccessLabels() steals focus due to showLabels() always focusing first field when focusInvalidField is true #170

@Anduil

Description

@Anduil

Describe the bug

Calling validator.showSuccessLabels({...}) causes focus to jump to the first field in the provided map, even though we’re rendering success labels (not errors). This happens because showLabels() unconditionally focuses the first field when globalConfig.focusInvalidField is true, regardless of whether we’re showing errors or success labels.

This breaks UX when you want to display a success hint for a specific field while the user is typing in a different field: the caret jumps unexpectedly.

Relevant code (from library source):

showLabels(fields: ShowLabelsInterface, isError: boolean): void {
  Object.keys(fields).forEach((fieldName, i) => {
    const error = fields[fieldName];

    const key = this.getKeyByFieldSelector(fieldName);
    if (!key || !this.fields[key]) {
      console.error(`Field not found. Check the field selector.`);
      return;
    }

    const field = this.fields[key];

    field.isValid = !isError;
    this.clearFieldStyle(key);
    this.clearFieldLabel(key);

    this.renderFieldError(key, false, error);

    // Problem: this runs even for success labels
    if (i === 0 && this.globalConfig.focusInvalidField) {
      setTimeout(() => field.elem.focus(), 0);
    }
  });
}

To Reproduce

  1. Initialize JustValidate with focusInvalidField: true.
  2. Set up two inputs, e.g. #a and #b. Focus and start typing into #b.
  3. Programmatically call validator.showSuccessLabels({ '#a': 'Looks good' }).
  4. Observe focus moves from #b to #a.

Minimal example:

  <form id="form">
    <input id="a" />
    <input id="b" />
  </form>
  <script>
    const v = new JustValidate('#form', {
      focusInvalidField: true
    }).addField('#a', [{
      validator: (value, context) => true,
    }]).addField('#b', [{
      validator: (value, context) => true,
    }]).onValidate((ctx) => {
      // Simulate showing a success label for #a while the user is on #b
      v.showSuccessLabels({
        '#a': 'Looks good'
      });
      // Caret jumps to #a unexpectedly
    });
    document.getElementById('b').focus();
  </script>

Expected behavior

  • showSuccessLabels() should not move focus.
  • Focusing should only occur when rendering errors and only if focusInvalidField is true.

Actual behavior

  • Focus jumps to the first field passed into showSuccessLabels() whenever focusInvalidField is true.

Screenshots

N/A (behavior is a visible focus jump/caret move).

Additional context

  • The focus line is guarded only by i === 0 && this.globalConfig.focusInvalidField, ignoring isError.
  • This also affects scenarios where we programmatically show success for one field based on another field’s change (common with cross-field rules).

Suggested enhancement

If desired, a separate option like focusOnSuccessLabels (default false) could be added.

In addition

The onValidate delegate does not provide a way to determine whether a field’s value has changed since the last validation. This is useful when you want to show success feedback only for fields edited since the previous successful validation.

Why this matters

  • Avoids noisy success labels for untouched fields.
  • Enables precise UX flows (e.g., “validate edited fields only”, show per-field success on change).
  • Helps with forms that do cross-field updates without revalidating the whole form.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions