I am using Forms API to create a select box. This works great. However, I need to set an HTML attribute for each of the options generated by the #options element.

I know I can set HTML attributes using the #attributes element, but this sets the attribute for the select box, not the individual entries.

I can create the entries like this:

$options['http://www.drupal.org'] = 'My Favorite Site';
$options['http://www.php.net'] = 'My Most Frequented Site';

Then I simply pass $options into the #options portion of the Forms API like so:

'#options' => $options,

I can also set HTML attributes to the whole select box using:

'#attributes' => array('class' => 'levelx');

But like I said, this would add a class attribute to the select box, not the individual entries.

Does anyone know how I can add an attribute to the individual selections per their creation using Forms API?

I tried this in a naive attempt:

$options['http://www.drupal.org']['#attributes'] = 'class=levelx';

Thanks.

Comments

danielb’s picture

Override theme_select (http://api.drupal.org/api/function/theme_select/6) in your theme
Mainly you will have to write your own version of http://api.drupal.org/api/function/form_select_options/6

Zoologico’s picture

So I think what you are saying is that I need to run through my code to build the form as I normally do, then right before I do return $form;, I need to pass the form element of type 'select' through a theme() function, and then do return $form;?

The theme() function will do what I instruct it to do by overriding the form_select_options() function?

This is just slightly over my head, but I think I can reach if I stretch a little, so if it's alright, let me see how far I can get and then come back with follow up questions.

nevets’s picture

Actually the theme function is called as part of building the form.

Zoologico’s picture

Right right, I meant the theme_select() function per my overriding it.
Thanks, still working on it and reading up to get to where I need to get or get stuck at which point I'll be back

Zoologico’s picture

I overrode the theme_select() function by adding it to my theme's template.php file like so:

/**
 * Overriding theme_select() function to format the individual items in a select box
 *
 */
function mytheme_select($element) {
  $select = '';
  $size = $element['#size'] ? ' size="' . $element['#size'] . '"' : '';
  _form_set_class($element, array('form-select'));
  $multiple = isset($element['#multiple']) && $element['#multiple'];
  return theme('form_element', $element, '<select name="'. $element['#name'] .''. ($multiple ? '[]' : '') .'"'. ($multiple ? ' multiple="multiple" ' : '') . drupal_attributes($element['#attributes']) .' id="'. $element['#id'] .'" '. $size .'>'. form_select_options($element) .'</select>');
}

This means that instead of calling the original theme_select() function found in form.inc, it will call this one (right now it does the same thing).

As you can see, it does call the form_select_options() function on the last line.

I need to write my own version of that function. Is there a way to override that function like I did my theme_select() function?

nevets’s picture

You can simply call a different function where form_select_options() is called.

Zoologico’s picture

I just want to say thanks for your guidance.

I was able to do what I wanted based in great part to your guidance.

For those that want to do this in the future here is how I did it:

1) Override the theme_select() function by adding a new function to your theme's template.php file and replace the word "theme" in the declaration of the function with the name of your theme. For example, if youa re using the Garland theme, the new name of the function would be garland_select() as the example below will show. The code should initially be an exact copy of the actual theme_select() function which you can get from the API reference guide (api.drupal.org).

2) Create a new version of the form_select_options() function within the same template.php file. You can call it myform_select_options. The code should initially be an exact copy of the actual form_select_options() function which you can get from the API reference guide (api.drupal.org).

3) Look at the code in the function you created in step one and instead of calling the original form_select_options on the last line, have it call your new one (myform_select_options).

4) Modify the new myform_select_options to build each entry of the select box to your liking.

Below are the functions that I used to accomplish this. Note that all I wanted to do what add "class="levelx"" to each option where x was the depth level. In another function using Forms API, I add "-" (hyphens) to symbolize depth and be displayed more deeply to the user, so I just went in and counted "-" with the PHP function substr_count() to let me know what level the entries are found in and then divide by 2 since I added two "-" every time I needed to display an entry at a deeper level. Don't worry about this too much since this is my own need. You will modify the new myform_select_options() function to do what YOU need it to do.

Here they are (if you copy and paste them, do not include the php tags at the beginning and end of each example since those should already exist in your templete.php file, I added them for enabling color coding):

Just below is the overridden theme_select() function in my theme's template.php file. Note the new garland name when it is declared. This is just an example that uses the garland theme. You must replace this with your theme's name (step one above). Also note that the last line calls my new version of the form_select_options() function called myform_select_options() which I show you below.

<?php
/**
 * Overriding theme_select() function to format the individual items in a select box
 *
 */
function garland_select($element) {
  $select = '';
  $size = $element['#size'] ? ' size="' . $element['#size'] . '"' : '';
  _form_set_class($element, array('form-select'));
  $multiple = isset($element['#multiple']) && $element['#multiple'];
  return theme('form_element', $element, '<select name="'. $element['#name'] .''. ($multiple ? '[]' : '') .'"'. ($multiple ? ' multiple="multiple" ' : '') . drupal_attributes($element['#attributes']) .' id="'. $element['#id'] .'" '. $size .'>'. myform_select_options($element) .'</select>');
}
?>

Just below is the custom form_select_options() function in my theme's template.php file. Note that I only modified the instruction just before "return $options;" because that is what I needed to affect. You may be looking to modify something else.

<?php
/**
 * Calling alternative form_select_options() function to format the individual items in a select box
 *
 */
function myform_select_options($element, $choices = NULL) {
  if (!isset($choices)) {
    $choices = $element['#options'];
  }
  // array_key_exists() accommodates the rare event where $element['#value'] is NULL.
  // isset() fails in this situation.
  $value_valid = isset($element['#value']) || array_key_exists('#value', $element);
  $value_is_array = is_array($element['#value']);
  $options = '';
  foreach ($choices as $key => $choice) {
    if (is_array($choice)) {
      $options .= '<optgroup label="'. $key .'">';
      $options .= form_select_options($element, $choice);
      $options .= '</optgroup>';
    }
    elseif (is_object($choice)) {
      $options .= form_select_options($element, $choice->option);
    }
    else {
      $key = (string)$key;
      if ($value_valid && (!$value_is_array && (string)$element['#value'] === $key || ($value_is_array && in_array($key, $element['#value'])))) {
        $selected = ' selected="selected"';
      }
      else {
        $selected = '';
      }
      $options .= '<option value="'. check_plain($key) .'" class="level' . check_plain((substr_count($choice, '-')) / 2) . '"' . $selected .'>'. check_plain($choice) .'</option>';
    }
  }
  return $options;
}
?>

Thanks again to the folks above who helped to guide me.

Marko B’s picture

there is a key error in post abowe

if u want to have classes for all levels of hierarchy then u have to have

"myform_select_options" called on all parts of the same function and in code aboe default function is called "form_select_options" and then this code doesnt work and is useless. use this instead

function myform_select_options($element, $choices = NULL) {
  if (!isset($choices)) {
    $choices = $element['#options'];
  }
  //print_r ($element);
  // array_key_exists() accommodates the rare event where $element['#value'] is NULL.
  // isset() fails in this situation.
  $value_valid = isset($element['#value']) || array_key_exists('#value', $element);
  $value_is_array = is_array($element['#value']);
  $options = '';
  foreach ($choices as $key => $choice) {
    if (is_array($choice)) {
      $options .= '<optgroup label="'. $key .'">';
      $options .= myform_select_options($element, $choice);
      $options .= '</optgroup>';
    }
    elseif (is_object($choice)) {
      $options .= myform_select_options($element, $choice->option);
    }
    else {
      $key = (string)$key;
      if ($value_valid && (!$value_is_array && (string)$element['#value'] === $key || ($value_is_array && in_array($key, $element['#value'])))) {
        $selected = ' selected="selected"';
      }
      else {
        $selected = '';
      }
	 $child_term_no_dash = preg_replace('/^-([a-zA-Z0-9æøåÆØÅ]+)/' ,'$1', check_plain($choice));
   
      $options .= '<option value="'. check_plain($key) .'" class="level' . check_plain(substr_count($choice, '-')) . '"' . $selected .'>' . $child_term_no_dash .'</option>';
    }
  }
  return $options;
}

and finally u have all the classes u need, ufff spent 2 hours on this, strange that nobody ever tought dropdown for taxonomy would be themed, this is pretty hard to do and it should be easier in drupal.

jp.fox’s picture

The theme registry has to be rebuilt ones to take effect.

To do that, just put the following line in your template.php, reload a page and remove it.

drupal_rebuild_theme_registry();

http://api.drupal.org/api/function/drupal_rebuild_theme_registry/6

vishalkhialani’s picture

My goal is to add title="sometext" into my form.

So below is the html output I am able to achieve based on your post. I am using d7. Please check and advice what I am doing wrong.

<select id="edit-select" class="form-select" name="select">
<option value="1" title="1">One</option>
<option value="2" title="1">Two</option>
</select>

As you can see title is there but the value is wrong. Below is my form and the functions which I had to override.

My form:

	$form['select'] = array(
        '#type' => 'select',
        '#options' => array(1 => 'One', 2 => 'Two'),
        '#title' => array(1 => 'One', 1 => 'One') 
        // I tried many combinations but nothing seems to work.
        );

My Theme functions.

function kt_vusers_select($variables) {
  $element = $variables['element'];
  element_set_attributes($element, array('id', 'name', 'size'));
  _form_set_class($element, array('form-select'));

  return '<select' . drupal_attributes($element['#attributes']) . '>' . kt_vusers_form_select_options($element) . '</select>';
}


function kt_vusers_form_select_options($element, $choices = NULL) {
  // Build up your own version of form_select_options here
  // that takes into account your extra attribute needs.
  // This will probably involve inspecting your custom FAPI property,
  // which we'll call #extra_option_attributes

  if (!isset($choices)) {
    $choices = $element['#options'];
  }
  // array_key_exists() accommodates the rare event where $element['#value'] is NULL.
  // isset() fails in this situation.
  $value_valid = isset($element['#value']) || array_key_exists('#value', $element);
  
  // @vishal  so there I have declared the variable to accept the values.
  $vtitle = isset($element['#title']) || array_key_exists('#title', $element);

  $value_is_array = $value_valid && is_array($element['#value']);
  $options = '';
  foreach ($choices as $key => $choice) {
    if (is_array($choice)) {
      $options .= '<optgroup label="' . $key . '">';
      $options .= form_select_options($element, $choice);
      $options .= '</optgroup>';
    }
    elseif (is_object($choice)) {
      $options .= form_select_options($element, $choice->option);
    }
    else {
      $key = (string) $key;
      if ($value_valid && (!$value_is_array && (string) $element['#value'] === $key || ($value_is_array && in_array($key, $element['#value'])))) {
        $selected = ' selected="selected"';
      }
      else {
        $selected = '';
      }
      // @vishal this is where the variable is being used.
      $options .= '<option title="'.$vtitle.'" value="' . check_plain($key) . '"' . $selected . '>' . check_plain($choice) . '</option>';
    }
  }
  return $options;


}

cheers,
vishal

redcrackle.com

aqwadon’s picture

it's a very old post, but exactly what i'm looking for. and i haven't been able to make it run...
My mytheme_select function is never called. Is there something special to do with drupal 7 ?

aqwadon’s picture

up... please !

yuseferi’s picture

After 13 years still it does not supported :( 

I have be born to be mankind