When two buttons are added to a form, one vanilla HTML button and one image button, $form_state['triggering_element'] is always the image button regardless of which button is clicked. If both buttons are HTML buttons then the triggering element is set properly. If both buttons are image buttons, the last button in the form will be the triggering element. This is true when using both ajax and normal submit.

In order to demonstrate, I have created a content type called "test" and used the following code for the buttons and their ajax callbacks.

/**
 * Implements hook_form_FORM_ID_alter().
 */
function mymodule_form_test_node_form_alter(&$form, &$form_state, $form_id) {
  $form['html_button'] = array(
    '#type' => 'button',
    '#value' => 'HTML button',
    '#ajax' => array(
      'callback' => 'mymodule_button_html_callback',
      'wrapper' => 'mymodule_replace',
    ),
  );

  $form['image_button'] = array(
    '#type' => 'image_button',
    '#value' => 'Image button',
    '#src' => drupal_get_path('module', 'mymodule') . '/image.jpg',
    '#ajax' => array(
      'callback' => 'mymodule_button_image_callback',
      'wrapper' => 'mymodule_replace',
    ),
  );

  $form['#prefix'] = '<div id="mymodule_replace">';
  $form['#suffix'] = '</div>';
}

/**
 * Callback function for the HTML button's ajax event.
 */
function mymodule_button_html_callback(&$form, &$form_state) {
  drupal_set_message('HTML button');
  return $form;
}

/**
 * Callback function for the image button's ajax event.
 */
function mymodule_button_image_callback(&$form, &$form_state) {
  drupal_set_message('Image button');
  return $form;
}

Issue fork drupal-1452894

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

interx’s picture

wojtha’s picture

Title: Image button is always the triggering element » Elements with #has_garbage_value and #value are always are always set as triggering element
Version: 7.12 » 7.x-dev
Issue tags: +DrupalWTF

The workaround is to get remove #value property of image_button. If it exist, there is a bug in the Drupal form processing, specifically in the _form_button_was_clicked() function: According to this function, every element with non-empty '#has_garbage_value' property (like the image_button) and with non-empty #value is considered to be a "triggering element". If there are multiple elements of that kind in the form, the last one is picked up.

function _form_button_was_clicked($element, &$form_state) {
  // First detect normal 'vanilla' button clicks. Traditionally, all
  // standard buttons on a form share the same name (usually 'op'),
  // and the specific return value is used to determine which was
  // clicked. This ONLY works as long as $form['#name'] puts the
  // value at the top level of the tree of $_POST data.
  if (isset($form_state['input'][$element['#name']]) && $form_state['input'][$element['#name']] == $element['#value']) {
    return TRUE;
  }
  // When image buttons are clicked, browsers do NOT pass the form element
  // value in $_POST. Instead they pass an integer representing the
  // coordinates of the click on the button image. This means that image
  // buttons MUST have unique $form['#name'] values, but the details of
  // their $_POST data should be ignored.
  elseif (!empty($element['#has_garbage_value']) && isset($element['#value']) && $element['#value'] !== '') { // <== HERE
    return TRUE;
  }
  return FALSE;
}

I'm currently trying this fix:

- elseif (!empty($element['#has_garbage_value']) && isset($element['#value']) && $element['#value'] !== '') {
+ elseif (!empty($element['#has_garbage_value']) && isset($form_state['input'][$element['#name'] . '_x']) && isset($form_state['input'][$element['#name'] . '_y'])) {

So the current workaround is to not use the #value property. However this property is allowed in the FAPI docs and thus this is kind of a Drupal WTF - an unexpected behavior.

wojtha’s picture

Status: Active » Needs review
StatusFileSize
new1.25 KB
new564 bytes

Fix for D7. I'll post D8 patch later. It seems that FAPI doesn't change in that case.

The first patch just extends the test and is expected to fail.

wojtha’s picture

Title: Elements with #has_garbage_value and #value are always are always set as triggering element » Elements with #has_garbage_value and #value are always set as a triggering element

Title correction.

damien tournoud’s picture

The code looks correct. Why do you except it to behave differently?

wojtha’s picture

Version: 7.x-dev » 8.x-dev
StatusFileSize
new1.35 KB
new645 bytes

Patch against D8. The first patch file just extends the test module and is expected to fail.

damien tournoud’s picture

See form_type_image_button_value(), that transform the (kind of browser-specific) submit (as far as I know you can find both element_name and element_name.x, element_name.y, the latter actually being the HTML5 pick). As far as I understand, this code is perfectly correct. If an image element has a value, it is considered to be submitted.

wojtha’s picture

The code looks correct. Why do you except it to behave differently?

Maybe I've explained it wrong. The current - buggy - behavior of Drupal form processing is unexpected, because the "#value" is allowed property according to API, but the _form_button_was_clicked function doesn't count with that...

wojtha’s picture

If an image element has a value, it is considered to be submitted.

Ok, I see your point. You're right, but it is not very DX friendly as the rest two types of "buttons" have that property. It is confusing. It happend to me today and to many people before (according to this and some other issue queues), plus there is no single word about that behavior in the Forms API.

So at least this is a bug in the documention and (IMHO) DX issue.

David_Rothstein’s picture

This is very similar to #873070: When an image button appears after another button in a form, the wrong triggering element and #submit handlers are detected, which is older. Both have patches (with similar code changes in them), although this one is farther along in some ways and the other is farther along in other ways.

Should we close this issue and consolidate all work over there?

damien tournoud’s picture

Status: Needs review » Needs work

Ok, I see your point. You're right, but it is not very DX friendly as the rest two types of "buttons" have that property. It is confusing. It happend to me today and to many people before (according to this and some other issue queues), plus there is no single word about that behavior in the Forms API.

I agree that this is confusing. But the real problem is that we are using #value for submit and button in the first place. We should really be #default_value or similar in that case...

One simple thing we could do (and backport) is to use #value as the value of the alt="" attribute for image buttons. That way the behavior of image buttons is the same as other types of buttons (aka. #value is the textual representation of the button).

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.

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.

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.

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

Drupal 8.4.4 was released on January 3, 2018 and is the final full bugfix release for the Drupal 8.4.x series. Drupal 8.4.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.5.0 on March 7, 2018. (Drupal 8.5.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.5.x-dev branch from now on, and new development or disruptive changes should 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.

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

Drupal 8.5.6 was released on August 1, 2018 and is the final bugfix release for the Drupal 8.5.x series. Drupal 8.5.x will not receive any further development aside from security fixes. Sites should prepare to update to 8.6.0 on September 5, 2018. (Drupal 8.6.0-rc1 is available for testing.)

Bug reports should be targeted against the 8.6.x-dev branch from now on, and new development or disruptive changes should 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.6.x-dev » 8.8.x-dev

Drupal 8.6.x will not receive any further development aside from security fixes. Bug reports should be targeted against the 8.8.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.9.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.

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

Drupal 8.8.7 was released on June 3, 2020 and is the final full bugfix release for the Drupal 8.8.x series. Drupal 8.8.x will not receive any further development aside from security fixes. Sites should prepare to update to Drupal 8.9.0 or Drupal 9.0.0 for ongoing support.

Bug reports should be targeted against the 8.9.x-dev branch from now on, and new development or disruptive changes should 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.

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

Drupal 8 is end-of-life as of November 17, 2021. There will not be further changes made to Drupal 8. Bugfixes are now made to the 9.3.x and higher branches only. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.2.x-dev » 9.3.x-dev
amber himes matz’s picture

Issue summary: View changes
Status: Needs work » Postponed (maintainer needs more info)
Issue tags: +Bug Smash Initiative

Is this still a point of confusion? Should the issue be revived for Drupal 9?

amber himes matz’s picture

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.15 was released on June 1st, 2022 and is the final full bugfix release for the Drupal 9.3.x series. Drupal 9.3.x will not receive any further development aside from security fixes. Drupal 9 bug reports should be targeted for the 9.4.x-dev branch from now on, and new development or disruptive changes should be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.9 was released on December 7, 2022 and is the final full bugfix release for the Drupal 9.4.x series. Drupal 9.4.x will not receive any further development aside from security fixes. Drupal 9 bug reports should be targeted for the 9.5.x-dev branch from now on, and new development or disruptive changes should be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

smustgrave’s picture

Status: Postponed (maintainer needs more info) » Closed (outdated)

Thank you everyone for this.

Closing as outdated as there hasn't been a follow up for #22

If still a bug please reopen updating issue summary for D10

Thanks!

mlncn’s picture

Version: 9.5.x-dev » 11.x-dev
Status: Closed (outdated) » Needs work

This is absolutely still a bug in Drupal 11.

Button Field module documents:

Important note

Due to #1452894: Elements with #has_garbage_value and #value are always set as a triggering element you can not use image buttons on edit forms when there are any other buttons. Non-editable displays are unaffected.

And where i'm coming from, i confidently told Gin in #3539394: Image buttons cannot be kept in Gin's sticky form actions that core's image button form element is a valid alternative to a standard submit input form element:

As noted in #3359619: UX when using gin with Content Moderation and Workflows, Workflow Buttons module pairs very well with Gin for improving the experience of using content moderation workflows, and the 2.0.x branch of Workflow Buttons' "Trash workflow" submodule switches to using the image button to have a trash icon for the soft-delete, similar to the trash icon Claro/Gin add to core's delete link. […]

But in short swapping the submit button out and substituting an image button for it works, and Gin should support this core form element when adding Gin attributes and looking for the #gin_action_item flag.

And i get it looking perfect and then discover that no matter which button i press, the image button (soft delete!) is the one that gets incorrectly identified as the trigger button.

mlncn’s picture

And it is not a duplicate with changing values client side; although that may be related, this is being caused with alteration in a Drupal form/widget alter.

mlncn’s picture

Title: Elements with #has_garbage_value and #value are always set as a triggering element » Elements with #has_garbage_value (image buttons) are always set as a triggering element
mlncn’s picture

Essentially the fix by wojtha in this issue 13 years ago and richthegeek and cvangysel in #873070: When an image button appears after another button in a form, the wrong triggering element and #submit handlers are detected 14 years ago remains correct and needed.

When image buttons are clicked, browsers do not set NAME in the POST, instead setting NAME_x and NAME_y (with x and y the coordinates of the click). To get the clicked button we need to test for these parameters instead of the presence of value— that is, equally . (Damien's suggestion of using #value for image buttons also does not work, whether the button is pressed or not, whether it has a value or not, the value gets set anyway, and set to "true", presumably because of how image buttons are treated by the browser— the only thing that is safe to check are if coordinates were set.)

And yes (replying to 13 year old comments from another issue now, but the same feedback will come up) these changes make the alternative condition specific to image buttons, but i'm fairly certain that is because that is all there is— the only other element in core which uses #has_garbage_value is Radios, which aren't ever going to be triggering a form submission.

So, yeah, image buttons are ridiculous and Drupal core could totally deprecate them and not support them since basically nobody has cared for a decade (especially if we finally do #1671190: Use <button /> form element type instead of <input type="submit" />), but as long as we have ImageButton extending Submit we need to support it properly as an alternative to Submit, that works alongside Submit buttons.

The merge request is ready and works, but leaving Needs work as it needs a test showing that this fails even when value is not set in the initial form element definition. As my use case is altering an existing button there is some possibility that something about that is adding 'value' back, but definitely unsetting value is not enough.

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.