Fields output currently only includes css zebra striping (even/odd) information for themers. This is an attempt to add the more additional classes to make their lives easier.

The first addition is a bookend class (e.g. field-item-first, field-item-last) that themers can use to specially treat the bookends of their design elements.

The second is a delta (e.g. field-item-1, field-item-2, etc)that they can use to force clears or apply arbitrary styling via.

And finally the even odd class is updated to improve its namespacing a little (even -> field-item-even).

The end result of a multivalue text field field rendered with these im mind would look like the following:

<div class="field field-name-field-test-text field-type-text field-label-above">
  <div class="field-label">Test Text:&nbsp;</div>
  <div class="field-items">
    <div class="field-item field-item-odd field-item-1 field-item-first">One</div>
    <div class="field-item field-item-even field-item-2">Two</div>
    <div class="field-item field-item-odd field-item-3 field-item-last">Three</div>
  </div>
</div>

Comments

dawehner’s picture

Status: Active » Closed (won't fix)

This output is not from views.
This is core field.tpl.php/template_preprocess_field
There is not first/last set.

ryan.armstrong’s picture

Project: Views (for Drupal 7) » Drupal core
Version: 7.x-3.x-dev » 7.x-dev
Component: Miscellaneous » field system
Status: Closed (won't fix) » Active

Ok, I'm moving this issue over to Drupal core. This seems like something that needs to be addressed, as it makes it difficult to theme fields with multiple content, such as images. Thanks Dereine!

damien tournoud’s picture

Category: bug » feature

This is called a feature request. It's a small one so if someone rolls a patch we might be able to get this into D7.

ryan.armstrong’s picture

Oo whoops, yea my fault, put it in the wrong category!

rjgoldsborough’s picture

Status: Active » Needs review
StatusFileSize
new719 bytes

Here is a patch that adds first and last classes to core fields.

nlounds’s picture

Thanks for the patch. Unfortunately, for me, it is applying the 'last' class to the last even item. So if the last item happens to be odd, the 'last' class gets applied to the next to last item.

yched’s picture

Status: Needs review » Needs work
+++ modules/field/field_new.module	2011-01-09 16:23:59.721108104 -0500
@@ -1161,7 +1161,7 @@ function theme_field($variables) {
   foreach ($variables['items'] as $delta => $item) {
...
+    $classes = 'field-item ' . ($delta % 2 ? 'odd' : 'even') . ($delta == 0 ? ' first' : '') . ($delta == count($delta) + 1 ? ' last' : '');

$delta == count($delta) + 1 should probably be $delta == count($variables['items']) + 1.
Although, for performance reasons, it would be best to precompute the value outside the loop.

Also, note that there is a template version of this output as well (field.tpl.php), that would need to be updated accordingly (the count should in this case be precomputed in preprocess)

Powered by Dreditor.

yched’s picture

Title: Views not setting 'first' or 'last' css classes for field-items » Field output lacks 'first' and 'last' css classes for field-items

Better title. This is not for Views.

gdd’s picture

Version: 7.x-dev » 8.x-dev

Subscribe, would like to see this and will reroll when i have time

mstrelan’s picture

StatusFileSize
new2.01 KB

Reroll of the patch with yched's suggestions. For an additional performance boost I have precomputed the maximum delta rather than the item count so we can compare $delta == $max_delta rather than $delta == $item_count - 1.

mstrelan’s picture

Status: Needs work » Needs review
mstrelan’s picture

StatusFileSize
new2.01 KB

Here's another one with consistent spacing before the classes added by PHP. By adding the spaces before the class there is never a leading or trailing space. The patch in #10 also ensured this, however the odd/even class was inconsistent with the first/last class.

dawehner’s picture

Status: Needs review » Needs work

As yched said before

+      <div class="field-item<?php print $delta % 2 ? ' odd' : ' even'; ?><?php print $delta == 0 ? ' first' : ''; ?><?php print $delta == $max_delta ? ' last' : ''; ?>"<?php print $item_attributes[$delta]; ?>><?php print render($item); ?></div>

This is too much php for a template file. Please move it to the preprocess function.

mstrelan’s picture

This is too much php for a template file. Please move it to the preprocess function.

I thought yched was talking about precomputing the max delta, rather than the classes for a field item. Nonetheless I agree that it is really messy in the template file, so I'd be happy to give this another go.

I have a couple of questions though.

Question 1: does $delta == position in array?

The code below suggests that the first key of $variables['items'] might not be 0 and the last key might not be equal to the last key of $element['#items'].

  $variables['items'] = array();
  foreach ($element['#items'] as $delta => $item) {
    if (!empty($element[$delta])) {
      $variables['items'][$delta] = $element[$delta];
    }
  }

This would mean to get the first and last classes we would have to do something like below.

  $variables['items'] = array();
  $first = FALSE;
  $last = FALSE;
  $i = 0;
  foreach ($element['#items'] as $delta => $item) {
    if (!empty($element[$delta])) {
      ++$i;
      $variables['items'][$delta] = $element[$delta];
      $variables['item_attributes'][$first]['class'][] = 'field-item';
      if ($i % 2) {
        $variables['item_attributes'][$first]['class'][] = 'odd';
      }
      else {
        $variables['item_attributes'][$first]['class'][] = 'even';
      }
      if ($first === FALSE) {
        $first = $delta;
      }
      $last = $delta;
    }
  }
  if ($first !== FALSE) {
    $variables['item_attributes'][$first]['class'][] = 'first';
    $variables['item_attributes'][$last]['class'][] = 'last';
  }

Note that $i is required because it is not guaranteed that every delta value of $element['#items'] will end up in $variables['items'].

For the same reason we can't just say $variables['item_attributes'][0]['class'] = 'first';

And once again we can't say
$max_delta = array_pop(array_keys($element['#items'])); $variables['item_attributes'][$max_delta]['class'] = 'last';

And finally, the current logic to determine even and odd is flawed because there may be a gap in delta values, ie you might have 0, 1, 2, 4, 5, 7, 10.

ON THE OTHER HAND is it safe to assume that every delta value will exist in both arrays, ordered in increments of 1 from 0 to n? This will make the code a little more manageable.

Question 2: what are the policies with changing theme functions and template files in a point release?

If we change the preprocess function to precompute the classes in $item_attributes[$delta]['class'] and then update field.tpl.php to match then we will be messing up people who have overridden one or the other but not both, as they will lose their "field-item" and "even/odd" classes.

If we have to wait for Drupal 8 then will Drupal even support browsers that don't support :first-child and :last-child or :first-of-type and :last-of-type selectors? Probably.

philsward’s picture

I would like to throw in my $0.02 about this issue and expand it further to having a unique css value for each field value. Maybe this is where the delta stuff comes into play, I don't know but I just got done setting up a small used car website where the customer wanted an "availability" field of "In Stock" or "SOLD!". I easily enough setup a Boolean field and proceeded with the true false setup of the field. It works perfect, except that I want the false statement, in this case "SOLD!", to be nice big bold letters colored red. Unfortunately, there is no "smartness" to the fields in a way that lets you theme individual field values. It's all or nothing. It would be nice if there was some way to have a unique css class tied to the true statement, and another unique class for the false statement.

Doing this type of setup would open a lot of theming doors in other facets of the fields as well.

I also just realized this is something that needs to be pushed to all contrib facets of Drupal. Views, Panels etc. Anything that displays the content of Drupal. I just noticed that views rewrites all of the css to accommodate their own styling which is different than the default values. I'm not saying all of the contrib modules need to use the default drupal css, but it would be awesome if they could call on certain aspects of a unique theming approach.

Just thought of something else, what about adding an option for each field for theming purposes. Allow the user to checkbox enable additional css classes to make it more unique. It would be turned off by default therefore not creating a messy environment for all of the fields but only enabling the unique css across the fields that are needing it.

Zachmo’s picture

Status: Needs work » Needs review
StatusFileSize
new2.39 KB

I took a stab at patching this issue. I'm not too happy about the extra loop through the items but it gets the job done. Let me know if you see a more efficient way to do this.

Basically we loop through the items again after the first items array is created and add our first last and even odd classes. These are then added a new item in variables called delta_classes which is a an array of all of the classes for each item keyed on its delta. The loop is based on the item's position in the array and not its delta so we should be getting the true even/odd state.

When a field item is rendered we call this new variable, delta_classes, instead of $delta % 2 ? ' odd' : ' even'. This has been updated in both the fields theme function and the template file.

loopy1492’s picture

I'd like to add to this while we're at it. I think that an identifier should be added for all of the items (not just the first and the last) so that they may be individually selected with css cross-browser. Something like this for the current line 1201 (not sure what it would be on the various suggested patches above)...

$classes = 'field-item item-' . ($delta+1) . ' ' . ($delta % 2 ? 'odd' : 'even');

That would add a "item-1" class to the first item, a "item-2" to the second item, etc. Then you'd only have to check for the last one and add a "last" class.

I've tried this on my own install and it seems to work just fine.

michaelfavia’s picture

Perhaps I'm missing something but this might be a solution that doesn't require a for() loop in D7 (the platform I had the issue in). Ill attach a patch for D8 next and once its accepted/rejected backport to this.

michaelfavia’s picture

StatusFileSize
new1.74 KB

Patch for drupal 8 as promised. Can i get a witness?

What do you think Zachmo?

I can speed it up by avoiding the classes as attributes and just implode them myself if wed like but i though it simplified it quite a bit.

Anthony Pero’s picture

Just trying to do this today, actually. My use case is printing taxonomy tags in my tpl file as inline tags in the "posted by" section. I need to add commas to each item, but, obviously, not to the last. Is there a theme function I can override in template.php to get this working in D7 right away without hacking core? If this should go in the forum instead of here, just let me know... I'm not in the issue queues much, so I'm not sure if this is even appropriate to ask.

effulgentsia’s picture

Not sure yet if #1649780: Remove first/last/odd/even classes in favor of CSS3 pseudo selectors will fly, but linking that here, since if it does, that will affect this issue.

jenlampton’s picture

Version: 8.x-dev » 7.x-dev
Status: Needs review » Needs work

We decided not to support first and last CSS classes in D8 because we can now use CSS3 psuedo-selectors to grab the first and last items. I'm changing the version back to D7 since you still may want these classes on older versions of Drupal, but the patch will need to be re-rolled so I'm also changing the status back.

*edit* looks like @effulgentsia and I were cross-posting :) Sorry about that.

damienmckenna’s picture

Status: Needs work » Needs review
StatusFileSize
new1.73 KB

Rerolled against D7.

sokrplare’s picture

#23 applied beautifully for me. Probably can RTBC here.

dcam’s picture

Status: Needs review » Reviewed & tested by the community
StatusFileSize
new22.69 KB
new15.24 KB

#23 works for me too. Before and after images are attached.

yched’s picture

Status: Reviewed & tested by the community » Needs work

Replacing "odd" with "field-item-odd" cannot fly for D7. We cannot change existing markup, existing sites rely on that.

damienmckenna’s picture

Status: Needs work » Needs review
StatusFileSize
new1.71 KB

Rerolled to fix the issue raised by yched.

yched’s picture

Status: Needs review » Reviewed & tested by the community

Thanks !
Looks good to me :-)

David_Rothstein’s picture

Version: 7.x-dev » 8.x-dev
Status: Reviewed & tested by the community » Needs review

#1649780: Remove first/last/odd/even classes in favor of CSS3 pseudo selectors hasn't had any activity in two months, and we can't guarantee that it will make it into Drupal 8. To avoid introducing regressions, we have to have some fix for this in Drupal 8 before we fix Drupal 7, so that means either postponing this issue on that one (which seems silly) or just fixing this in Drupal 8 quickly first (which seems a lot simpler).

(Actually, the current patch there looks like it still leaves the <div class="..."> stuff behind, so it's missing some good things that this patch does, so we really should just keep this issue separate.)

Looking at the patch, it looks good but why doesn't it change field.tpl.php also? That was discussed earlier in the issue, but I'm not sure why it was dropped.

Question 2: what are the policies with changing theme functions and template files in a point release?

If we change the preprocess function to precompute the classes in $item_attributes[$delta]['class'] and then update field.tpl.php to match then we will be messing up people who have overridden one or the other but not both, as they will lose their "field-item" and "even/odd" classes.

In general, see http://drupal.org/node/1527558. So, this is definitely worth thinking about.

As far as I understand this patch, it will not affect someone who has overridden field.tpl.php or theme_field() (although I suppose it could result in them getting something like <div class=".." class=".."> in their HTML)? It will affect someone who has totally replaced template_preprocess_field() but hasn't replaced the theme function itself. That seems relatively unlikely to me, but at least we'd need a release notes mention for this.

Keeping the changes inside the theme/template only (for Drupal 7) does sound like it'd be a bit safer overall, though...

damienmckenna’s picture

Version: 8.x-dev » 7.x-dev
StatusFileSize
new2.09 KB

@David_Rothstein: Going back to D7 for a second, just to make sure I'm making the correct change, is this what you're thinking of? The change to field.tpl.php feels a little unwieldy, any tips for streamlining it?

damienmckenna’s picture

Version: 7.x-dev » 8.x-dev
StatusFileSize
new2.13 KB

A d8 version of the patch from #30.

Status: Needs review » Needs work

The last submitted patch, drupal-n964288-31-d8.patch, failed testing.

David_Rothstein’s picture

Damien, yeah, something along those lines - if we don't do it in preprocess we'd have to do it inline (and repeat the code in theme_field() and field.tpl.php). It's ugly and I'm not sure there's any way to simplify it (it could be made more readable by breaking it up onto multiple lines, though).

For Drupal 8, I'd definitely stick with doing it in preprocess, as the earlier patches did (since that is clearly better); I apologize if I implied otherwise. For Drupal 7, I guess it's still an open question which way to go, depending on what we think we might break.

mpotter’s picture

Patch in #30 is failing in 7.22

Sorry, nevermind, wrong patch

mpotter’s picture

Issue summary: View changes

Add the delta case and spell out the issue clearly

loopy1492’s picture

So, it looks like the patch works for D7. I was wondering if we need to do anything to have it rolled into a release? Sorry if that's a dumb question, but I'm ignorant of how this works.

star-szr’s picture

Issue summary: View changes
Status: Needs work » Closed (won't fix)

#1649780: Remove first/last/odd/even classes in favor of CSS3 pseudo selectors got in so this won't fly for Drupal 8. I'm going to close this issue because I don't think we can change any D7 core markup, even just adding new classes has the potential of breaking existing sites.

It seems to me that if these classes are needed for a D7 theme they can be added in a theme or module preprocess function or template override.

loopy1492’s picture

So, I am far more seasoned with Drupal than I used to be and I feel Cottser is completely right here. I took an hour and modified field.tpl.php to give us classes for first, last, and the numerical order of the field item.

Of course, I don't think this is in the "Drupal" way, but I absolutely hate the convoluted nature of in-line conditionals, so someone can do it up proper if they want. At any rate, this works a treat on my site.

<div class="<?php print $classes; ?>"<?php print $attributes; ?>>
  <?php if (!$label_hidden): ?>
    <div class="field-label"<?php print $title_attributes; ?>><?php print $label ?>:&nbsp;</div>
  <?php endif; ?>
  <div class="field-items"<?php print $content_attributes; ?>>
    <?php foreach ($items as $delta => $item): ?>
    <?php
      //get the numeral
      $itemNumeral = $delta+1;
      //give this item a class based on which item it is in the list
      $additionalClasses = 'item-' . $itemNumeral;
      // if this is the first item, give it the "first" class
      if ($itemNumeral == 1){
        $additionalClasses .= ' first';
      }
      // if this is the last item, give it the "last" class
      if ($itemNumeral == count($items)){
        $additionalClasses .= ' last';
      }      
    ?>
    <div class="field-item <?php print $additionalClasses?> <?php print $delta % 2 ? 'odd' : 'even'; ?>"<?php print $item_attributes[$delta]; ?>><?php print render($item); ?></div>
    <?php endforeach; ?>
  </div>
</div>

Of course, you don't want to alter the one in the core field module. You'll want to put this template in your Theme folder.