Hello,

I'm currently creating a new theme for my Drupal based website. The theme is named 'Entreo'.

I have a question about entreo_menu_item(). Is is possible for a certain item to 'know' if it's a child? I want to change the menu button layout depending on the level of the item. E.g. the first level items have different layouts in comparison with the second level, and the third level.

Thanks!

Comments

markdingemanse’s picture

I think your solution may lie in clever use of the CSS selectors. For example, the following CSS declarations:

.navigation ul li {
background-color:red;
}
.navigation ul li ul li {
background-color:blue
}
.navigation ul li ul li ul li {
background-color:green;
}

...should give the first level of menu-items a red background, the second level a blue, and the third level a green background. The first declaration targets all li's that are child of an ul -- i.e. all li's in the div that has class="navigation". However, the second declaration, being more specific, overrules part of the first declaration and picks out all children and grandchildren of the first level. The third declaration is even more specific and thus overrules the earlier two as regards to children of children.

The key here is the C in CSS: Cascading StyleSheets. Citing the W3 CSS specification, [t]he CSS cascade assigns a weight to each style rule. When several rules apply, the one with the greatest weight takes precedence. In this game, greater specifity equals more weight and that's why the above will work in picking out different levels of your menu.

eyos’s picture

Hi Mark. Thanks for your answer. My problem is that the menu layout is a bit to complicated to use ul and li, or to just do the layout with CSS. Well, at least... I find it to complicated :) For that reason, I work with a combination of table rows and div's.

markdingemanse’s picture

Even when your menu is not outputted in the form of nested lists, this method should work in principle. That's because the basic principles of CSS selectors I laid out above apply to every html element.

However, speaking from my experience in web design I can say that it is highly advisable to use semantically sound HTML (i.e. using lists for listy things like menus) and to maintain a strict separation of content and presentation (i.e. using HTML for the former and CSS for the latter). It makes your sites much more flexible, future proof, intuitive, and (last but not least) search engine friendly.

Anyway, I guess it's best to post the HTML output you have now so that we can figure out the best solution to your problem, be it CSS or something like theme override functions.

eyos’s picture

I agree that using CSS is actually the cleanest solution. I keep having problems with it though.. I find it hard to create pages based on div's and css.. But maybe I just need to practice more.

I use the following HTML for the menu:

<table width="220" border="0" cellspacing="0" cellpadding="0">
  <tr>
    <td width="3" background="/themes/entreo/images/borders/left_1.gif"><img alt="" src="/themes/entreo/images/borders/left_1.gif" width="3" height="13" /></td>
    <td width="220" valign="top" bgcolor="#FFFFFF"><table width="220"  border="0" cellspacing="0" cellpadding="0">
        <tr>
          <td width="5" height="15"><img src="themes/entreo/images/menuelements/section_background_left.gif" width="5" height="16"></td>
          <td width="210" height="15" background="themes/entreo/images/menuelements/section_background.gif" class="menu-title"><a href="./" class="menu-title">Section
              1</a></td>
          <td width="5" height="15"><img src="themes/entreo/images/menuelements/section_background_right.gif" width="5" height="16"></td>
        </tr>
        <tr>
          <td height="1" colspan="3"><img src="themes/entreo/images/menuelements/line.gif" width="220" height="1"></td>
        </tr>
        <tr>
          <td width="5" height="15"><img src="themes/entreo/images/menuelements/section_background_left.gif" width="5" height="16"></td>
          <td height="15" background="themes/entreo/images/menuelements/section_background.gif" class="menu-title"><a href="news.php" class="menu-title">Section
              2</a></td>
          <td width="5" height="15"><img src="themes/entreo/images/menuelements/section_background_right.gif" width="5" height="16"></td>
        </tr>
        <tr>
          <td height="1" colspan="3"><img src="themes/entreo/images/menuelements/line.gif" width="220" height="1"></td>
        </tr>
        <tr>
          <td height="15"><img src="themes/entreo/images/menuelements/subsection_background_1_left.gif" width="5" height="16"></td>
          <td height="15" background="themes/entreo/images/menuelements/subsection_background_1_mid.gif" class="menu-item"><a href="node/1" class="menu-item">Page
              1</a></td>
          <td height="15"><img src="themes/entreo/images/menuelements/subsection_background_1_right.gif" width="5" height="16"></td>
        </tr>
        <tr>
          <td height="1" colspan="3"><img src="themes/entreo/images/menuelements/subsection_line.gif" width="220" height="1"></td>
        </tr>
        <tr>
          <td height="15"><img src="themes/entreo/images/menuelements/subsection_background_2_left.gif" width="5" height="16"></td>
          <td height="15" background="themes/entreo/images/menuelements/subsection_background_2_mid.gif" class="menu-item"> <a href="node/2" class="menu-item">Page
              2</a></td>
          <td height="15"><img src="themes/entreo/images/menuelements/subsection_background_2_right.gif" width="5" height="16"></td>
        </tr>
        <tr>
          <td height="1" colspan="3"><img src="themes/entreo/images/menuelements/subsection_line.gif" width="220" height="1"></td>
        </tr>
        <tr>
          <td height="15"><img src="themes/entreo/images/menuelements/section_background_left.gif" width="5" height="16"></td>
          <td height="15" background="themes/entreo/images/menuelements/section_background.gif" class="menu-title"><a href="forums" class="menu-title">Forum</a></td>
          <td height="15"><img src="themes/entreo/images/menuelements/section_background_right.gif" width="5" height="16"></td>
        </tr>
        <tr>
          <td height="1" colspan="3"><img src="themes/entreo/images/menuelements/line.gif" width="220" height="1"></td>
        </tr>
        <tr>
          <td height="15"><img src="themes/entreo/images/menuelements/subsection_background_1_left.gif" width="5" height="16"></td>
          <td height="15" background="themes/entreo/images/menuelements/subsection_background_1_mid.gif" class="menu-item"><a href="node/3" class="menu-item">Item
              1</a></td>
          <td height="15"><img src="themes/entreo/images/menuelements/subsection_background_1_right.gif" width="5" height="16"></td>
        </tr>
        <tr>
          <td height="1" colspan="3"><img src="themes/entreo/images/menuelements/subsection_line.gif" width="220" height="1"></td>
        </tr>
        <tr>
          <td height="15"><img src="themes/entreo/images/menuelements/subsection_background_2_left.gif" width="5" height="16"></td>
          <td height="15" background="themes/entreo/images/menuelements/subsection_background_2_mid.gif" class="menu-item"> <a href="node/4" class="menu-item">Item
              2</a></td>
          <td height="15"><img src="themes/entreo/images/menuelements/subsection_background_2_right.gif" width="5" height="16"></td>
        </tr>
        <tr>
          <td height="1" colspan="3"><img src="themes/entreo/images/menuelements/subsection_line.gif" width="220" height="1"></td>
        </tr>
        <tr>
          <td height="15"><img src="themes/entreo/images/menuelements/subsection_background_1_left.gif" width="5" height="16"></td>
          <td height="15" background="themes/entreo/images/menuelements/subsection_background_1_mid.gif" class="menu-item"> <a href="node/5" class="menu-item">&nbsp;&nbsp;Subitem
              1</a></td>
          <td height="15"><img src="themes/entreo/images/menuelements/subsection_background_1_right.gif" width="5" height="16"></td>
        </tr>
        <tr>
          <td height="1" colspan="3"><img src="themes/entreo/images/menuelements/line.gif" width="220" height="1"></td>
        </tr>
        <tr>
          <td height="15"><img src="themes/entreo/images/menuelements/section_background_left.gif" width="5" height="16"></td>
          <td height="15" background="themes/entreo/images/menuelements/section_background.gif" class="menu-title"><a href="search" class="menu-title">Search </a></td>
          <td height="15"><img src="themes/entreo/images/menuelements/section_background_right.gif" width="5" height="16"></td>
        </tr>
        <tr>
          <td height="1" colspan="3"><img src="themes/entreo/images/menuelements/line.gif" width="220" height="1"></td>
        </tr>
      </table>
    </td>
  </tr>
  <tr>
    <td colspan="2"><img alt="" src="/themes/entreo/images/borders/menuborder_1.gif" width="223" height="7" /></td>
  </tr>
</table>

I guess the above code is horrifying to you?! ;)

An image of the menu as I would like it to be can be found here. The menu items are of course not real.

markdingemanse’s picture

Well, since you're asking: frankly, yes, that does look like horrible code to me. In any case, since you've lost all of the semantics that comes with the normal output of functions like theme_menu_tree(), it's going to be quite a dirty hack to add in 'child-awareness' somewhere. Too dirty for me, in fact (but maybe I'm stubborn), so I won't be able to help you with your current code.

I can show however what my solution would be. Starting with the default output of theme_menu_tree($entreo1) (assuming you have a menu called 'entreo1'), I'd do something like this to create a menu like your image:

<h2>HTML:</h2>
<code>
<div id="entreo1">
	<ul>
		<li><a href="#">Section 1</a></li>
		<li><a href="#">Section 2</a>
			<ul>
				<li><a href="#">Page 1</a></li>
				<li><a href="#">Page 2</a></li>
			</ul>
		</li>
		<li><a href="#" title="">Forum</a>
			<ul>
				<li><a href="#">Item 1</a></li>
				<li><a href="#">Item 2</a>
					<ul>
						<li><a href="#">Subitem 1</a>
					</ul>
				</li>
			</ul>
		</li>
		<li><a href="#">Search</a></li>
	</ul>
</div>

<h2>CSS:</h2>
<code>
#entreo1 ul {
	/* set some defaults */
	background-color:#fff;
	margin:0;
	padding:0;
	font-family:Verdana, Arial, Helvetica, sans-serif;
	font-size:10px;
	list-style:none;
	list-style-position:inside;
}
#entreo1 ul li {
	/* more default settings, and the bg for the first level */
	background-repeat:no-repeat;
	background-position:top left;
	background-image:url(section_background.gif);
}
#entreo ul li ul li {
	/* the bg for the second and third level */
	background-image:url(subsection_background.gif);
}
#entreo ul li ul li ul li {
	/* the bg for the third level is the same as the second level as far as I can see, but there is some indentation */
	padding-left:8px;
}

(I may have glossed over some default styles and I haven't styled the <a>'s of course, but you get the point.)

As you know, this way of coding has a lot of advantages. It is more accessible and intuitive (and, not unimportantly, this equals better SEO); it is easier to maintain; and it is much more flexible as it is extremely easy to change the layout at a later stage, redesigning or realigning the whole thing.

I hope this helps in any way. If I could seduce you into standards based web design, I'd very much like to, although I know that it can be quite some work to start creating tableless designs if you have some table-based designs lying around already. Yes, it can seem quite hard at first (but surely you remember when you started coding table-based layout; that was hard too, wasn't it?), but it is also very rewarding. And as soon as you've got the hang of it, nothing seems more natural than tableless CSS-based designs, and you'll never want to go back. At least that's my experience.

[Edit: see for example a recent Drupal project of mine for a standards based design implementing different menu layouts for different levels. BTW, I use primary_links() and secondary_links() there.]

eyos’s picture

Thanks again for you help! I actually started with converting the menu to CSS/DIV-based markup immediately. I'm quite satisfied expect for one thing. The background image for the second and third level items isn't shown over the full width of the item. Instead, the background starts with the start of the menu item text.

ITEM 1 -> background has 100% width
__ITEM 2 -> background has a little less than 100% width
____ITEM 3 -> background has a bit more than a little less than 100% width.

I've been playing around with padding and margin, but can't seem to get the right way of indenting the text while maintaining the right width for the background. Any thoughts on that?

markdingemanse’s picture

Ah, I didn't think about that in the above code. The way the CSS box model works, one should indeed expect the bg-image in a <li> to shift if the li is given a left-margin. Giving it a padding-left instead should work, though.

Another solution might be just to give all list items in the menu the same margin and padding, and to fiddle with the placement of the <a> within lower level <li>'s instead (e.g. give that <a> a left padding of 8px). That way you can be sure nothing happens to the background-images of the <li>-elements, while still indenting the text of lower level elements.

eyos’s picture

Great, it works! Never thought of padding the A-element. Thank you very much for your help.

markdingemanse’s picture

You're welcome; always glad to help.

eyos’s picture

It seems I was happy a bit to early. In IE7 and Opera (and possibly Firefox too), the level two and three items aren't shown or are shown on top of the level one items below it. Strangly enough, IE6 does display the menu correct.

This is my HTML:

<div id="menu">
<ul class="menu">
<li class="leaf"><a href="/" title="">Start</a></li>
<li class="leaf"><a href="/aggregator" title="">News</a></li>
<li class="leaf"><a href="/glossary" title="">Glossary</a></li>
<li class="leaf"><a href="/user/2">My account</a></li>
<li class="collapsed"><a href="/node/add" title="">Add content</a></li>
<li class="leaf"><a href="/search" title="">Search</a></li>
<li class="expanded"><a href="/admin" class="active">Admin</a>
<ul class="menu">
<li class="leaf"><a href="/admin/block">Blocks</a></li>
<li class="leaf"><a href="/admin/forum">Forums</a></li>
<li class="leaf"><a href="/admin/user">Users</a></li>
(etc. etc.)

</ul>
</li>
<li class="leaf"><a href="/logout">Logout</a></li>

</ul>
</div>

This is the CSS:

#menu .active {
        color: #0033CC;
}

#menu ul {
        /* set some defaults */
        background-color:#fff;
        margin:0;
        padding:0;
        font-family:Verdana, Arial, Helvetica, sans-serif;
        font-size:10px;
}

#menu ul li {
        /* more default settings, and the bg for the first level */
        background-repeat:repeat-x;
        background-position:top left;
        background-image:url(/themes/entreo/images/menuelements/section_background_v2.gif);
        height: 20px;
        list-style-image: none;
        list-style-type: none;
        padding-top:0px;
        margin-top:0px;
        padding-left:0px;
        margin-left:0px;
}

#menu ul li a {
        font-weight: bold;
        color: #000000;
        text-decoration: none;
        padding-left:8px;
}

#menu ul li a:hover {
        color: #0033CC;
}

#menu ul li ul {
        /* the bg for the second level */
        /*height: 20px;*/
        background-repeat:repeat-x;
        background-position:top left;
        padding-top:1px;
        padding-left:0px;
        margin-left:0px;
        margin-top:5px;
}

#menu ul li ul li {
        /* the bg for the second level */
        background-image:url(/themes/entreo/images/menuelements/subsection_background_v2.gif);
        height: 20px;
        padding-left:0px;
        margin-left:0px;
        width: 100%;
}

#menu ul li ul li a {
        padding-left:16px;
}

#menu ul li ul li a:hover {
        color: #0033CC;
}
#menu ul li ul li ul {
        /* the bg for the third level */
        /*height: 20px;*/
}

#menu ul li ul li ul li {
        /* the bg for the third level */
}

#menu ul li ul li ul li a {
        padding-left:30px;
}

#menu ul li ul li ul li a:hover {
        color: #0033CC;
}

There are probably some CSS-styles that I can reduce because they've been defined at a higer level.

Is there a way to force second and third level items to be displayed just like the level one items?

markdingemanse’s picture

In your third CSS declaration (#menu ul li) you're giving all <li>'s in the menu a 20px height. However, submenus are embedded in these same <li>'s, so obviously you didn't want literally all <li>'s to have that height; those containing submenus need to expand along with their contents. Standards compliant browsers apply the height as expected, so the submenus originate in a 20px space and below that space the following 20px-high <li> follows, resulting in an overlap. The IE6 bug that inadvertently renders your menu as you'd like it to is known as the 'expanding box problem', and this bug is indeed fixed in IE7.

Your solution again lies in specifying the height on the <a> tag rather than on the <li>. Note that since <a> is an inline element (a nice explanation of these concepts can be found here) you'll have to tell it to display as a block level element to be able to set a height. This is done using display:block. So to your #menu ul li a declaration you add the following two lines:

display:block;
height:20px;

...and you remove all height specifications from the declarations targeting <li>-elements. That should do it.

markdingemanse’s picture

As you said yourself already, there's quite some redundancy in your CSS. For example, if you want all links to get a #0033CC color on hover, setting it once should suffice. Thus,

#menu ul li a:hover {
color:#0033CC;
}

should do the trick. If it doesn't, it can only be because this declaration is overruled by a more specific (i.e. more weighty) one, so you'd have to look what other declarations (possibly in other CSS files) speak to this <a>.

Why does it work? Remember that #menu ul li a targets any <a>-element that is embedded in a <li> that is embedded in an <ul> that is located in the element with the id "menu". This applies to a third level <a> as much as to a first level one. That is just the way this particular CSS selector (it is called the descendant selector) works; other selectors (such as the child selector which is unfortunately not supported in IE6) use different pattern matching algorithms.

I suggest just trying which declaration you can pull out while still keeping the presentation like you want it. That is probably one of the easiest ways to learn how powerful descendant selectors are.