As a followup to #1414510: Remove drupal_reset_static for drupal_html_id() when form validation fails, it seems there is a similar bug where you have two forms on a page, and then submit one successfully resulting in a form rebuild (with the page being re-rendered).

There may be other similar duplicate ID bugs as well; this is just the easiest to spot one that I noticed.

Related issues:
#1575060: ajax_html_ids are broken for forms with file element (encoding=multipart/form-data)
#1305882: drupal_html_id() considered harmful; remove ajax_html_ids to use GET (not POST) AJAX requests

CommentFileSizeAuthor
#73 1831560-73.patch5.8 KBLendude
#73 interdiff-1831560-71-73.txt722 bytesLendude
#71 1831560-71.patch5.79 KBmrinalini9
#61 interdiff-1831560-58-61.txt864 bytesacbramley
#61 1831560-61.patch5.71 KBacbramley
#58 interdiff-1831560-51-58.txt2.01 KBacbramley
#58 1831560-58.patch5.71 KBacbramley
#51 1831560-51.patch5.66 KBharsha012
#49 1831560-49.patch5.62 KBharsha012
#41 drupal-html-ids-1831560-41-D7.patch1.73 KBCRZDEV
#38 drupal-html-ids-1831560-38-D7.patch1.72 KBCRZDEV
#35 drupal-html-ids-1831560-35-D7.patch617 bytesCRZDEV
#33 interdiff.txt6.18 KBmvwensen
#31 views-8.3.x-exposed_filters_unique_ids-1831560-31.patch5.69 KBmvwensen
#29 views-8.3.x-exposed_filters_unique_ids-1831560-29.patch3.71 KBmvwensen
#26 interdiff.txt1.62 KBacbramley
#26 1831560-26.patch5.33 KBacbramley
#24 1831560-24.patch3.71 KBacbramley
#21 1831560-21.patch1.69 KBacbramley
#14 d7-form-html-id-1831560-14.patch2.57 KBDevin Carlson
#9 d8_form_html_id.patch2.59 KBfago
#7 d7_form_html_id.patch2.78 KBfago
#2 form-rebuild-html-ids-1831560-2.patch2.91 KBDavid_Rothstein
#1 form-rebuild-html-ids-1831560-1-TESTS-ONLY.patch1.92 KBDavid_Rothstein
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

David_Rothstein’s picture

Status: Active » Needs review
FileSize
1.92 KB

Here is a patch with a test that should expose the bug.

David_Rothstein’s picture

And one with a possible fix.

Essentially, I'm pretty suspicious about the code in the form API which resets HTML IDs when a form is being processed, so I think we might just consider removing it.

It was added a long time ago in #111719: FAPI fails to check auto-generated IDs for uniqueness - XHTML validation fails. and as the code comments explain, its purpose is to avoid needlessly incrementing IDs when a form is built twice on the same page request but only displayed once. However, that seems like an unusual and unideal situation to begin with (especially with the current form API), and even if it ever does happen the worst we get is some needlessly incremented IDs, which these days isn't a big deal really since you already can't rely on auto-generated IDs having a specific value in CSS and JavaScript anyway.

So, let's see what the testbot thinks about totally removing that code.

sun’s picture

Title: Duplicate HTML IDs on a page when a form is rebuilt » Remove form ID auto-increment entirely (was: Duplicate HTML IDs on a page when a form is rebuilt)

Clarifying issue title based on #2

Status: Needs review » Needs work

The last submitted patch, form-rebuild-html-ids-1831560-2.patch, failed testing.

fago’s picture

oh, I just realized I opened a duplicate: #2034631: Remove drupal_reset_static for drupal_html_id() during form processing
Besides some tests would need to be addapted I'm not sure it's a simple fix to roll out for d7 though, as it will change HTML-IDs for some folks.

Here is my post from there:

So right now, form API resets drupal_html_id() IDs each time a form is processed (and there are no validation errors, thanks to #1414510: Remove drupal_reset_static for drupal_html_id() when form validation fails ). However, this is problematic if
- forms a processed during an ajax request. We do a lot of work to add in all past html IDs, increase the payload of ajax requests to have them, and then we reset the html IDs before the form is rendered? That's dumb.
- if multiple forms are used on page and some of them are always processed - this is a supported form API feature and used by exposed filter forms of Views:

 *   - always_process: If TRUE and the method is GET, a form_id is not
 *     necessary. This should only be used on RESTful GET forms that do NOT
 *     write data, as this could lead to security issues. It is useful so that
 *     searches do not need to have a form_id in their query arguments to
 *     trigger the search.

So once this feature is used you have a reset of your html IDs somewhere in the rendering pipeline (where the exposed views form is displayed), what in my case resulted in duplicate IDs and consequently in bizarre JS errors due to wrongly merged AJAX command settings (those are keyed by a now not more unique ID).

    // drupal_html_id() maintains a cache of element IDs it has seen,
    // so it can prevent duplicates. We want to be sure we reset that
    // cache when a form is processed, so scenarios that result in
    // the form being built behind the scenes and again for the
    // browser don't increment all the element IDs needlessly.
    if (!form_get_errors()) {
      // In case of errors, do not break HTML IDs of other forms.
      drupal_static_reset('drupal_html_id');
    }

So this creates troubles, and that "just" for being nice and not unnecessarily incrementing element IDs? Furthermore, unnecessarily incrementing the IDs will only happen if you process forms behind the scenes what is generally not best practice as you should use API functions instead.

So let's remove that troublesome reset.

fago’s picture

Title: Remove form ID auto-increment entirely (was: Duplicate HTML IDs on a page when a form is rebuilt) » Remove drupal_reset_static for drupal_html_id() during form processing

It's about HTML-ids not $form_id though - so updating title.

fago’s picture

Status: Needs work » Needs review
FileSize
2.78 KB

So this creates troubles, and that "just" for being nice and not unnecessarily incrementing element IDs?

As tests have shown this is not just for being nice - I guess the comment was not clear to me here. So this patch makes sure we keep the html IDs during rebuilds, but instead of just resetting we better revert them to the previous state.

Status: Needs review » Needs work

The last submitted patch, d7_form_html_id.patch, failed testing.

fago’s picture

Status: Needs work » Needs review
FileSize
2.59 KB

Patch of #7 was for Drupal 7. This one is for Drupal 8.

The last submitted patch, d8_form_html_id.patch, failed testing.

Anonymous’s picture

Issue summary: View changes

Updated issue summary.

holdmann’s picture

Patch of #7 works not as expected.

If we create views with 2 exposed forms, which of that has similar element id with different values (f.g. two node types use two taxonomy vocabularies, each of them consists brands. So field identifier obviosly should be brand_id or similar...).

All time form is submitted user will see 'An illegal choice has been detected. Please contact the site administrator.' message, despite of his selection.

This srting always returns true (form.inc line 1348)
elseif (!isset($options[$elements['#value']])) {
because $options consists elements of another form. But i haven't no idea why.

Any suggestions?

guschilds’s picture

Issue summary: View changes

Patch #7 worked for me with Drupal 7.

I had multiple exposed forms on the same page and having the same item in multiple forms was causing problems. 'drupal_html_id' was being reset between each form, so I was not able to use it to create unique item IDs. With this patch, I was able to and my the forms work as expected.

Keeping the status as "Needs work" due to failed testing.

tim.plunkett’s picture

Title: Remove drupal_reset_static for drupal_html_id() during form processing » Remove Html::resetSeenIds() call during form processing
Devin Carlson’s picture

Version: 8.0.x-dev » 7.x-dev
Status: Needs work » Needs review
Related issues: +#2555481: Uncaught TypeError: Cannot read property 'form' of undefined - error commons 3.29
FileSize
2.57 KB

The patch in #7 works for me as well. Attaching a reroll; I'll move back to D8 after a test run.

The changes to autocomplete in Drupal 7.39 surfaced this issue in Drupal Commons. Commons provides a "browsing widget" which is a set of jQuery UI tabs, where each tab contains an AJAX view displaying nodes of a specific content type. Each content type view displays a form in the header which allows users to create a new node of the same type.

Many of the forms contain the same fields (title, tags, associated organic groups) which all receive the same IDs. While this is invalid HTML, with the autocomplete changes, having one or more autocomplete-enabled fields now results in an Uncaught TypeError: Cannot read property 'form' of undefined error which causes all JS to fail.

Status: Needs review » Needs work

The last submitted patch, 14: d7-form-html-id-1831560-14.patch, failed testing.

Devin Carlson’s picture

Version: 7.x-dev » 8.0.x-dev
dawehner’s picture

This is now about the call to Html::resetSeenIds();

Version: 8.0.x-dev » 8.1.x-dev

Drupal 8.0.6 was released on April 6 and is the final bugfix release for the Drupal 8.0.x series. Drupal 8.0.x will not receive any further development aside from security fixes. Drupal 8.1.0-rc1 is now available and sites should prepare to update to 8.1.0.

Bug reports should be targeted against the 8.1.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.1.x-dev » 8.2.x-dev

Drupal 8.1.9 was released on September 7 and is the final bugfix release for the Drupal 8.1.x series. Drupal 8.1.x will not receive any further development aside from security fixes. Drupal 8.2.0-rc1 is now available and sites should prepare to upgrade to 8.2.0.

Bug reports should be targeted against the 8.2.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

acbramley’s picture

Just tracked this issue down after an accessibility review.

Caused by 2 views exposed filters on the same page. During \Drupal\Core\Form\FormBuilder::processForm of the second form the Html::resetSeenIds() call clears the "edit-actions" id used as the container id for the actions element.

acbramley’s picture

Here's a failing test. Let me know if this is completely in the wrong place!

andypost’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 21: 1831560-21.patch, failed testing.

acbramley’s picture

Status: Needs work » Needs review
FileSize
3.71 KB

Following what the patch in #9 was doing (moving the reset into rebuildForm) I've whipped up this. It makes the test in #21 pass and the rest of the form builder tests pass but lets see what else breaks.

Status: Needs review » Needs work

The last submitted patch, 24: 1831560-24.patch, failed testing.

acbramley’s picture

Fixes test failures

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.6 was released on February 1, 2017 and is the final full bugfix release for the Drupal 8.2.x series. Drupal 8.2.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.3.0 on April 5, 2017. (Drupal 8.3.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.3.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

sylvainar’s picture

Hey !

I'm currently experimenting the same problem on a project I'm on. The patch seems great but it doesn't pass on 8.3, the version I'm using. I'll try to modify it in order to import your tests too.

Is this is going to be merged? Few people actually seems to care, but this problem is a real accessibility killer…

mvwensen’s picture

Manually applied patch #26 onto the 8.3.x branch, recreated a patch that applies on 8.3.x. Please review and fix if necessary!

Status: Needs review » Needs work
mvwensen’s picture

New patch added, let's see if the tests work this time.

acbramley’s picture

@mvwensen nice! Can you provide an interdiff please?

mvwensen’s picture

FileSize
6.18 KB

Here you go!

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.6 was released on August 2, 2017 and is the final full bugfix release for the Drupal 8.3.x series. Drupal 8.3.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.4.0 on October 4, 2017. (Drupal 8.4.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.4.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Status: Needs review » Needs work

The last submitted patch, 35: drupal-html-ids-1831560-35-D7.patch, failed testing. View results

tunic’s picture

Not sure but it seems disk was full:

...
11:00:48 Checks display of aggregator items 227 passes, 0 fails, and 0 exceptions
12:43:09 Build timed out (after 110 minutes). Marking the build as aborted.
12:43:09 Build was aborted
12:43:09 ERROR: Step ‘Archive the artifacts’ failed: no workspace for drupal_d7 #45453
12:43:09 Checking console output
...

Trying to enqueue it again.

#EDIT: First try yes, but second try disk was ok. Two errors detected, see https://www.drupal.org/pift-ci-job/800872.

CRZDEV’s picture

Status: Needs work » Needs review
borisson_’s picture

Status: Needs review » Needs work
  1. +++ b/includes/form.inc
    @@ -503,6 +503,16 @@ function drupal_rebuild_form($form_id, &$form_state, $old_form = NULL) {
    +  // drupal_html_id() maintains a cache of element IDs it has seen,
    +  // so it can prevent duplicates. We want to be sure we reset that
    +  // cache when a form is processed, so scenarios that result in
    +  // the form being built behind the scenes and again for the
    +  // browser don't increment all the element IDs needlessly.
    

    I know that this commend is just copy pasted, but it should be reflowed to be closer to 80 cols. It should also start with a capital letter, meaning that the first sentence should be different (because the we can't have the function start with a capital).

  2. +++ b/includes/form.inc
    @@ -503,6 +503,16 @@ function drupal_rebuild_form($form_id, &$form_state, $old_form = NULL) {
    +  if (!form_get_errors()) {
    +  // In case of errors, do not break HTML IDs of other forms.
    +    drupal_static_reset('drupal_html_id');
    +  }
    

    The indentation here looks off.

CRZDEV’s picture

Status: Needs work » Needs review
FileSize
1.73 KB

borisson_ here goes new patch with the suggested improvements, thanks for the review.
Any other suggestion is welcome.

borisson_’s picture

Status: Needs review » Reviewed & tested by the community

Awesome, looks good!

PS: Next time, please provide an interdiff as well as a patch to make reviewing easier.

CRZDEV’s picture

Sure, thanks!

catch’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs tests

This should have test coverage if at all possible. Looks like there's rough tests already on the issue that didn't make it into the final patch.

tim.plunkett’s picture

Version: 8.4.x-dev » 8.5.x-dev

Also, the patches since #31 have been for D7.
Hopefully the work done since then can be salvaged and put back into a workable D8 patch.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

alzz’s picture

Applied #41 patch on 7.56 and works. As it was moved to "Reviewed & tested by the community" and the moved to "Needs work" for Drupal 8 I think we should include this fix in Drupal 7 and keep going with the Drupal 8 approximation.

harsha012’s picture

Status: Needs work » Needs review
FileSize
5.62 KB

re-rolled the patch to 8.5.x

Status: Needs review » Needs work

The last submitted patch, 49: 1831560-49.patch, failed testing. View results

harsha012’s picture

Status: Needs work » Needs review
FileSize
5.66 KB

fixed the minor glitch

andypost’s picture

Gábor Hojtsy’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: -Needs tests

How can we be sure that there are not unintended side effects? Are we testing some (other) scenarios of form ID names (already)?

  1. +++ b/core/lib/Drupal/Core/Form/FormBuilder.php
    @@ -378,6 +378,16 @@ public function rebuildForm($form_id, FormStateInterface &$form_state, $old_form
    +    ¶
    +    // \Drupal\Component\Utility\Html::getUniqueId() maintains a cache of
    
    +++ b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php
    @@ -483,6 +483,36 @@ public function testUniqueHtmlId() {
    +    $form_arg_2 = $this->getMock('Drupal\Core\Form\FormInterface');  ¶
    +    $form_arg_2->expects($this->exactly(1))
    

    Minor: whitespace issues in these two hunks.

  2. +++ b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php
    @@ -483,6 +483,36 @@ public function testUniqueHtmlId() {
    +   * Tests that HTML IDs are unique when building 2 forms with the same element.
    

    It is about elements with the same name, not necessarily the same element, right? Eg. they could be entirely different element types, etc.

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Couttsy’s picture

I'm experiencing this problem now and it's a real accessibility killer. Is there any hotfix that I can use to resolve it? I'm on the current version of Drupal 8. For how long this issue has been open it really makes me think nobody cares about this problem.

acbramley’s picture

@Couttsy there are patches in this issue that you can use to fix it. E.g the patch in #51.

acbramley’s picture

Status: Needs work » Needs review
FileSize
5.71 KB
2.01 KB

Rerolled, fixed whitespace and comment issues from #53 and fixed deprecated calls to getMock()

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Status: Needs review » Needs work

The last submitted patch, 58: 1831560-58.patch, failed testing. View results

acbramley’s picture

Status: Needs work » Needs review
FileSize
5.71 KB
864 bytes

Fixed failure in #58, it looks like the ids went back to their previous edit-type id, but given that it's obviously flakey to rely on the id, I've changed it to use the select label.

larowlan’s picture

Status: Needs review » Reviewed & tested by the community

Looks good

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 61: 1831560-61.patch, failed testing. View results

Lendude’s picture

Status: Needs work » Reviewed & tested by the community

Random media fail, back to RTBC

Lendude’s picture

catch’s picture

+++ b/core/lib/Drupal/Core/Form/FormBuilder.php
@@ -385,6 +385,16 @@ public function rebuildForm($form_id, FormStateInterface &$form_state, $old_form
+    // result in the form being built behind the scenes and again for the
+    // browser don't increment all the element IDs needlessly.
+    if (!FormState::hasAnyErrors()) {
+      // In case of errors, do not break HTML IDs of other forms.
+      Html::resetSeenIds();
+    }
+

Pre-existing issue with the comment, or maybe due to the context change? The condition is that the form doesn't have any errors, but the comment is 'in case of errors'.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Lendude’s picture

@catch I think the comment is correct, if somewhat clumsily worded. This comes from #1414510: Remove drupal_reset_static for drupal_html_id() when form validation fails where there are screenshots showing that the reset of ID's causes a break when done when there are errors.
So the comment should probably be something along the lines of "We only reset HTML ID's when there are no validation errors as this can cause ID collisions with other forms on the page otherwise.", but that should probably be follow-up?

catch’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs reroll

Patch no longer applies. Ideally whoever re-rolls this could also make the comment change suggested in #68, or whoever commits this could do it on commit. Don't think we need a separate issue really but also don't want to hold this up any more.

mrinalini9’s picture

Assigned: Unassigned » mrinalini9
mrinalini9’s picture

Assigned: mrinalini9 » Unassigned
Status: Needs work » Needs review
FileSize
5.79 KB

Rerolled patch for 9.1.x along with the changes suggested in #68, please review.

acbramley’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: -Needs reroll

Back to RTBC

Lendude’s picture

Quick clean up for the comment being over 80 chars, leaving RTBC

  • catch committed bc62d34 on 9.1.x
    Issue #1831560 by acbramley, CRZDEV, mvwensen, fago, David_Rothstein,...

  • catch committed 2c4697f on 9.0.x
    Issue #1831560 by acbramley, CRZDEV, mvwensen, fago, David_Rothstein,...

  • catch committed 9800cd5 on 8.9.x
    Issue #1831560 by acbramley, CRZDEV, mvwensen, fago, David_Rothstein,...
catch’s picture

Version: 9.1.x-dev » 8.9.x-dev
Status: Reviewed & tested by the community » Fixed

Committed/pushed to 9.1.x and cherry-picked through to 8.9.x, thanks!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

Krzysztof Domański’s picture

#3169918: data-drupal-selector isn't stable since 8.9.0
HTML IDs have been changed. Do we need a Change Record?