12

I have a heavily themed form to integrate, whose structure is shown below. I am just about there for the most part except for the submit.

 <form action="#">
   <fieldset>
     <legend>Authentification</legend>
       <label for="email">Courriel*</label>
       <input type="text" name="email" id="email">
       <label for="password">Mot de passe*</label>
       <input type="password" name="password" id="password" class="last">
       <a href="#" title="Mot de passe oublié?" class="clearfix">Forgot password?</a>
       <button type="submit" class="clearfix"><span>Login</span></button>
   </fieldset>
 </form>

I have tried many different combinations, turns out button_type has no effect on core. So I used this hack, hoping it would fix my issue. Alas, it only changes the 'type' attribute (obviously), and not the element itself. The button type can hold other elements, in this case the span is needed to hold a background image, it needs to be in a span to stretch as the text in the button is dynamic.

Does anyone have a clue as to how I can generate the following line of markup using the form API?

<button type="submit" class="clearfix"><span>Login</span></button>
2
  • For Drupal 8 the submit button will become <botton type="submit">, see drupal.org/node/1671190 Nov 3, 2016 at 13:39
  • Wishful thinking, we are on D9 and still no button elements :(
    – Felix Eve
    Jan 7, 2021 at 21:22

8 Answers 8

12

In D7 would recommend:

$form['custom-form'] = array(
  '#prefix' => '<button type="submit">',
  '#suffix' => '</button>',
  '#markup' => '<span>' . t('Login') . '</span>',
);

That way you can replace the #markup in an alter function later if needed, without having to rebuild the button HTML.

1
  • This method woesn't support autocompete. Apr 18, 2015 at 13:35
18

As an addition, just in case someone runs into the same trouble as I did - when using the #markup or #prefix/#suffix trick on a form's actions group, the submit callback function won't be called at all, unless a submit type element is present. My workaround was like this:

$form['actions']['submit'] = array
(
    '#type' => 'submit',
    '#value' => '',
    '#attributes' => array( 'style' => array( 'display: none' )), // hide the input field
    '#submit' => array( 'my_callback_for_the_form_submit' ),
    '#prefix' => '<button type="submit" class="btn btn-primary">Add <i class="fa fa-plus-square-o">',
    '#suffix' => '</i></button>',
);

That way you can use custom HTML for submit action groups.

1
  • This was the best answer given... Jan 30, 2015 at 6:27
4

To add some custom tag you can use the following snippets:

// Drupal 6.
$form = array();

// Other elements.

$form['custom-form'] = array(
    '#value' => '<button type="submit" class="clearfix"><span>Login</span></button>',
);
// Drupal 7.
$form = array();

// Other elements.

$form['custom-form'] = array(
    '#markup' => '<button type="submit" class="clearfix"><span>Login</span></button>',
);
3
  • This did not really work but it did get me on trying '#markup' instead of #value, and that did the trick. Thanks bro, I appreciate. Oct 21, 2011 at 18:20
  • 1
    You did not informed about your Drupal version. #value is for Drupal6. #markup introduced in Drupal 7 Oct 21, 2011 at 18:27
  • Yes friend, my bad. I should've mentionned version number. Oct 21, 2011 at 20:11
2

Just for completeness I'll post an alternative solution that involves overriding theme_button (taken from this blog post)

First add a buttontype attribute to the form element:

$form['submit'] = array (
    '#type' => 'submit',
    '#buttontype' => 'button',
    '#value' => 'Search',
);

And then override theme button:

/**
 * Override of theme_button().
 *
 * Render the button element as a button and the submit element as an input element.
 */
function MYTHEME_button($variables) {
  $element = $variables['element'];
  $element['#attributes']['type'] = 'submit';

  element_set_attributes($element, array('id', 'name', 'value'));  

  $element['#attributes']['class'][] = 'form-' . $element['#button_type'];
  if (!empty($element['#attributes']['disabled'])) {
    $element['#attributes']['class'][] = 'form-button-disabled';
  }

  if (isset($element['#buttontype']) && $element['#buttontype'] == 'button') {
    $value = $element['#value'];
    unset($element['#attributes']['value']);
    return '<button' . drupal_attributes($element['#attributes']) . '>' . $value . '</button>';
  }
  else {
    return '<input' . drupal_attributes($element['#attributes']) . ' />';
  }
}

This creates problems however if there is more that one button in the form as Drupal is unable to detect which button has been clicked.

This can be addressed by adding an #after_build callback to the form:

$form['#after_build'][] = 'mymodule_force_triggering_element';

And then in the after build function:

function mymodule_force_triggering_element($form, &$form_state) {
  if (isset($form_state['input']['submit'])) {
    $form_state['triggering_element'] = $form['submit'];
  } elseif (isset($form_state['input']['other_button'])) {
    $form_state['triggering_element'] = $form['other_button'];
  }
  return $form;
}
2

Here is how I achieve this in Drupal 8. Basically I create a new theme suggestion so i can override the button with a custom twig file.

Add this code inside your mythemename.theme file:

/**
 * Add twig suggestions for input elements.
 *
 * If a form api element has a data-twig-suggestion attribute, then allow twig
 * theme override, add to suggestions.
 *
 * @param array $suggestions
 *   Current list of twig suggestions.
 * @param array $variables
 *   Every variable of the current element.
 */
function mythemename_theme_suggestions_input_alter(&$suggestions, array $variables) {
  $element = $variables['element'];

  if (isset($element['#attributes']['data-twig-suggestion'])) {
    $suggestions[] = 'input__' . $element['#type'] . '__' . $element['#attributes']['data-twig-suggestion'];
  }
}

In your code wherever you create your form, add a 'data-twig-suggestion' attribute to your submit button:

$form['submit'] = [
      '#type' => 'submit',
      '#value' => t('Submit') . ' >',
      '#attributes' => [
        'data-twig-suggestion' => 'button',
      ],
    ];

Now if you have twig debug enabled and you check the html source of your button on the site, you will see a new twig suggestion:

<!-- FILE NAME SUGGESTIONS:
   * input--submit.html.twig
   * input--submit--button.html.twig
   x input.html.twig
-->

Now you can create a input--submit--button.html.twig file (I place this in mythemename/templates/form_elements but you can place it elsewhere if you like):

<button{{ attributes }} type='submit'>
    <span class="great-success">Submit</span>
</button>
1

I tried Óscar Gómez Alcañiz's answer but my form was still not getting submitted. As a workaround I altered his solution so the input was sitting over top of the button but was transparent:

$form['actions']['submit'] = array (
    '#type' => 'submit',
    '#value' => '',
    '#attributes' => array( 'style' => 'position: absolute; left: 0; right: 0; top: 0; bottom: 0; border: none; opacity: 0; width: 100%;'), // put input field over the top of button and make transparent
    '#prefix' => '<button type="submit" class="btn btn-primary">Add <i class="fa fa-plus-square-o">',
    '#suffix' => '</i></button>',
);

This way the actual input[type="submit] is getting clicked and triggering the action but the button

Probably a good idea to put all that CSS in a stylesheet in real life but just put in the inline style tag here as an example.

0

Keeping with the forms api and rendering a button, html_tag type does the trick:

https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Render%21Element%21HtmlTag.php/class/HtmlTag

https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme_html_tag/7.x

$form['my_button'] = [
  '#type' => 'html_tag',
  '#tag' => 'button',
  '#value' => t('My Button'),
  '#attributes' => $attributes,
];
-3

More correct way is:

$form['submit'] = array(
  '#type' => 'button',
  '#value' => '<span>Login</span>',
);

It produces valid HTML like this:

<button value="&lt;span&gt;Login&lt;/span&gt;" type="submit">
    <span>Login</span>
</button>

...and this method doesn't brake autocomplete and other features.

5
  • 1
    It does not return a <button> tag, at least in D7. The last line of theme_button() in includes/form.inc is return '<input' . drupal_attributes($element['#attributes']) . ' />';
    – daniels
    May 7, 2015 at 9:02
  • Could you please recheck? I've copied this code from my working custom module. May 10, 2015 at 17:17
  • If it works for you it means you've overridden theme_button in a custom theme or module. daniels is right.
    – Felix Eve
    Jun 26, 2015 at 0:31
  • @FelixEve, correct! I've overriden the button in custom function. Is there any othere method to do this without custom function? Jun 27, 2015 at 9:53
  • This thread has a good overview of all of the methods available.
    – Felix Eve
    Jun 29, 2015 at 1:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.