When adding Form Components to the wonderful WebForm node, there is a Description field. This description is appearing AFTER the Form Component (textfield etc) when the webform is viewed. How can I move this description ABOVE the Form Components? I looked at the webform-form.tpl.php file in the module, but I didn't see anything available there, unless I overlooked something.

How can I re-order the form components and their descriptions?

Thanks guys.

Comments

meiophylly’s picture

I don't have a solution for you, but I wanted to bump your post because I'd be interested in doing this too. Does anyone else know how to accomplish this?

hendrakieran’s picture

I need this too, anyone?

calebm12’s picture

same problem. subscribing

Iowagirl’s picture

I would love instructions on this as well.

It would make sense as most default themes (I've build forms in several using webform) to have a description that provides instructions in detail right above the field as default IN ADDITION to what's available.

So the form has:

Label
Instructions or detailed information about the what the person would enter in the field
Field
Description field for further information (as is) that has specific instructions for data entry

Thanks,

- Keri

bsenftner’s picture

Is there any reason you can't make these changes via CSS? With Firebug (a plugin for Firefox) you can inspect the page elements and many themes also support a "local.css" file for overwriting the default CSS layout.

Just locate the elements you need/want to move via Firebug, figure out the CSS that accomplishes your desired layout (which can be done interactively via firebug), and put those changes into your local.css file.

for more explicit instructions, I wrote some tutorials here:
(The first three lead you up to understanding #4, if necessary)
http://www.missingubercartmanual.com/How-to-Customize-a-Drupal-Web-Site
http://www.missingubercartmanual.com/How-to-Check-if-a-Theme-supports-Lo...
http://www.missingubercartmanual.com/How-to-Customize-a-Sites-Appearance...
http://www.missingubercartmanual.com/How-to-Make-Changes-to-a-Drupal-Theme

[Off-topic] "hey "Iowagirl", I grew up there... left a long, long time ago... still fond memories...

i25’s picture

I still haven't found a solution for this one! It seems like it should be fairly easy to re-arrange the elements and place the description before the input fields... this would be the intuitive way. It shouldn't require CSS hacking to do this. Does anyone have any ideas or solutions since the last post?

bsenftner’s picture

Have you considered using hook_form_alter()?

I've been using that to modify various forms, as in moving elements around, as well as adding and deleting elements.

You'll need to write a custom module to do this. Here's one such implementation where I make significant changes to a content type node form:

function customModuleName_form_alter( &$form, $form_state, $form_id )
{
  if ($form_id == 'customContentType_node_form')
  {    
    // change the submit button's text:
    $form['buttons']['submit']['#value'] = 'Store to database';
    
    // this moves the 'Save without compiling' button inside
    // the same form div as the other submission buttons
    if (isset($form['save'])) {
       $form['buttons']['save'] = $form['save'];
       unset( $form['save'] );
    } 
    
    // install a custom validation callback:
    $form['#validate'][] = 'customContentType_compile_validate';
  
    // move the these fields lower in the form:
    $form['#content_extra_fields']['body_field']['weight'] = 3;
    $form['#content_extra_fields']['taxonomy']['weight'] = 5;
    $form['#content_extra_fields']['comment_settings']['weight'] = 6;

    // get rid of form elements that may confuse end-users
    unset( $form['menu'] );              
    unset( $form['body_field']['format'] );
    unset( $form['revision_information'] );
    unset( $form['path'] );
    unset( $form['attachments'] );
    unset( $form['author'] );
    unset( $form['options'] );
    unset( $form['comment_settings'] );
    unset( $form['buttons']['preview'] );

    unset( $form[0]['subscriptions'] );
  }
}
paul.dambra’s picture

In general I'd support this method but the nature of the module is that the item in $form holding the description could be nested several layers deep. There is also the fairly large possibility that there is more than one description.

You'd need some recursive loop to search for and then reorder the appropriate nested arrays on each and every form load. I can't help think that moving the description when it is output to the screen is going to be a much cleaner approach.

Also since ['description'] is held in ['extra'] my gut feeling is that moving it out of that location would simply make it dissapear rather than alter it's display location...

Am happy to be proven wrong :)

i25’s picture

Guys,
Any suggestions for re-ordering the elements in a webform???

paul.dambra’s picture

I've just been looking at this myself... function theme_webform_element in webform.module reads as being responsible for outputting the item including it's description but editing it doesn't affect my output. Looking at the submission tree you have... for example

[7] => Array
                                (
                                    [nid] => 9
                                    [cid] => 7
                                    [form_key] => membership_number
                                    [name] => Membership Number
                                    [type] => textfield
                                    [value] => 
                                    [extra] => Array
                                        (
                                            [description] => You need to be a member of the BMC . MCofS. MCI to host
                                            [title_display] => 0
                                            [disabled] => 0
                                            [unique] => 0
                                            [conditional_operator] => =
                                            [width] => 
                                            [maxlength] => 
                                            [field_prefix] => 
                                            [field_suffix] => 
                                            [attributes] => Array
                                                (
                                                )

                                            [conditional_component] => 
                                            [conditional_values] => 
                                        )

                                    [mandatory] => 0
                                    [pid] => 3
                                    [weight] => 8
                                    [page_num] => 1
                                )

Here you can see the description is held in ...[extra][description]

That array seems to be built by function webform_node_load

switching

    $component['value'] = $c['value'];
    $component['extra'] = unserialize($c['extra']);

around doesn't alter my output either. After clearing all caches and with the theme registry rebuilt.

I'm reaching the end of my understanding of the loading process. Theme developer module suggests that theme_webform_element_wrapper is responsible for outputting the element but putting a drupal_set_message call within that function doesn't set a message.

I think being able to switch this would be a great alteration to the module. It was the first thing two of my users asked for once they'd seen what could be done.

paul.dambra’s picture

If I put watchdog('info','<pre>'. check_plain(print_r($content,TRUE)) .'</pre>'); for example in theme_webform_element, theme_webform_element_wrapper and webform_element_wrapper I don't get any output to the dblog. If I put watchdog('info','<pre>'. check_plain(print_r($form['submitted'],TRUE)) .'</pre>'); into webform-form.tpl.php then I get output to dblog.

Does this mean those functions aren't involved? Or that I can't dblog from them?

paul.dambra’s picture

I have a feeling I should be looking to pull the logic in e.g. theme_webform_element into a preprocess function and then use a template but other work beckons so I'll have to come back to this another time - it isn't the most pressing piece of work for our existing development...

If someone wants to comment that the above idea is madness then I'll appreciate the time saving of not investigating it :-)

Alexander Matveev’s picture

Subscribing heh

Alexander Matveev’s picture

Hey guys, looks like this is the right direction http://drupal.org/node/804980#comment-2994932

paul.dambra’s picture

FWIW here's the code I used to "fix" this in Drupal 6. My theme is called bmc_zen replace that in the below as appropriate and add this to your theme's template.php

/**
 * Replacement for theme_form_element().
 */
function bmc_zen_form_element($element, $value) {
  $wrapper_classes = array(
   'form-item',
  );
  $output = '<div class="' . implode(' ', $wrapper_classes) . '" id="' . $element['#id'] . '-wrapper">' . "\n";
  $required = !empty($element['#required']) ? '<span class="form-required" title="' . t('This field is required.') . '">*</span>' : '';

  if (!empty($element['#title'])) {
    $title = $element['#title'];
    $output .= ' <label for="' . $element['#id'] . '">' . t('!title: !required', array('!title' => filter_xss_admin($title), '!required' => $required)) . "</label>\n";
  }

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

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

  return $output;
}

Obviously check your template.php for this function just in case :) Hope that helps someone.

paul.dambra’s picture

/**
 * Replacement for theme_form_element().
 */
function bmc_zen_form_element($element, $value) {
  $wrapper_classes = array(
   'form-item',
  );
  $output = '<div class="' . implode(' ', $wrapper_classes) . '" id="' . $element['#id'] . '-wrapper">' . "\n";
  $required = !empty($element['#required']) ? '<span class="form-required" title="' . t('This field is required.') . '">*</span>' : '';

  if (!empty($element['#title'])) {
    $title = $element['#title'];
    $output .= ' <label for="' . $element['#id'] . '">' . t('!title: !required', array('!title' => filter_xss_admin($title), '!required' => $required)) . "</label>\n";
  }
	
	//not every element looks right with the description above it.
  If (array_key_exists('#webform_component',$element) && $element['#type']!='checkbox'){
	  if (!empty($element['#description'])) {
		$output .= ' <div class="description">' . $element['#description'] . "</div>\n";
	  }	  
	  $output .= '<div id="' . $element['#id'] . '">' . $value . '</div>' . "\n";
  } else {
  	$output .= '<div id="' . $element['#id'] . '">' . $value . '</div>' . "\n";
  	if (!empty($element['#description'])) {
		$output .= ' <div class="description">' . $element['#description'] . "</div>\n";
	  }  
  }
  $output .= "</div>\n";

  return $output;
}

added If (array_key_exists('#webform_component',$element) && $element['#type']!='checkbox') to restrict the description switch to webform components that aren't checkboxes.

rumblewand’s picture

I changed it a bit to get my webform descriptions above the input field for drupal 7. Drupal 7 allows you to just make calls just to webform itself.

/**
* Replacement for theme_webform_element() to enable descriptions to come BEFORE the field to be filled out.
*/
function danland_webform_element($variables) {
  $element = $variables['element'];
  $value = $variables['element']['#children'];

  $wrapper_classes = array(
    'form-item',
  );
  $output = '<div class="' . implode(' ', $wrapper_classes) . '" id="' . $element['#id'] . '-wrapper">' . "\n";
  $required = !empty($element['#required']) ? '<span class="form-required" title="' . t('This field is required.') . '">*</span>' : '';

  if (!empty($element['#title'])) {
    $title = $element['#title'];
    $output .= ' <label for="' . $element['#id'] . '">' . t('!title: !required', array('!title' => filter_xss_admin($title), '!required' => $required)) . "</label>\n";
  }

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

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

  return $output;
}

You will of course change "danland" to the title of your theme.

baisong’s picture

Great solution, rumblewand!

I modified one line to suppress disabled component titles from being displayed:

if (!empty($element['#title'])) {

should be

if (!empty($element['#title']) && ($element['#title_display'] != 'none')) {

mcfilms’s picture

Maybe give this module a try: http://drupal.org/project/arrange_fields

It promises to do exactly what you are asking for.

A list of some of the Drupal sites I have designed and/or developed can be viewed at motioncity.com

mrvinch’s picture

theme_webform_element from webform 7.x-4.9 suporting conditionals with the description between the Question label and the options/answer field:

/**
 * Replacement for theme_form_element().
 */
function theme_webform_element($variables) {
  $element = $variables['element'];

  $output = '<div ' . drupal_attributes($element['#wrapper_attributes']) . '>' . "\n";
  $prefix = isset($element['#field_prefix']) ? '<span class="field-prefix">' . webform_filter_xss($element['#field_prefix']) . '</span> ' : '';
  $suffix = isset($element['#field_suffix']) ? ' <span class="field-suffix">' . webform_filter_xss($element['#field_suffix']) . '</span>' : '';

  // managed_file uses a different id, make sure the label points to the correct id.
  if (isset($element['#type']) && $element['#type'] === 'managed_file') {
    if (!empty($variables['element']['#id'])) {
      $variables['element']['#id'] .= '-upload';
    }
  }

  switch ($element['#title_display']) {
    case 'inline':
    case 'before':
    case 'invisible':
      $output .= ' ' . theme('form_element_label', $variables);
      if (!empty($element['#description'])) {
    		$output .= ' <div class="description">' . $element['#description'] . "</div>\n";
  		}
      $output .= ' ' . $prefix . $element['#children'] . $suffix . "\n";
      break;

    case 'after':
      $output .= ' ' . $prefix . $element['#children'] . $suffix;
      $output .= ' ' . theme('form_element_label', $variables) . "\n";
      if (!empty($element['#description'])) {
    		$output .= ' <div class="description">' . $element['#description'] . "</div>\n";
  		}
      break;

    case 'none':
    case 'attribute':
      // Output no label and no required marker, only the children.
    	if (!empty($element['#description'])) {
    		$output .= ' <div class="description">' . $element['#description'] . "</div>\n";
  		}
      $output .= ' ' . $prefix . $element['#children'] . $suffix . "\n";
      break;
  }

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

  return $output;
}
leahmd’s picture

Thank you so much for sharing this, saved me a lot of work today.

megabait’s picture

All good, except label in checkbox, its doesn't "live" (explaining: when I click on label checkbox doesn't check, I need to click on checkbox to mark it checked). How to make label active again?
I am find, problem was div and label has got same id

<?php/**
* Replacement for theme_form_element().
*/
function YOURTHEME_form_element($element, $value) {
  $wrapper_classes = array(
   'form-item',
  );
  $output = '<div class="' . implode(' ', $wrapper_classes) . '" id="' . $element['#id'] . '-wrapper">' . "\n";
  $required = !empty($element['#required']) ? '<span class="form-required" title="' . t('This field is required.') . '">*</span>' : '';

  if (!empty($element['#title']) && ($element['#title_display'] != 'none')) {
    $title = $element['#title'];
    $output .= ' <label for="' . $element['#id'] . '">'. t('!title: !required', array('!title' => filter_xss_admin($title), '!required' => $required)) . "</label>\n";
  }

//not every element looks right with the description above it.
  If (array_key_exists('#webform_component',$element) && $element['#type']!='checkbox'){
  if (!empty($element['#description'])) {
$output .= ' <div class="description">' . $element['#description'] . "</div>\n";
  }  
  $output .= '<div id="' . $element['#id'] . '-wrap">' . $value . '</div>' . "\n";
  } else {
  $output .= '<div id="' . $element['#id'] . '-wrap">' . $value . '</div>' . "\n";
  if (!empty($element['#description'])) {
$output .= ' <div class="description">' . $element['#description'] . "</div>\n";
  } 
  }
  $output .= "</div>\n";

  return $output;
}?>
rachelf’s picture

Look in the sites/all/modules/webform folder and find the file webform.module

Edit this to find the function theme_form_element

Copy this entire function and then paste it into your template.php file in your current theme.

Rename the function from theme_form_element to MYTHEMENAME_form_element (for example, if your theme is called infrared then rename the function infrared_form_element).

Now edit this function to move the description field to where you want it. For example, I want the description to appear immediately after the title of the field, so I edited it like this:

/**
 * Replacement for theme_form_element().
 */
function infrared_stf_webform_element($variables) {
  // Ensure defaults.
  $variables['element'] += array(
    '#title_display' => 'before',
  );

  $element = $variables['element'];

  // All elements using this for display only are given the "display" type.
  if (isset($element['#format']) && $element['#format'] == 'html') {
    $type = 'display';
  }
  else {
    $type = (isset($element['#type']) && !in_array($element['#type'], array('markup', 'textfield'))) ? $element['#type'] : $element['#webform_component']['type'];
  }
  $parents = str_replace('_', '-', implode('--', array_slice($element['#parents'], 1)));

  $wrapper_classes = array(
   'form-item',
   'webform-component',
   'webform-component-' . $type,
  );
  if (isset($element['#title_display']) && $element['#title_display'] == 'inline') {
    $wrapper_classes[] = 'webform-container-inline';
  }
  $output = '<div class="' . implode(' ', $wrapper_classes) . '" id="webform-component-' . $parents . '">' . "\n";
  $required = !empty($element['#required']) ? '<span class="form-required" title="' . t('This field is required.') . '">*</span>' : '';

  // 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">' . _webform_filter_xss($element['#field_prefix']) . '</span> ' : '';
  $suffix = isset($element['#field_suffix']) ? ' <span class="field-suffix">' . _webform_filter_xss($element['#field_suffix']) . '</span>' : '';

  switch ($element['#title_display']) {
    case 'inline':
    case 'before':
    case 'invisible':
      $output .= ' ' . theme('form_element_label', $variables);
	  if (!empty($element['#description'])) {
		$output .= ' <div class="description">' . $element['#description'] . "</div>\n";
	  }

      $output .= ' ' . $prefix . $element['#children'] . $suffix . "\n";
      break;

    case 'after':
      $output .= ' ' . $prefix . $element['#children'] . $suffix;
      $output .= ' ' . theme('form_element_label', $variables) . "\n";
	  if (!empty($element['#description'])) {
		$output .= ' <div class="description">' . $element['#description'] . "</div>\n";
	  }

      break;

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

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

  return $output;
}

You'll need to flush the cache (or at least the theme registry) for the change to take effect.

Ochode9’s picture

This is the step by step that I took.
1 Going to my theme folder in the FTP and editing the template.php
2 Pasting in the begging of the code on top of another function (for example). As every one is saying you have to change yourthemename for the name of the folder of your theme. The magical code that is written several times in this post:
3 You need to flush the cache: http://www.yourwebsitename.com/admin/config/development/performance
4 The code:

}
/**
* Replacement for theme_form_element().
*/
function yourthemename_webform_element($variables) {
// Ensure defaults.
$variables['element'] += array(
'#title_display' => 'before',
);

$element = $variables['element'];

// All elements using this for display only are given the "display" type.
if (isset($element['#format']) && $element['#format'] == 'html') {
$type = 'display';
}
else {
$type = (isset($element['#type']) && !in_array($element['#type'], array('markup', 'textfield'))) ? $element['#type'] : $element['#webform_component']['type'];
}
$parents = str_replace('_', '-', implode('--', array_slice($element['#parents'], 1)));

$wrapper_classes = array(
'form-item',
'webform-component',
'webform-component-' . $type,
);
if (isset($element['#title_display']) && $element['#title_display'] == 'inline') {
$wrapper_classes[] = 'webform-container-inline';
}
$output = '

' . "\n";
$required = !empty($element['#required']) ? '*' : '';

// 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']) ? '' . _webform_filter_xss($element['#field_prefix']) . ' ' : '';
$suffix = isset($element['#field_suffix']) ? ' ' . _webform_filter_xss($element['#field_suffix']) . '' : '';

switch ($element['#title_display']) {
case 'inline':
case 'before':
case 'invisible':
$output .= ' ' . theme('form_element_label', $variables);
if (!empty($element['#description'])) {
$output .= '

' . $element['#description'] . "

\n";
}

$output .= ' ' . $prefix . $element['#children'] . $suffix . "\n";
break;

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

' . $element['#description'] . "

\n";
}

break;

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

$output .= "

\n";

return $output;

}

dianacastillo’s picture

your answer above did not work for me , but using yourtheme_form_element
like here https://groups.drupal.org/node/206593#comment-683418 did

Diana Castillo

SharonD214@aol.com’s picture

Just what I was looking for. Thanks

Sharon

davidgtuttle’s picture

This might be a dead issue, but I solved my problem by putting this function in my template.php file. The affects all drupal forms so other use cases would probably call for targeting specific forms.

function THEMENAME_form_alter(&$form, $form_state, $form_id) {
if(isset($form['submitted'])){
foreach ($form['submitted'] as &$curr){
if(isset($curr['#description']) && isset($curr['#title'])){
$curr['#title'].='

'.$curr['#description'].'

';
$curr['#description']='';
}
}
}
}

mradamjohn’s picture

Seems like this older thread doesn't yet tie into an important reference that I found useful.

The following page covers a variety of modules and approaches. The comment by Atomox should be heeded as well.

https://www.drupal.org/node/2319111

Cheers!

quiron’s picture

For Drupal 7.x is so easy as do this in your theme template.php:

function YOURTHEME_preprocess_node(&$vars) {
    $form = $vars['content']['webform']['#form']['submitted'];
    foreach($form as $k => $el) {
      if(is_array($el) && isset($el['#type']) && $el['#type'] == 'checkboxes') {
        $vars['content']['webform']['#form']['submitted'][$k]['#webform_component']['extra']['description_above'] = 1;                                     
      }
  }
}