Here's a simple task.

For accessibility reasons, from a custom module I need to perform alterations on textfield based form elements (such as email, textfield, etc ...) on custom forms displayed to front users.

These forms extends the Drupal\Core\Form\FormBase class.

The alterations I need to perform would be for exemple to automatically and programmatically copy the value of the field's placeholder text and put it in the field's title attribute (not the field's label, I'm talking about the HTML title attribute).

So the field's markup would look something like this :

<input type="email" placeholder="Email" title="Email" />

I want to check all custom forms for the entire website.

I tried different methods :

  • hook_field_widget_form_alter : doesn't fire for fields on custom forms because they are not widgets.
  • hook_preprocess_form_element : It's too late to perform these changes. The values just won't take effect on the field.
  • hook_form_alter : Well it's just too general, I find it a bit dodgy to parse the entire Render Array, check for fields, etc ... but may be it is the only way..

I looked into the Form API, Render API, Plugins API, I looked into services, nothing seems to offer any solution, unless of course I missing something, and I probably do.

Has anyone any idea on how to achieve this in the best Drupal 8 way?

  • Please don't add [solved] to the title and post an answer in the question body, you're able to answer your own questions here if you have a resolution. Thanks!
    – Clive
    Mar 10, 2017 at 11:57

3 Answers 3


In hook_template_preprocess_form_element the (input-)tags is already rendered. You can use

function hook/template_preprocess_input(&$variables) {
  $variables['attributes']['title'] = 'my-title';
  $variables['attributes']['placeholder'] = 'my-placeholder';

to alter the input tag. use the same method for all other desired tags. Here's a nice tutorial.

You could make use of hook/template_preprocess to go a more generic way.

  • I don't want to have the code in my theme. I want to generate a module for generic accessibility use cases. Mar 10, 2017 at 10:44
  • use hook_ (in modules) or template_ (in themes) - not matter here ;)
    – rémy
    Mar 10, 2017 at 10:45
  • I tried hook_preprocess_input along with with hook_preprocess_form_element but the changes won't take effect... Mar 10, 2017 at 11:20
  • can you show some code pls - have you tried hook_preprocess
    – rémy
    Mar 10, 2017 at 11:23
  • 1
    None of $variables['attributes']['title'] or $variables['element']['#attributes']['title'] work... Mar 10, 2017 at 11:43

here is a piece of code :

function MODULE_preprocess_input(&$variables) {
    if (!\Drupal::service('router.admin_context')->isAdminRoute()) {
        $field = $variables['element'];
        if ($field['#type'] == 'email') {
            if (isset($field['#attributes']['placeholder'])) {
                $placeholder = $field['#attributes']['placeholder'];
                $variables['element']['#attributes']['title'] = $placeholder;

None of above works for me. There is no 'title' field which can be changed, but instead there is a TranslatableMarkup object inside $variables["attributes"]["value"]

For me this works:

function MODULE_preprocess_input(&$variables)
  if ($variables['attributes']['class'][0] == 'delete-order-item') {

    $variables["attributes"]["value"] = new \Drupal\Core\StringTranslation\TranslatableMarkup("X");

Basically I'm changing name of "Remove" button to just "X" and title really is replaced, but problem is that with new name (value attribute actually) delete action is not working any more. But title is replaced successfully.

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.