Problem/Motivation

The links to the form elements with errors are (sometimes) wrong, they use non-existent anchors (ids on the page)

Steps to reproduce

  1. Install the Standard profile.
  2. Enable the inline_form_errors module.
  3. Create a content type.
  4. Add a required file upload field of type 'image' to the content type.
  5. Create new content of the content type
  6. To limit the validation errors give the node a title.
  7. Submit the form without uploading an image so that it doesn't pass validation for the required field.

Before
Inline form error message when no image uploaded and it is a required field

The inline form error link will have a fragment like #edit-field-image-0 (depending on what you named the field). But the form element that has the error has an ID like #edit-field-image-0-upload. The link does not have a valid target.

Proposed resolution

\Drupal\Core\Theme\ThemeManager::render() uses $element['#attributes']['id'] to send as an element id to the template

  if (isset($info['render element'])) {
      $key = $info['render element'];
      if (isset($variables[$key]['#attributes'])) {
        $variables['attributes'] = AttributeHelper::mergeCollections($variables['attributes'], $variables[$key]['#attributes']);
      }
    }

(at least for fieldset elements), while \Drupal\inline_form_errors\FormErrorHandler::displayErrorMessages() uses $form_element['#id'] to build the link. And they are not the same.
So we should use $form_element['#attributes']['id'] instead of $form_element['#id'] in \Drupal\inline_form_errors\FormErrorHandler::displayErrorMessages().

Remaining tasks

Create test coverage
Code review

User interface changes

N/A

Introduced terminology

N/A

API changes

N/A

Data model changes

N/A

Release notes snippet

N/A

Issue fork drupal-3557245

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

gease created an issue. See original summary.

gease’s picture

Title: Use $element['$attributes]['id'] instead of $element['#id'] to create a link anchor » Use $element['#attributes]['id'] instead of $element['#id'] to create a link anchor
quietone’s picture

Version: 11.2.x-dev » 11.x-dev

Hi, in Drupal core changes are made on on 11.x (our main development branch) first, and are then back ported as needed according to the Core change policies. Thanks.

santanu mondal’s picture

Working on it..

santanu mondal’s picture

Status: Active » Needs review
smustgrave’s picture

Status: Needs review » Needs work
Issue tags: +Needs tests

This will need a test for how it's an issue

oily’s picture

For the test coverage this form could be re-used:
core/modules/field_layout/tests/modules/field_layout_test/src/Form/EmbeddedForm.php

It could be re-used in a functional test in the inline_form_errors module.

The functional test could be a re-factor of:
core/modules/field_layout/tests/modules/field_layout_test/src/Form/EmbeddedForm.php

These ideas are based on the IS steps to reproduce.

oily’s picture

Issue summary: View changes

Applied the IS template. Updated remaining tasks.

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.

rkoller’s picture

I ran into the non-functioning anchor links as well. I went ahead and manually tested which fields are actually affected, and the problem applies to the media field and the image field. I've manually added the snippet in MR13771 to the FormErrorHandler.php, commented out line 99 ,and add the following snippet right afterward - and the MR definitely needs a rebase, i've added the tag for that

$id = $form_element['#attributes']['id'] ?? $form_element['#id'] ?? NULL;
$error_links[] = Link::fromTextAndUrl($title, Url::fromRoute('<none>', [], ['fragment' => $id, 'external' => TRUE]))->toRenderable();

With the snippet in place the anchor link worked for the media field but it still fails to work for the image field. On a related note, it looks like not every field type has a green focus outline in addition to the red outline for errors in Claro. The following field types only have the red outline for errors:

  • Formatted Long
  • Formatted Long with Summary
  • Media
  • File
  • Image
  • Selection Text (with checkbox/radio button display option)
  • Selection Integer (with checkbox/radio button display option)
  • DateTime
  • Startdate
  • Enddate
  • Timestamp

But unsure if the focus outline should be fixed within the scope of this issue or if it should go into a followup.

rkoller’s picture

Issue tags: +wcag247

and forgot to add the tag for the focus visible success criterion as well.

kentr’s picture

It would be helpful to have steps to reproduce that don't require a contrib module.

That might be obtainable from #11.

Might also be obtainable by taking from the STR in #3589057: setErrorByName uses wrong path to link. Those aren't clear, so they will need improvement.

In the meantime, I'll look into rebasing it.

smustgrave’s picture

Issue tags: -Needs tests, -Needs rebase

Rebased and add a test.

This one is weird where it's kinda a task kinda bug. Essentially we are switching from always using $element[#id] to using $element['#attributes] but still falling back to $element[#id] if not present.

smustgrave’s picture

Think this one is ready for review.

smustgrave’s picture

Status: Needs work » Needs review
mgifford’s picture

AI Problem Summary

Drupal's FormErrorHandler::displayErrorMessages() builds error summary links using $form_element['#id'], but the rendered form element receives its actual HTML `id` from $form_element['#attributes']['id']. These values can differ — particularly on media and image fields in cascading paragraph forms — causing error summary links to point to non-existent anchors. Keyboard and screen reader users cannot navigate to the errored field.

Target Description

Component: Error summary links in Inline Form Errors
Route: Any form with validation errors (e.g., /node/add/article with cascading paragraphs)
State: Form submitted with empty required fields (media/image fields)
Semantic target: ul > li > a[href="#edit-*"] (error summary links)
Actual rendered element: input[id="edit-*--<random>"] (different ID)
CSS fallback: .error-summary ul li a

Analysis

The bug is **functional**, not **presentational**. The screen reader reads the same DOM structure and link text regardless of whether the `href` points to a valid or invalid anchor. The broken anchor means:

- **Keyboard users:** Pressing Enter on the error link does nothing (no scroll/focus to the field)
- **Screen reader users:** Following the link does not move focus to the errored field
- **All users:** The navigation affordance is broken, but the announcement is the same

Current Behavior vs. Fixed Behavior

Aspect Without Patch With Patch
Error link href #edit-title #edit-title--abc123
Target element exists? No Yes
Keyboard navigation works? No Yes
Screen reader can reach field? No Yes
VSR announcement Same Same

Acceptance Criteria

After the fix:

  1. Error summary link `href` must point to the rendered element's actual ID ($form_element['#attributes']['id'])
  2. If $form_element['#attributes']['id'] is not set, fall back to $form_element['#id']
  3. Clicking/following the error link must move focus to the errored field
  4. Screen reader users must be able to navigate from the error summary to the errored field

References

dcam’s picture

Issue summary: View changes
Status: Needs review » Needs work
Issue tags: -Needs steps to reproduce

This was tagged as needing steps to reproduce the issue. Since no one provided any other than a vague paragraph of prose and a couple of hints, I had to work them out for myself. I don't actually know how accurate they are because what I came up with is not fixed by the solution in the MR. The issue persists even after applying the change. This may be due to an issue in the Image field or it could indicate that the change is incomplete. I don't know which.

oily’s picture

Re: #19, I manually reproduced the bug on a Drupal 11.4 site. I have added a comment to the code. As I state, the problem may be related to the bug mentioned by longwave in his code review. Is there an issue for that bug?

The fix is not currently working because $form_element['#attributes']['id'] is null.

oily’s picture

Issue summary: View changes

Updated the IS steps to reproduce. Removed references to the article content type since it no longer ships with the standard profile.

oily’s picture

oily’s picture

Issue summary: View changes
kentr’s picture

As I state, the problem may be related to the bug mentioned by longwave in his code review. Is there an issue for that bug?

I don't think it needs another issue. It looks like this line just needs to allow for the possibility of a 0 value.

$has_id = !empty($id);

"0" is a valid value for the idattribute, so $has_id should be TRUE when $id is 0.

It (edit: probably) should also allow for the possibility that $id is a string of spaces. Whitespace isn't allowed in selectors, but !empty(' ') evaluates to TRUE. $has_id should be FALSE in that case.

Edit: reference - https://html.spec.whatwg.org/multipage/dom.html#the-id-attribute

oily’s picture

Re: #24 But the line $id = $form_element['#attributes']['id'] ?? $form_element['#id'] ?? NULL;

precedes the line $has_id = !empty($id);

As I see it $has_id cannot fix anything because the damage has already been done. $form_element['#attributes']['id'] is null so the value that is being assigned is the next one: $form_element['#id'].

The value returned for the id by $form_element['#id'] lacks '-upload' on the end which it requires to make it the correct id value.

The solution seems to be to find a way to get $form_element['#attributes']['id'] to work correctly and produce the correct id including '-upload' on the end instead of returning null.

kentr’s picture

The value returned for the id by $form_element['#id'] lacks '-upload' on the end which it requires to make it the correct id value.

Ok. That sounds like a different problem than the bug @longwave described regarding 0.