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.

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