Changing Primary Links to Images

On my site, I wanted to use images instead of text for the primary tabs, so I came up with a method to do so. You'll want to look for where the primary links are generated in page.tpl.php. The default is this:

  <?php if (is_array($primary_links)) : ?>
    <ul id="primary">
    <?php foreach ($primary_links as $link): ?>
      <li><?php print $link?></li>
    <?php endforeach; ?>
    </ul>
  <?php endif; ?>

And you'll want to replace it with this snippet:

<?php if(is_array($primary_links)) : ?>
  <ul id="primary">
    <?php
      $l2i
['pattern'] = '$(<a.*>)(.*)(</a>)$ie';
     
$l2i['themepath'] = url($directory);
     
$l2i['replacement'] = "\"\\1\".'<img src=\"$l2i[themepath]/icon_'.str_replace(' ','_',strtolower('\\2')).'.png\" alt=\"\\2\" />\\3'";
      foreach (
$primary_links as $link) {
        print
"<li>".preg_replace($l2i['pattern'],$l2i['replacement'],$link)."</li>\n";
      }
      unset(
$l2i);
   
?>

  </ul>
<?php endif; ?>

The images should be stored in your themes directory. You should make your icon names be in the format of "icon_menu_item.png", where "Menu Item" is the title of the Link. Here's some more examples:

Pictures - "icon_pictures.png"
Graphic Design - "icon_graphic_design.png"

Basically, you'll want to make the title lowercase, change the spaces to underscores, and wrap it with "icon_" and ".png".

images in the menu

mortendk - October 11, 2006 - 13:25

I came up with another solution.
Its almost doing the same thing but im also checking to se if the menu elm is active, and i put it inside my template.php file

The images is placed in: THEME_NAME/images/menu/
normale menu files: menu_1.gif, menu_2.gif ... etc
active menu files:menu_1_active.gif, menu_2_active.gif ... etc

template.php:

function phptemplate_menu_links($links) {
if (!count($links)) {
return '';
}

$i="1";
foreach ($links as $index => $link) {
//image
if (stristr($index, "active")) {
$img = '<a href="$1" title="$2"><img src="'.base_path() . path_to_theme().'/images/menu/menu_'. $i .'_active.gif" alt="$3"></a>';
}else{
$img = '<a href="$1" title="$2"><img src="'.base_path() . path_to_theme().'/images/menu/menu_'. $i .'.gif" alt="$3"></a>';
}
$output .= preg_replace('/<a href="(.*?)" title="(.*?)">(.*?)<\\/a>/i', $img, $link);

$output .= "\n";
$i++;
}
return $output;
}

call the menu inside page.php
if (isset($primary_links)) {
print phptemplate_menu_links($primary_links);
}

enjoy :)
/morten.dk *king of Denmark*

Not working

saiprasad_saiprasad - April 17, 2009 - 06:22

Hi mortendk , it is not working . it returns as "array". image is not displayed. i am using drupal6.10

------------------------
Sai prasad

Another solution

bdornbush - March 4, 2007 - 00:58

I modified the approach in http://drupal.org/node/110199 so that I can replace a menu name with an arbitrary image as follows:

In template.php, instead of rewriting theme_menu_item_link(),
rewrite theme_menu_links() as follows:

<?php
function phptemplate_menu_links($links) {
  if (!
count($links)) {
    return
'';
  }
 
$level_tmp = explode('-', key($links));
 
$level = $level_tmp[0];
 
$output = "<ul class=\"links-$level\">\n";
  foreach (
$links as $index => $link) {
   
$output .= '<li';
    if (
stristr($index, 'active')) {
     
$output .= ' class="active"';
    }
    if (
strpos($link['title'], '<img') === 0) {
     
$output .= ">". l($link['title'], $link['href'], $link['attributes'], $link['query'], $link['fragment'], FALSE, TRUE) ."</li>\n";
    }
    else {
       
$output .= ">". l($link['title'], $link['href'], $link['attributes'], $link['query'], $link['fragment']) ."</li>\n";
    }
  }
 
$output .= '</ul>';

  return
$output;
}
?>

Icons in Primary Links

kasiawaka - April 7, 2008 - 13:41

This is another solution that allows to add images to the primary links. This is a combined solution from a few posts and themes. Also, I tested it on Drupal 5.7 - works OK.

1. FIRST SOLUTION: add images as a background

1a. Add the below code to template.php (template.php is located inside the themes/theme_you_are_using/):

<?php
// allows to display icons with the primary links
function primary_links_add_icons() {
 
$links = menu_primary_links();
 
$level_tmp = explode('-', key($links));
 
$level = $level_tmp[0];
 
$output = "<ul class=\"links-$level\">\n";   
  if (
$links) {
    foreach (
$links as $link) {
      
$link = l($link['title'], $link['href'], $link['attributes'], $link['query'], $link['fragment']);
      
$cssid = str_replace(' ', '_', strip_tags($link));
      
$output .= '<li id="'.$cssid.'">' . $link . '</li>';
    };
   
$output .= '</ul>';
  }
  return
$output;
}
?>

1b. Change primary links code in the page.tpl.php (page.tpl.php is located inside the themes/theme_you_are_using/)

Original code in Zen themes (code may vary from theme to theme but basically we are looking for $primary_links var)

          <?php if ($primary_links): ?>
            <div id="primary">
              <?php print theme('links', $primary_links); ?>
            </div> <!-- /#primary -->
          <?php endif; ?>

Change to

<?php
<!-- changed to display primary links with icons-->                   
    <?
php if ($primary_links): ?>

           <div id="primary">
      <?php print primary_links_add_icons(); ?>  //name of the function must match the name used above in template.php
            </div> <!-- /#primary -->
         <?php endif; ?>
?>

1c. Add to the styles.css (style.css is located inside the themes/theme_you_are_using/)

li#Articles_and_Stories a{
display: block;
background: url(../images/icons/articles_and_stories.gif) no-repeat top center;
}
li#Assess_Skills a{
display: block;
background: url(../images/icons/assess_skills.gif) no-repeat top center;
}
li#Do_Activities a{
display: block;
background: url(../images/icons/do_activities.gif) no-repeat top center;
}

Where "Articles_and_Stories", "Assess_Skills", "Do_Activities" are the primary links menu items with titles like "Articles and Stories", "Assess Skills", "Do Activities". Remember to add _ between the words used in the menu title to create a proper css code.

2. SECOND SOLUTION: add images using img src tag (which gives more options to manipulate the layout)

2a. Add the below code to template.php (template.php is located inside the themes/theme_you_are_using/):

function primary_links_add_icons() {
  $links = menu_primary_links();
  $level_tmp = explode('-', key($links));
  $level = $level_tmp[0];
  $output = "<ul class=\"links-$level\">\n";
  if ($links) {
    foreach ($links as $link) {
$link = l($link['title'], $link['href'], $link['attributes'], $link['query'], $link['fragment']);
        $cssid = str_replace(' ', '_', strip_tags($link));
        $output .= '<li id="'.$cssid.'"><img src="'. path_to_theme() .'/images/icons/'. $cssid.'.gif">' . $link . '</li>';
    };
  $output .= '</ul>';
  }
  return $output;
}

In my case, icons are store in the current themes directory, in images/icons/ folder and have extension .gif but that can be changed by changing the line

       $output .= '<li id="'.$cssid.'"><img src="'. path_to_theme() .'/images/icons/'. $cssid.'.gif"><br />' . $link . '</li>'; 

2b. Change primary links code in the page.tpl.php (page.tpl.php is located inside the themes/theme_you_are_using/) - use the same code as listed above under 1b.

3. THIRD SOLUTION: very much like the 2nd, but allows the image to be clickable

I used the original code posted at the top of the page and modified it to include both link title and image.

3a. Add the below code to template.php (template.php is located inside the themes/theme_you_are_using/):

function primary_links_add_icons() {
  $links = menu_primary_links();
  $level_tmp = explode('-', key($links));
  $level = $level_tmp[0];
  $output = "<ul class=\"links-$level\">\n";
  $l2i['pattern'] = '$(<a.*>)(.*)(</a>)$ie';
  $l2i['replacement'] = "\"\\1\".'<img src=\"'. path_to_theme() .'/images/icons/'.str_replace(' ','_',strtolower('\\2')).'.gif\" alt=\"\\2\" />\\2\\3'";
  if ($links) {
    foreach ($links as $link) {
$link = l($link['title'], $link['href'], $link['attributes'], $link['query'], $link['fragment']);
          $output .= '<li>'. preg_replace($l2i['pattern'],$l2i['replacement'],$link) .'</li>';
    };
  $output .= '</ul>';
  }
  return $output;
}

In my case, icons are store in the current themes directory, in images/icons/ folder and have extension .gif but that can be changed by changing the line

         $l2i['replacement'] = "\"\\1\".'<img src=\"'. path_to_theme() .'/images/icons/'.str_replace(' ','_',strtolower('\\2')).'.gif\" alt=\"\\2\" />\\2\\3'";

3b. Change primary links code in the page.tpl.php (page.tpl.php is located inside the themes/theme_you_are_using/) - use the same code as listed above under 1b.

4. FOURTH SOLUTION: display icons (images) but no text

4a. Add the below code to template.php (template.php is located inside the themes/theme_you_are_using/):

function primary_links_icons_only() {
  $links = menu_primary_links();
  $level_tmp = explode('-', key($links));
  $level = $level_tmp[0];
  $output = "<ul class=\"links-$level\">\n";
  $l2i['pattern'] = '$(<a.*>)(.*)(</a>)$ie';
  $l2i['replacement'] = "\"\\1\".'<img src=\"'. path_to_theme() .'/images/icons/'.str_replace(' ','_',strtolower('\\2')).'.gif\" alt=\"\\2\" />\\3'";
  if ($links) {
    foreach ($links as $link) {
$link = l($link['title'], $link['href'], $link['attributes'], $link['query'], $link['fragment']);
          $output .= '<li>'. preg_replace($l2i['pattern'],$l2i['replacement'],$link) .'</li>';
    };
  $output .= '</ul>';
  }
  return $output;
}

4b. Add code to the page.tpl.php or, if you are using CCK module and want to display those links only on certain type of pages (for example, only on title pages and you created Title Pages template, add this code in node-title_pages.tpl.php, located inside the themes/theme_you_are_using/)

<?php
<!-- changed to display primary links with icons but no text-->                   
    <?
php if ($primary_links): ?>

           <div id="primary">
      <?php print primary_links_icons_only(); ?>  //name of the function must match the name used above in template.php
            </div> <!-- /#primary -->
         <?php endif; ?>
?>

Instead of doing the

shane_jordan - June 25, 2008 - 20:11

Instead of doing the preg_replace for option 4, can't you just use option 1 and wrap the link in a span tag. Then, in your CSS set the span tag so that it doesn't display. This is what they do on the csszengarden website to replace words with images using straight CSS. So, for your function, it'd be something like:

<?php
// allows to display icons with the primary links
function primary_links_add_icons() {
 
$links = menu_primary_links();
 
$level_tmp = explode('-', key($links));
 
$level = $level_tmp[0];
 
$output = "<ul class=\"links-$level\">\n";   
  if (
$links) {
    foreach (
$links as $link) {
      
$link = l($link['title'], $link['href'], $link['attributes'], $link['query'], $link['fragment']);
      
$cssid = str_replace(' ', '_', strip_tags($link));
      
$output .= '<li id="'.$cssid.'"><span class="menu-hidden-text">' . $link . '</span></li>';
    };
   
$output .= '</ul>';
  }
  return
$output;
}
?>

And then for the CSS it would be:

.menu-hidden-text {display: hidden; }

Thanks,
Shane

The images aren't completely clickable.

SteveJB - July 12, 2008 - 13:27

Shane, while your code seems more streamlined and was the first code I tried, I had difficulty clicking icons (the active link did not span/cover the entire icon) so I ended up using kasiawaka's 4th option.

Making full image clickable

shane_jordan - July 14, 2008 - 11:52

To make the full image clickable, you really need the span tag inside of the tag. Then, you set the span to that it has a display:none property and the has a display:block property. This will make the tag display across the entire li area. If you wanted to do that, you'd have to replace the text in the $link variable with the span variable. So, it'd be more something like this:

<?php
// allows to display icons with the primary links
function primary_links_add_icons() {
 
$links = menu_primary_links();
 
$level_tmp = explode('-', key($links));
 
$level = $level_tmp[0];
 
$output = "<ul class=\"links-$level\">\n";  
  if (
$links) {
    foreach (
$links as $link) {
      
$link = l('<span class="menu-hidden-text">'.$link['title'].'</span>', $link['href'], $link['attributes'], $link['query'], $link['fragment']);
      
$cssid = str_replace(' ', '_', strip_tags($link));
      
$output .= '<li id="'.$cssid.'">' . $link . '</li>';
    };
   
$output .= '</ul>';
  }
  return
$output;
}
?>

Then, set a css property for the for that UL A to be display: block;

Thanks,
Shane

You don't need the span tag

gas - August 11, 2008 - 11:59

Hi,
Acording to CSSzengarden they are just using the < a > tags to get the image replacement without the need of span.

style.css:

ul.primary-links li a { /*FOR ALL MENU ITEMS*/
text-indent: -10001px;
display: block;
width: 160px;
height: 19px;
}

li#Articles_and_Stories a{ /*MENU ITEM*/
background: url(images/icons/articles_and_stories.gif) no-repeat top left;
}

li#Articles_and_Stories a.active{ /*Set image for at Articles and stories page*/
background: url(images/icons/articles_and_stories2.gif) no-repeat top left;
}

li#Contact a{ /*MENU ITEM*/
background: url(images/icons/contact.gif) no-repeat top left;
}

li#Contact a.active{ /*Set image of this menu item at the contact page*/
background: url(images/icons/contact2.gif) no-repeat top left;
}
...

page.tpl.php

    <?php if ($primary_links): ?>
    <?php print primary_links_add_icons(); ?>  //name of the function must match the name used above in template.php
    <?php endif; ?>

template.php

// allows to display icons with the primary links
function primary_links_add_icons() {
  $links = menu_primary_links();
  $level_tmp = explode('-', key($links));
  $level = $level_tmp[0];
  $output = "<ul class=\"primary-links\">\n"; 
  if ($links) {
    foreach ($links as $link) {
       $cssid = str_replace(' ', '_', strip_tags($link['title'])); //this first
       $link = l($link['title'], $link['href'], $link['attributes'], $link['query'], $link['fragment']);
       $output .= '<li id="'.$cssid.'">' . $link. '</li>'; //no need of span
    };
    $output .= '</ul>';
  }
  return $output;
}

And then, you'll need the gifs inside images/icons folder:
images/icons/articles_and_stories.gif
images/icons/articles_and_stories2.gif
images/icons/contact.gif
images/icons/contact2.gif
...

This way it works for Firefox3 and IE7.

Works great but Bullet shows

wvrent - February 5, 2009 - 18:36

It works really well but I happen to have images with transparency and the bullet from the list is appearing through, both in IE and Firefox. Setting "list-style-type: none" does not work. Turns out, at least with garland, there is ul setting to change the background to an image of a bullet

I had to add the following to my css

ul.primary-links li {
background-image: none;
}

re

jjclint - February 13, 2009 - 12:01

So you simply replace (delete the old syntax line) print for primary links in page.tpl with the new primary_links_add_icons right?

p.s
trying to get rid of the silly bullets now, saw a solution just a post below good stuff :)

p.p.s

jjclint - February 13, 2009 - 12:06

btw - This works in drupal 6 too
the other method mentioned in this thread did not work for me (see my post below)
my only concern is it causes some hidden bug.

If there's a more neat way to achieve this in drupal 6 I'll be happy to know about, Cheers.

for drupal 6

thebenedict - May 6, 2009 - 03:00

This is working great for me, but for reasons described here: http://justinhileman.info/articles/unsupported-operand-types-in-drupal-6x

I needed this to get it working in drupal 6:

$options = array(
  'fragment' => $link['fragment'],
  'query' => $link['query']);
$link = l($link['title'], $link['href'], $options);

Number 4 worked for me but...

oscott - September 19, 2008 - 10:20

I used a modified version of your number 4. and got it working. The only issue i have is with this bit ...

4b. Add code to the page.tpl.php or, if you are using CCK module and want to display those links only on certain type of pages (for example, only on title pages and you created Title Pages template, add this code in node-title_pages.tpl.php, located inside the themes/theme_you_are_using/)

Do you know of a way to get this to stay throughout? if i navigate to a view of for example the shopping cart of my site, the primary links return to text, yet i cannot find and more page.tpl's to add the code to. Unless i am to make note of every type of page in the site and make overides for each.

Thanks for the code by the way.

It should reference the

shane_jordan - September 19, 2008 - 11:04

It should reference the page.tpl.php if there is not one for that specific type of page. The default would always be page.tpl.php and then you can override it by adding other ones based on the type.

- Shane

For Drupal 6?

mios01 - November 14, 2008 - 20:01

This seems to be exactly what I've tried to do for the last couple of hours (plenty of hours that is) in Drupal 6. But, my Google skills doesn't serve me right. Can anyone point me to how this is done in Drupal 6?

Regards

Mikael

I just spent hours looking

Krythis - November 23, 2008 - 02:58

I just spent hours looking through threads and Google searches, but finally found the method to do this

This is the page I found: http://www.kryogenix.org/code/browser/lir/

Example: The code I used to replace the Primary Links title with an the image /themes/garland/images/primary_silver.png:

#block-menu-primary-links H2{
    padding: 40px 0 0 0;
    overflow: hidden;
    background-image: url(images/primary_silver.png);
    background-repeat: no-repeat;
    height: 0px !important;
    height /**/:40px;
}

regarding the http://www.kryogenix.org/code/browser/lir/ method

jjclint - February 13, 2009 - 10:35

Hi
I have the same problem and I'm also looking for a smooth non-hack css solution. I've read the linked article and tried the method myself and all it does for me is to push the actual link so it's seated just under the image and out of the screen. The problem is that the replacing image doesn't actually work as a link so you can't click on it.

I've tried this with a block header as well (thinking that maybe the method only works with non-interactive elements) so I've replaced the left side bar tittle with an image. This time the actual header just seat there visible under the new "replacing" image. maybe it's to do with my css setup and there's more padding than there should be - that's the only thing which will pops into my head right now that can cause this problem, because the height is set to 0.

Anyway regarding the first problem I've mentioned (items aren't interactive) I'm clueless. Any insightsm what am I doing wrong here???

p.s
maybe I should also mention that I'm using D6

For D6

avdp - February 16, 2009 - 20:31

Hi, I just did some 'plumbing' for D6. I am actually not familiar with PHP, but I got it to work using the error messages:

Changed this:
$l2i['replacement'] = "\"\\1\".'<img src=\"'. path_to_theme() .'/images/titles/'.str_replace(' ','_',strtolower('\\2')).'.png\" alt=\"\\2\" />\\2\\3'";
to this: (find the /)
$l2i['replacement'] = "\"\\1\".'<img src=\"/'. path_to_theme() .'/images/titles/'.str_replace(' ','_',strtolower('\\2')).'.png\" alt=\"\\2\" />\\2\\3'";

and modified:

foreach ($links as $link) {
  $options = array(
    'fragment' => $link['fragment'],
    'query' => $link['query']);
  $link = l($link['title'], $link['href'], $options);
  $output .= '<li>'. preg_replace($l2i['pattern'],$l2i['replacement'],$link) .'</li>';
};

Don't know if this is proper code, but it works anyway,
regards/

Solution 4 works best (IMHO!)

anneeasterling - December 31, 2008 - 15:24

After searching and testing different options for a couple of days, Solution 4 worked best on a site that I'm working on (currently in dev; will post live link when it's finished). Thank you, Kasia!

I had to make one modification. In order to form the proper path for the img, an extra / is needed in before '. path_to_theme() .' I'm also using png's instead of gif's, so this is the code that works on my D5.14 site:

// Use icons for primary menu
function primary_links_icons_only() {
  $links = menu_primary_links();
  $level_tmp = explode('-', key($links));
  $level = $level_tmp[0];
  $output = "<ul class=\"links-$level\">\n";
  $l2i['pattern'] = '$(<a.*>)(.*)(</a>)$ie';
  $l2i['replacement'] = "\"\\1\".'<img src=\"/'. path_to_theme() .'/images/icons/'.str_replace(' ','_',strtolower('\\2')).'.png\" alt=\"\\2\" />\\3'";
  if ($links) {
    foreach ($links as $link) {
$link = l($link['title'], $link['href'], $link['attributes'], $link['query'], $link['fragment']);
          $output .= '<li>'. preg_replace($l2i['pattern'],$l2i['replacement'],$link) .'</li>';
    };
  $output .= '</ul>';
  }
  return $output;
}

Thanks again!
Anne

Successfully ported to D6 too.

anneeasterling - April 22, 2009 - 04:26

An update: I ported my site to D6.10 and this code worked there too. See it here: http://mouse-map.com/

Happy day,
Anne

how to make images change on click?

Mr Cronk - April 29, 2009 - 13:22

I used a modified version of number 4 as well. One modification was to get rid of those bullets, as the above methods didn't work for me.

I used this in the .ccs to get rid of those bullets:

#navbar ul {
list-style: none;
}

Second was the use of a second image to make the buttons clickable:

li#Home a.active{ /*MENU ITEM*/
background: url(images/icons/button_home_clicked.gif) no-repeat top right;
}
li#Home a{ /*MENU ITEM*/
background: url(images/icons/button_home.gif) no-repeat top right;
}

The problem with this was that the buttons don't really "click". The image changes once the link is active, but not on the mouse click event. Using a.hover didn't seem to work.

Can anyone suggest a way of changing the image on the mouse click event?

Instead of using a.hover use

crosputni - September 20, 2009 - 22:22

Instead of using a.hover use a:hover

changing primary links DRUPAL 6x

cygii - August 19, 2009 - 15:44

I've got question. How can I change primary links to something like that :

<li><a href="#">

<img src="####.gif" alt="" />

<span>TEXT TEXT</span>

</a></li>

Removing apostrophes from the menu links

hfidgen - September 10, 2009 - 13:22

Hiya,

This took me several hours to figure out cos it was such a pain in the ass! Read the full details here:
http://stackoverflow.com/questions/1404723/pregreplace-strreplace-apostr...

Suffice to say, if you want images linked in your menu, with text on top, this is the code I've been using. There is a commented preg_replace expression which removes the text too if you like.

<?php
function primary_links_add_icons() {
 
$links = menu_primary_links();
 
$level_tmp = explode('-', key($links));
 
$level = $level_tmp[0];
 
$output = "<ul class=\"links-$level\">\n";  
  if (
$links) {
    foreach (
$links as $link) {
          
$link['title'] = str_replace('\'', '', $link['title']);
       
$link = l($link['title'], $link['href'], $link['attributes'], $link['query'], $link['fragment']);
          
$cssid = str_replace(' ', '_', strip_tags($link));
   
/*put the preg_replace here*/   
       
$output .= '<li id="'.$cssid.'">' . $link .'</li>';
    };
   
$output .= '</ul>';
  }
  return
$output;
}
?>

This is the preg_replace code - it appears to break the php output above:
/*$link = preg_replace('#().*?()#', '$1$2', $link);*/

 
 

Drupal is a registered trademark of Dries Buytaert.