3

The standard way the radio buttons field is rendered on a node add/edit form is that there is a wrapper div with classes:

form-item form-type-radio form-item-mymodule-imglayout-und

and inside there are input and label elements. Where to hook to change this? I would like the wrapper div to have classes:

form-item form-type-radio form-item-mymodule-imglayout-und mycustomclass

And the custom class should be dependent on the option the button represents, or just be sequential: first, second, third, ...etc

Additional info:

My point is to be able to add a distinct big background image to each radio button, depending on the option it represents. Like below - note that each radio button has an associated image, representing its option visually.

enter image description here

  • Maybe this link give you a hint on how to do that ... encodez.com/blog/how-to-theme-radio-drupal.html – Andreas W. Wylach May 5 '12 at 9:44
  • Thanks for the hint, but... the problem is that theme_radio() returns html for 1 radio button containing just <input> element. This html is then rendered by theme_form_element() and wrapped in div which I described above. It would be good to be able to somehow add e.g. a '#theme_wrapper' function to this element, but I have no idea how. It should be done during preprocess, i.e. at the stage of creating the render array. I checked in Theme Developer, but there are no preprocess or process functions for form radio, and the only candidate function name is form_element().. looks very high level :( – camcam May 5 '12 at 15:32
  • By the way, the whole point of this is to be able to add a distinct big background image to each radio button, depending on the option it represents. It would also do adding background to the label, but it has no distinct class. So I need a wrapper. – camcam May 5 '12 at 16:05
2

I think what you need to do is just altering the form array. You can accomplish this in your own hook_form_alter implementation.

For example:

function YOURMODULE_form_alter(&$form, &$form_state, $form_id) {
    if($form_id == 'YOUR_CONTENT_TYPE_node_form') {
        $form['YOUR_ELEMENT_NAME']['#attributes']['class'][] = 'your-custom-class';
    }
}
  • I thought of this too, but MY_ELEMENT_NAME is a radio button widget and its attributes as you mentioned above are classes added on the field level. I need to add a custom attribute (class) to each of its subitems (radio buttons), and depending on the option a particular radio is related to. So, e.g. the widget has 4 options: "top", "bottom", "left" and "right". It is reflected on each <input> element's classes by default. But I need to add a wrapper around each <input> element with this exact class. I'll update the question in a few minutes with an illustration why this is necessary like this. – camcam May 6 '12 at 9:40
2

Here is a possible solution to this problem which can be implemented in a custom module

function mymodule_preprocess_radio (&$variables) {
  $element = &$variables['element'];
  if ((isset($element['#name'])) && substr($element['#name'],0,18) == 'mymodule_imglayout') {
    $variables['theme_hook_suggestion'] = 'radio__imglayout';
  }
}

function theme_radio__imglayout($variables) {
  $element = $variables['element'];
  $element['#attributes']['type'] = 'radio';
  element_set_attributes($element, array('id', 'name', '#return_value' => 'value'));

  if (isset($element['#return_value']) && $element['#value'] !== FALSE && $element['#value'] == $element['#return_value']) {
    $element['#attributes']['checked'] = 'checked';
  }
  _form_set_class($element, array('form-radio'));

  $output = '<input' . drupal_attributes($element['#attributes']) . ' />';
  $id = $element['#id'];
  $mydiv = "<div class = 'imglayout {$id}'></div>";
  return   $mydiv .'<br/>'. $output  ;
}

function mymodule_theme($existing, $type, $theme, $path) {
   return array(
     'radio__imglayout' => array(
       'render element' => 'element',
     ),
  );
}

The $mydiv has a distinct class and is placed before the <input> and <label> elements, so just float it to the left and give each one width and height and a distinct background image - it will be displayed just above the radios (not below as I wanted originally but that's minor).

  • Does this actually work? i.e. is mymodule_preprocess_radio a hook, like other preprocess functions? My guess is that it implements api.drupal.org/api/drupal/modules%21system%21theme.api.php/… It would have been handy if the question-asker had put a comment on this accepted answer explaining why the accepted it. "Works for me" should be avoided as they can cause "table-flip". Thank you kindly. – therobyouknow May 3 '18 at 10:11
0

I accomplished this in the theme layer with the following theme function:

function THEME_form_element($variables) {
  $element = &$variables['element'];

  // This function is invoked as theme wrapper, but the rendered form element
  // may not necessarily have been processed by form_builder().
  $element += array(
    '#title_display' => 'before',
  );

  // Add element #id for #type 'item'.
  if (isset($element['#markup']) && !empty($element['#id'])) {
    $attributes['id'] = $element['#id'];
  }
  // Add element's #type and #name as class to aid with JS/CSS selectors.
  $attributes['class'] = array('form-item');
  if (!empty($element['#type'])) {
    $attributes['class'][] = 'form-type-' . strtr($element['#type'], '_', '-');
  }
  if (!empty($element['#name'])) {
    $attributes['class'][] = 'form-item-' . strtr($element['#name'], array(' ' => '-', '_' => '-', '[' => '-', ']' => ''));
  }
  // Add a class for disabled elements to facilitate cross-browser styling.
  if (!empty($element['#attributes']['disabled'])) {
    $attributes['class'][] = 'form-disabled';
  }
  if (isset($element['#type']) && isset($element['#return_value']) && $element['#type'] == 'radio') {
    $attributes['class'][] = 'form-radio-option-' . drupal_html_class($element['#return_value']);
  }
  $output = '<div' . drupal_attributes($attributes) . '>' . "\n";

  // If #title is not set, we don't display any label or required marker.
  if (!isset($element['#title'])) {
    $element['#title_display'] = 'none';
  }
  $prefix = isset($element['#field_prefix']) ? '<span class="field-prefix">' . $element['#field_prefix'] . '</span> ' : '';
  $suffix = isset($element['#field_suffix']) ? ' <span class="field-suffix">' . $element['#field_suffix'] . '</span>' : '';

  switch ($element['#title_display']) {
    case 'before':
    case 'invisible':
      $output .= ' ' . theme('form_element_label', $variables);
      $output .= ' ' . $prefix . $element['#children'] . $suffix . "\n";
      break;

    case 'after':
      $output .= ' ' . $prefix . $element['#children'] . $suffix;
      $output .= ' ' . theme('form_element_label', $variables) . "\n";
      break;

    case 'none':
    case 'attribute':
      // Output no label and no required marker, only the children.
      $output .= ' ' . $prefix . $element['#children'] . $suffix . "\n";
      break;
  }

  if (!empty($element['#description'])) {
    $output .= '<div class="description">' . $element['#description'] . "</div>\n";
  }

  $output .= "</div>\n";

  return $output;
}

This overrides theme_form_element, assigning a value-based class to each unique radio element container.

protected by kiamlaluno Sep 22 '12 at 1:25

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

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