A Drupal site typically comes with several content lists e.g. a list of news teasers, a list of 'related items' in the sidebar etc. With a few simple steps, we can make sure all of them have the same HTML output, giving the advantage of clean and reusable css.
The theme function used to theme lists in Drupal is theme_item_list(), which gives us a few advantages:
- The wrapper class '.item-list' provides an easy way of seperating the css from normal lists that clients would add in their content.
- A '.first' and '.last' class are added to the first and last items in the list. This is usefull in cases where we want to add a border between items in your list, but not under the last one.
This is how we can really optimize the usage of the item-list:
1. Optimize the item-list classes
With a few extra lines of code, it is possible to make the item-list output even beter. Copy the code below in your template.php file, and your item-lists will now also have an odd/even class, and even a class that counts the elements: item-1, item-2 etc.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | /** * add extra parameters to item lists */ function phptemplate_item_list($items = array(), $title = NULL, $type = 'ul', $attributes = NULL) { $output = '<div class="item-list clear-block">'; if (isset($title)) { $output .= '<h3>'. $title .'</h3>'; } if (!empty($items)) { $output .= "<$type". drupal_attributes($attributes) .'>'; $num_items = count($items); foreach ($items as $i => $item) { $attributes = array(); $children = array(); if (is_array($item)) { foreach ($item as $key => $value) { if ($key == 'data') { $data = $value; } elseif ($key == 'children') { $children = $value; } else { $attributes[$key] = $value; } } } else { $data = $item; } if (count($children) > 0) { $data .= theme_item_list($children, NULL, $type, $attributes); // Render nested list } $list_id = $i + 1; $attributes['class'] = empty($attributes['class']) ? 'item-'. $list_id : ($attributes['class'] .' item-'. $list_id); if ($i == 0) { $attributes['class'] = empty($attributes['class']) ? 'first' : ($attributes['class'] .' first'); } if ($i == $num_items - 1) { $attributes['class'] = empty($attributes['class']) ? 'last' : ($attributes['class'] .' last'); } if ($i%2) { $attributes['class'] = empty($attributes['class']) ? 'even' : ($attributes['class'] .' even'); } else { $attributes['class'] = empty($attributes['class']) ? 'odd' : ($attributes['class'] .' odd'); } $output .= '<li'. drupal_attributes($attributes) .'>'. $data ."</li>\n"; } $output .= "</$type>"; } $output .= '</div>'; return $output; } |
Don't forget to clear cache after adding the function!
2. Configure your views
The Views module is the best choice for generating content lists. By default, the output 'style' of a view is set to 'Unformatted'. Change this to 'HTML List' for all your views and then set the 'List type' to 'Unordered list'.
3. Update your views template file
If you now check the html output of an item-list generated by views, you will notice that the extra classes we added in our theme function overwrite earlier, are not shown in he list outputted by views. That's because views does not actually use that theme function. Instead, the item-list output is emulated by a template file that can be found under /sites/all/modules/views/theme/views-view-list.tpl.php. Copy this file to your theme folder, and change the output to the code below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | <?php // $Id$ /** * @file views-view-list.tpl.php * Default simple view template to display a list of rows. * * - $title : The title of this group of rows. May be empty. * - $options['type'] will either be ul or ol. * @ingroup views_templates */ ?> <div class="item-list"> <?php if (!empty($title)) : ?> <h3><?php print $title; ?></h3> <?php endif; ?> <<?php print $options['type']; ?> class="clear-block"> <?php foreach ($rows as $id => $row): ?> <?php $list_id = $id+1; $list_classes = 'item-'.$list_id; if ($list_id == 1){ $list_classes .= ' first'; } if ($list_id == count($classes)){ $list_classes .= ' last'; } if ($list_id % 2) { $list_classes .= ' odd'; } else { $list_classes .= ' even'; } ?> <li class="<?php print $list_classes; ?>"><?php print $row; ?></li> <?php endforeach; ?> </<?php print $options['type']; ?>> </div> |
The HTML code generated by views is now the same as if you would use theme_item_list().
4. Configure your custom modules
If you have any custom modules that generate lists of content, now is the time to make them use the theme_item_list() function.
5. Fix theme_pager()
Now that all content lists have the same output, there's one list that actually shouldn't be printed as an item list: the pager. Typically we would want our content lists to look like vertical lists, and the pager as a horizontal list, so the item-list wrapper is useless here and would require a lot of css overwrites.
The functions below are taken from the Tao theme. Add them to your template.php, and the pager will now be wrapped in a div with a 'pager' class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | /** * Override of theme_pager(). * Easily one of the most obnoxious theming jobs in Drupal core. * Goals: consolidate functionality into less than 5 functions and * ensure the markup will not conflict with major other styles * (theme_item_list() in particular). */ function phptemplate_pager($tags = array(), $limit = 10, $element = 0, $parameters = array(), $quantity = 9) { $pager_list = theme('pager_list', $tags, $limit, $element, $parameters, $quantity); $links = array(); $links['pager-first'] = theme('pager_first', ($tags[0] ? $tags[0] : t('First')), $limit, $element, $parameters); $links['pager-previous'] = theme('pager_previous', ($tags[1] ? $tags[1] : t('Prev')), $limit, $element, 1, $parameters); $links['pager-next'] = theme('pager_next', ($tags[3] ? $tags[3] : t('Next')), $limit, $element, 1, $parameters); $links['pager-last'] = theme('pager_last', ($tags[4] ? $tags[4] : t('Last')), $limit, $element, $parameters); $links = array_filter($links); $pager_links = theme('links', $links, array('class' => 'links pager pager-links')); if ($pager_list) { return "<div class='pager clear-block'>$pager_list $pager_links</div>"; } } /** * Split out page list generation into its own function. */ function phptemplate_pager_list($tags = array(), $limit = 10, $element = 0, $parameters = array(), $quantity = 9) { global $pager_page_array, $pager_total, $theme_key; if ($pager_total[$element] > 1) { // Calculate various markers within this pager piece: // Middle is used to "center" pages around the current page. $pager_middle = ceil($quantity / 2); // current is the page we are currently paged to $pager_current = $pager_page_array[$element] + 1; // first is the first page listed by this pager piece (re quantity) $pager_first = $pager_current - $pager_middle + 1; // last is the last page listed by this pager piece (re quantity) $pager_last = $pager_current + $quantity - $pager_middle; // max is the maximum page number $pager_max = $pager_total[$element]; // End of marker calculations. // Prepare for generation loop. $i = $pager_first; if ($pager_last > $pager_max) { // Adjust "center" if at end of query. $i = $i + ($pager_max - $pager_last); $pager_last = $pager_max; } if ($i <= 0) { // Adjust "center" if at start of query. $pager_last = $pager_last + (1 - $i); $i = 1; } // End of generation loop preparation. $links = array(); // When there is more than one page, create the pager list. if ($i != $pager_max) { // Now generate the actual pager piece. for ($i; $i <= $pager_last && $i <= $pager_max; $i++) { if ($i < $pager_current) { $links["$i pager-item"] = theme('pager_previous', $i, $limit, $element, ($pager_current - $i), $parameters); } if ($i == $pager_current) { $links["$i pager-current"] = array('title' => $i); } if ($i > $pager_current) { $links["$i pager-item"] = theme('pager_next', $i, $limit, $element, ($i - $pager_current), $parameters); } } return theme('links', $links, array('class' => 'links pager pager-list')); } } return ''; } /** * Return an array suitable for theme_links() rather than marked up HTML link. */ function phptemplate_pager_link($text, $page_new, $element, $parameters = array(), $attributes = array()) { $page = isset($_GET['page']) ? $_GET['page'] : ''; if ($new_page = implode(',', pager_load_array($page_new[$element], $element, explode(',', $page)))) { $parameters['page'] = $new_page; } $query = array(); if (count($parameters)) { $query[] = drupal_query_string_encode($parameters, array()); } $querystring = pager_get_querystring(); if ($querystring != '') { $query[] = $querystring; } // Set each pager link title if (!isset($attributes['title'])) { static $titles = NULL; if (!isset($titles)) { $titles = array( t('« first') => t('Go to first page'), t('‹ previous') => t('Go to previous page'), t('next ›') => t('Go to next page'), t('last »') => t('Go to last page'), ); } if (isset($titles[$text])) { $attributes['title'] = $titles[$text]; } else if (is_numeric($text)) { $attributes['title'] = t('Go to page @number', array('@number' => $text)); } } return array( 'title' => $text, 'href' => $_GET['q'], 'attributes' => $attributes, 'query' => count($query) ? implode('&', $query) : NULL, ); } |
That's it, now update your css to make your content lists look the way you want!

Comments