Field Descriptions under the Label and above fields

zeta1600's picture

Not sure why the descriptions of labels are placed under the fields. How can I move this up under the label in omega?

Comments

Good point, I have the same

samwillc's picture

Good point, I have the same problem where I would like the description directly above a field as it would make more sense to the user. As it stands, they see the field, scroll down, get the ever-so-helpful, but quite frankly annoying text format information and under that, my description, which is really important regarding that particular field.

You can obviously highlight via css but it's still under all the other stuff and not above the field.

Sam.

Can anyone decipher this and

zeta1600's picture

Can anyone decipher this and create a function to move the description below the label from this?
http://api.drupal.org/api/drupal/includes--form.inc/function/theme_form_...

It seems that there is no

jebbushell's picture

It seems that there is no weighting scheme. Therefore some other way is required. If it was me, I would use theme coding to unpack the array for each form element and then construct an output string in the required sequence.

// Register the function that needs to be called to do the adjustment:

function mymodule_theme() {
  return array (
    'my_form' => array (
        'render element' => 'form'
    ),
  );
}

// The function that does the work might look something like this:

function theme_my_form($variables)
{
  $form = $variables['form'];
  $work = drupal_render($form['field1']);
  // You then hack the string in $work into the sequence you want and append to the output string.
  $hacked = reallyuglyhack($work);
  $output .= $hacked;
  return $output;
}

The realluglyhack() function extracts the description-related markup and reinserts it where you want it.
I offer this in the belief that you can probably get it to work, but also in the hope that someone more knowledgeable than I will be so offended by it that they will feel compelled to correct me. There has to be a better way.

Not an omega issue

zeta1600's picture

This really isn't an omega issue, but I found this code that worked for me in case it helps someone else:
Add this to your theme template.php. If you don't have one, create one. Start it with <?php. Change your YOURTHEMENAME, then, don't forget to CLEAR CACHE on d7... here you go.

function YOURTHEMENAME_form_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;
}

Thanks zeta1600, nearly

samwillc's picture

Thanks zeta1600, nearly worked for me...

http://imageshack.us/photo/my-images/834/screenshot1tp.jpg/

But not quite!

Sam.

Drupal 7.14 and some sanity!

Daruka's picture

Thanks everyone for posting this thread. It has been really helpful in helping me move my field descriptions above the field input area.

I implemented the code in this post and accomplished the goal of moving descriptions above the input element, however I had two issues:

1) I have a few form fields without #id specified, which resulted in warnings:

Notice: Undefined index: #id in library1_form_element() (line 40 of /web/docs/sites/all/themes/library1/template.php).
Notice: Undefined index: #id in library1_form_element() (line 52 of /web/docs/sites/all/themes/library1/template.php).

2) The code did not pass div classes through to the element, making things like checkboxes and radio elements span mutiple lines (checkbox/radio is on one line while label is on next - not very pretty!)

After much debugging and frustration, it occurred to me there is a much easier way to do this, and I hope this is the best practice for future modifications...

  • Go into your drupal/includes/form.inc file
  • Find the theme_form_element function
  • Copy the entire function to your clipboard
  • Paste the function into your template.php file
  • Rename the function from theme_form_element to YOURTHEME_form_element (replacing YOURTHEME with the name of your theme)
  • Now modify the code to fit your needs.

In the case of moving descriptions around, I did the following:

  • Find the code where #description is printed (towards the bottom)
  • Cut this code and paste it twice into the switch for element['#title_display'] as follows:

  switch ($element['#title_display']) {
    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;
     if (!empty($element['#description'])) {
        $output .= '<div class="description">' . $element['#description'] . "</div>\n";
        }
      $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;
  }

There might be a more elegant solution than pasting the description code twice, but I wanted to get this done ASAP after spending too much time debugging!

Best regards,
-Daruka

Doesn't work for Textareas

alanmorris's picture

Thanks for this suggestion - it looked like it should work fine, but doesn't work for textareas.

Any suggestions on how to move the description for textareas would be very welcome.

Thanks again,

Alan

Daruka's code worked on my D7 install

sstacks's picture

Just FYI, if anyone wants to see what the complete function in your template.php file will look like after making this change, here it is:

function YOURTHEME_form_element($variables) {
$element = &$variables['element'];
// This is also used in the installer, pre-database setup.
$t = get_t();

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

switch ($element['#title_display']) {
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;
 if (!empty($element['#description'])) {
    $output .= '<div class="description">' . $element['#description'] . "</div>\n";
    }
  $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;

}

YOURTHEME_form_element

Kojo Unsui's picture

Great code. Don't forget to return output after stacks function.

It works for textarea, but only in plain text format and not with wysiwyg textareas !
I broke my head trying to solve that, no way.

Did anybody find the right way please ?

Textarea description

peteruithoven's picture

I'm also trying to get the description on top in textarea form elements.
I asume, somewhere the $element['#description'] is cleared before theme_form_element is called. Strangely it isn't done in the theme_text_format_wrapper function but somewhere after it.

The function filter_process_format (filter.module) creates a blacklist that includes description. When I remove description from this list the description is passed to theme_form_element and the description can be put to the top.

No clue how to properly fix this though...

theme_field_multiple_value_fo

peteruithoven's picture

theme_field_multiple_value_form calles drupal_render with the element (including description as argument). But the output is without the description.

This is a very ugly hack, but

peteruithoven's picture

This is a very ugly hack, but my knowledge of theming doesn't go far enough to figure out a proper way.

<?php
function protospace_text_format_wrapper($variables) {
 
$element = $variables['element'];
 
$output = '<div class="text-format-wrapper">';
 
$output .= $element['#children'];
 
//if (!empty($element['#description'])) {
  //  $output .= '<div class="description">' . $element['#description'] . '</div>';
  //}
 
$output .= "</div>\n";
 
 
$description = $element['#description'] ? '<div class="description">' . $element['#description'] . '</div>' : '';
 
$output  = str_replace('<div class="form-textarea-wrapper"', $description.'<div class="form-textarea-wrapper"',$output);

  return
$output;
}
?>

I've just written a module

Sheldon Rampton's picture

I've just written a module for Drupal 7 that offers a solution to this problem:

http://drupal.org/project/label_help

Sheldon Rampton
Senior web developer, New York State Senate
http://www.nysenate.gov
http://drupal.org/user/13085

download?

criscom's picture

Hi,
your module is what I have been looking for. Where can I download it?

Chris

thanks

TGEink's picture

Not at the point where I want to make code changes for a small but important change. So thanks you.

Hi criscom. You can get a git

Sheldon Rampton's picture

Hi criscom. You can get a git clone of it here:

http://drupal.org/project/label_help/git-instructions

Or, you can wait 24 hours until Drupal.org generates a downloadable tarball of the release.

Sheldon Rampton
Senior web developer, New York State Senate
http://www.nysenate.gov
http://drupal.org/user/13085

Did that

criscom's picture

Thanks, Sheldon. I downloaded the git clone already. Thanks.

JQuery Fix

Chappo's picture

I needed something fast and that would work for all field types without breaking.

So I made a fix in JQuery.

Very much a hack, but i needed this now.

$('.form-item .description').each(function () {
    var desc = $(this);
    var label = desc.siblings('label:first');
    var multTable, textArea;
   if (label.length && !desc.parents('.field-multiple-table').length) {
     desc.insertAfter(label);
   } else if ((multTable = desc.siblings('.field-multiple-table')) && multTable.length) {
       desc.insertAfter(multTable.find('label:first'));
}
});
$('.text-format-wrapper .description').each(function () {
  var desc = $(this);
    var label = desc.siblings('.form-item').find('label:first');
   if (label.length) {
        desc.insertAfter(label);
   }
});

Chappo I like the look of

El Bandito's picture

Chappo

I like the look of this as all the other solutions ( including the Label Help module ) appear to have various limitations.

Any chance you could pseudo-code the logic so I can make sure it is OK for my site ?

Thanks

It all became clearer as I

El Bandito's picture

It all became clearer as I tried to do it. My eventual code :

jQuery(document).ready(function ($) {

  // move the help text from below the input field to directly below the field label

  $('.form-item .description').each(function () {  // description is the help text
    var desc = $(this);
    var label = desc.siblings('label:first');
     if (label.length) {
       desc.insertAfter(label);
     }
  })
  // the help text is tangled up in the text format stuff on a filtered text field
  $('.text-format-wrapper .description').each(function () {
    var desc = $(this);
    var label = desc.siblings('.form-item').find('label:first');
     if (label.length) {
        desc.insertAfter(label);
     }
  })
});

Don't think chappo's .field-multiple-table stuff is relevant to my site ( although testing has been limited ! )

Another php solution

incaic's picture

Not a huge fan for using jquery for this kinda stuff, so here is another php solution that only manipulates the label and descriptions of fields you are interested in. Add these two functions to your template.php file.

<?php
function <theme_name>_form_element($variables) {
 
$element = $variables['element'];
  if (isset(
$element['#field_name'])) {
    switch (
$element['#field_name']) {
      case
'<field_name>':
       
$variables['element']['#description_top'] = $variables['element']['#description'];
        unset(
$variables['element']['#description']);
        break;
    }
  }
  return
theme_form_element($variables);
}

function <
theme_name>_form_element_label($variables) {
 
$output = theme_form_element_label($variables);
 
$element = $variables['element'];
  if (isset(
$element['#description_top'])) {
   
$output .= '<div class="description">' . $element['#description_top'] . "</div>\n";
  }
  return
$output;
}
?>

@Sheldon: Thank you!

lunk_rat's picture

@Sheldon: Thank you!

You should add this function

markosef's picture

You should add this function to template.php

http://api.drupalize.me/api/drupal/function/theme_webform_element/7

and then override it. Also if you don't want to have all description's before, but maybe just one. You should add this just above $output .= "\n";

if (!empty($element['#description']) && $variables['element']['#title']!='Reference') {
$output .= '

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

\n";
}

and this just above switch ($element['#title_display']) {

if (!empty($element['#description']) && $variables['element']['#title']=='Reference') {
$output .= '

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

\n";
}

@Daruka: Thanks!

trace303's picture

Thanks Daruka. Your solution worked perfectly for me, including textareas.

D7 Sandbox module for this

jrb's picture

I've added a sandbox module to do this in D7.

https://drupal.org/sandbox/jrb/top_description

It should work for all fields including WYSIWYG text areas and multiple fields.

Thanks jrb! Your module

Proteo's picture

Thanks jrb! Your module worked great with everything I threw at it :)

Omega Framework

Group organizers

Group events

Add to calendar

Group notifications

This group offers an RSS feed. Or subscribe to these personalized, sitewide feeds:

Hot content this week