*/ ?>

Trying to figure out a tricky drop down menu

I have a pretty tricky drop down menu to figure out for work, wondering if anyone has thoughts on how to create it?

screenshot9.pngI'm not sure entirely how to set this one up.  The menu needs to look like the screenshot to the left when open, with an array of different menus that open up.  Each one is a different width with different text.

The big thing I'm not sure about is setting up the main parent navigations for the menu.  In most autonav situations in concrete 5 I'd be looking at a nested list for outputting the code, but that doesn't really work with the carat.  I don't know exactly how to get that little danging carat to work. 

It all comes down to if I'm using image sprites for the main 'top level' of the nav, or if I'm going to try to make those actually work as list elements with text. If I go with list elements, then I need to make sure that the list elements grow.  The drop shadow would be really hard to do as well, because if I'm doing list elements then the carat is part of a separate div, an the shadow wouldn't be applied to it. 

So I guess with that I'm looking at being forced to use image sprites on the top levels, and then different width background images for the nested ul tag and then an extra div for the bottom carat. 

I went through this with Robert to see what he thinks and he thinks we might be able to do it with css but the carat keeps coming back to me as something that wouldn't work.  The drop shadow is too subtle, I don't think it would work to have the shadow on the carat be from the image png and then have the boxes with a css drop shadow.  Plus then there's trying to position it so that it's centered in all browsers regardless of the width of the parent container.

Sometimes you run into stuff like this from designers, fairly often actually.  The ideas that the designer has for what seems like a simple thing often turn out to not really work in the real world once you start looking at how you can actually slice up the files and turn them into HTML and CSS.  There are often ways to get around it but they're not always the best solution.  Using images for the top level of the navigation is really a pretty old way of doing things and isn't the best way to do stuff.  A well designed menu should be able to work regardless of the content in the menu, here we're going to have to be very tied to exactly what the content of the menu is.  There will be no option for adding new top-level navigation items to this site, or changing the names of any of the pages. I'll have to be generating the class to base the image sprite replacement on the top level based on the collection handle, so it will have to match. 

I'm still actually waiting on files for the project so it's kind of a moot point.  Just trying to get up to speed on some of the trickier elements now while we are waiting for files.  I already made a custom slideshow block for the site.  The client wants the images to fade in as if they are being stacked on the page, each one skewed a little bit differently.  I figured out part of it, I made a slideshow block where you add the images and they stack up with a little skew on each one differently.  The problem is that the jquery library that I'm using to skew the images uses the canvas element to create the skewed image, and apparently you can't target the images within the canvas element with css any more after that.  The design calls for padding, background color and drop shadow on the images, so I'm only partially there. 

In order to get around that one I think we're going to have to use image magic to composite in the images with another PNG that already has the drop shadow and padding and background color applied.  This works but again, only partially.  The problem is older versions of IE.  In both 6 and 7 png support doesn't allow for alpha blending on the drop shadows, so these browsers will end up with an ugly background color instead of a faded drop shadow.  The only way that I know of to get around this is to use a PNG fix on the document, but again, that won't help here because the image is actually a canvas element instead of an image element.

I'm wondering how people do things like drop shadow and padding on rotated images - or really how people skew images on websites in any way.  I found one javascript library that rotates images but that's all it does is images.  You can't skew dom elements.  If you could skew dom elements then I wouldn't have any issues, I could simply apply the css to the dom element that I'm skewing and be done with it.  Unfortunately this didn't really work.  I thought about just using the css transform properties but that doesn't degrade nicely so older browsers wouldn't see the skew.

We're checking with the client now to find out which way they want to go - should it rotate but be ugly in IE 6 & 7, or should it look nice in all browsers but not rotate in IE 6, 7 & 8.  Personally I'd go with the no rotation option because that will look the best, and the people who end up viewing the site in the browsers where it doesn't rotate won't know what they are missing.  Then again, people in IE 6 & 7 are a pretty limited number of people so maybe it's OK to give them a bad looking drop shadow, but keep the skew for IE 8.  That way is a lot harder to figure out how to set up, using image magic to generate out a composite image from a file manager file in concrete 5 is not something that I've ever tried before, that could be pretty tricky.

In other news it was 10 degrees outside this morning when I was getting ready for work and I decided to take the bus into work.  I don't do that too often but sometimes during the winter I just don't have the energy for riding when it's cold. I didn't have the energy this morning. It's a good thing that I didn't ride the whole way, I was overdressed and would have ended up all sweaty and then would have gotten cold.  I'll probably ride home tonight rather than ride the bus though - it was just so boring sitting there doing nothing for the whole commute.  I took some time to catch up on twitter and facebook on the smart phone but that didn't really fill up much time.  I did decide that I really do need to start using twitter more, though.  I pretty much only auto-publish to twitter when there are new blog posts, I really should be actually using it as a social networking platform.  I just put so much more of my effort into facebook and google plus, they both have models I like better.  There's more interaction and back-and-forth between people on facebook.  Twitter is 'hey, this is what I'm doing right now' - it seems very self centered and egotistical, not a networking platform at all.  Still, it is an important tool, I'm kind of silly not to try to use it more.  I used to be mostly twitter before I joined facebook, wonder why it changed.


By the end of the day I had the menu working with superfish and an image sprite.  The carat at the bottom of the sub-menu I did with an extra li tag with a 'cap' class at the end of the list.  For the first list item I'm also calling that "first" so that I can give it extra padding on the top.  This is because we need to be able to pull the sub-list up underneath the a tag in the parent li to get it to look right.  We could just add the padding in on the ul tag but we don't have a background image on the ul tag, otherwise the carat at the bottom wouldn't work.  I could do it with a background image on the ul tag and just do one single large image that's 1000 pixels high or something like that and attached to the bottom of the ul but that seemed kind of silly.  Instead I have two images, one for the bottom and then a 1px repeat that makes up the body.

I also explicitly got rid of any chance that there could be sub-menus, all sub pages will display at the second level.  There's no room in the design for an extra level of navigation. In the actual site I'm restricting sub page display to only the first level with the block controls but someone could change that in the future and I want it to look right.


  1. <?php
  2. defined('C5_EXECUTE') or die("Access Denied.");
  3. $aBlocks = $controller->generateNav();
  4. $c = Page::getCurrentPage();
  5. $containsPages = false;
  7. $nh = Loader::helper('navigation');
  9. //this will create an array of parent cIDs
  10. // this is used to highlight the navigation if the page
  11. // is in the path to the selected page
  12. $inspectC = $c;
  13. $selectedPathCIDs = array($inspectC->getCollectionID());
  14. $parentCIDnotZero = true;
  15. while ($parentCIDnotZero) {
  16. $cParentID = $inspectC->cParentID;
  17. if (!intval($cParentID)) {
  18. $parentCIDnotZero = false;
  19. } else {
  20. $selectedPathCIDs[] = $cParentID;
  21. $inspectC = Page::getById($cParentID);
  22. }
  23. }
  24. // create a placeholder for all hidden pages
  25. $excludedCIDs = array();
  26. foreach ($aBlocks as $ni) {
  27. $_c = $ni->getCollectionObject();
  28. $childrenArray = $_c->getCollectionChildrenArray();
  29. if ($_c->getCollectionAttributeValue('exclude_nav')) {
  30. // Get an array of children cIDs to block from subsequent loops
  31. $excludedCIDs = array_merge($childrenArray, $excludedCIDs);
  32. $excludedCIDs[] = $_c->getCollectionID(); // Add current cID to excluded array
  33. continue; // Move to next page
  34. }
  35. // this only works if you have the exclude_children attribute defined
  36. if ($_c->getCollectionAttributeValue('exclude_children')) {
  37. // Get an array of children cIDs
  38. $excludedCIDs = array_merge($childrenArray, $excludedCIDs);
  39. }
  40. // make sure our page isn't excluded. We're using an array
  41. // to keep from having 'orphaned' pages show up when pages are excluded
  42. // from nav but have children pages in the nav array
  43. if (!in_array($_c->getCollectionID(), $excludedCIDs)) {
  46. $target = $ni->getTarget();
  47. if ($target != '') {
  48. $target = 'target="' . $target . '"';
  49. }
  50. if (!$containsPages) {
  51. // this is the first time we've entered the loop so we print out the UL tag
  52. echo("<ul class='fancy-nav'>");
  53. }
  55. $containsPages = true;
  57. $thisLevel = $ni->getLevel();
  58. if ($thisLevel > $lastLevel) {
  59. // make sure we only make a sub-menu the first time through
  60. if ($ni->getLevel()==1){
  61. echo("<ul>");
  62. }
  63. } else if ($thisLevel < $lastLevel) {
  64. // lastWasHome is set the first time through the loop to
  65. // exclude the home page from navigation. We don't want to close
  66. // any tags because we didn't output anything for the home page
  67. // below
  68. if (!$lastWasHome) {
  69. for ($j = $thisLevel; $j < $lastLevel; $j++) {
  70. if ($lastLevel - $j > 1) {
  71. echo("</li>");
  72. } else {
  73. echo("</li><li class='cap'></li></ul></li>");
  74. }
  75. }
  76. }
  77. } else if ($i > 0) {
  78. if (!$lastWasHome) {
  79. echo("</li>");
  80. }
  81. }
  82. // this will give us our extra padding on the top of the sub menu
  83. if ($thisLevel > $lastLevel && $ni->getLevel()==1){
  84. $firstClass = " first";
  85. } else {
  86. $firstClass = "";
  87. }
  88. if ($lastWasHome) {
  89. $lastWasHome = 0;
  90. }
  91. $pageLink = false;
  92. // this only works if you define replace_link_with_first_in_nav boolean attribute
  93. if ($_c->getCollectionAttributeValue('replace_link_with_first_in_nav')) {
  94. $subPage = $_c->getFirstChild();
  95. if ($subPage instanceof Page) {
  96. $pageLink = $nh->getLinkToCollection($subPage);
  97. }
  98. }
  100. if (!$pageLink) {
  101. $pageLink = $ni->getURL();
  102. }
  103. // if we are on the home page, skip it
  104. if ($_c->getCollectionID() == HOME_CID) {
  105. $lastWasHome = 1;
  106. } else {
  107. // otherwise output our list elements
  108. if ($c->getCollectionID() == $_c->getCollectionID()) {
  109. echo('<li class="' . $_c->getCollectionHandle() . $firstClass . ' selected"><a ' . $target . ' href="' . $pageLink . '">' . $ni->getName() . '</a>');
  110. } elseif (in_array($_c->getCollectionID(), $selectedPathCIDs)) {
  111. echo('<li class="' . $_c->getCollectionHandle() . $firstClass .' path-selected"><a href="' . $pageLink . '" ' . $target . '>' . $ni->getName() . '</a>');
  112. } else {
  113. echo('<li class="' . $_c->getCollectionHandle() . $firstClass .'"><a href="' . $pageLink . '" ' . $target . ' >' . $ni->getName() . '</a>');
  114. }
  115. $lastLevel = $thisLevel;
  116. $i++;
  117. }
  118. }
  119. }
  121. $thisLevel = 0;
  122. if ($containsPages) {
  123. for ($i = $thisLevel; $i <= $lastLevel; $i++) {
  124. if ($i == 0){
  125. // this is our last close, add in the cap placeholder
  126. echo("</li><li class='cap'></li></ul>");
  127. } else {
  128. echo("</li></ul>");
  129. }
  130. }
  131. }
  132. ?>


  1. ul.fancy-nav {
  2. margin: 0;
  3. padding: 0;
  4. }
  5. ul.fancy-nav > li {
  6. position: relative;
  7. margin: 0;
  8. padding: 0;
  9. list-style-type: none;
  10. }
  11. ul.fancy-nav > li > a {
  12. text-indent: -9999em;
  13. overflow: hidden;
  14. display: inline-block;
  15. z-index: 12;
  16. background: url(main_nav_top.png) 0 0 no-repeat;
  17. width: 159px;
  18. height: 39px;
  19. position: relative;
  20. left: 0;
  21. top: 0;
  23. }
  24. ul.fancy-nav li ul {
  25. position: absolute;
  26. left: 0;
  27. top: -9999em;
  28. width: 159px;
  29. margin: 0;
  30. padding: 0;
  31. z-index: 11;
  32. }
  33. ul.fancy-nav li.sfHover ul {
  34. top: -0px;
  35. }
  36. ul.fancy-nav li ul li {
  37. margin: 0;
  38. padding: 0;
  39. text-align: center;
  40. width: 149px;
  41. padding-left: 5px;
  42. padding-right: 5px;
  43. list-style-type: none;
  44. background: url(sub_nav_stretch.png) 100% 100% repeat-y;
  45. }
  46. ul.fancy-nav li ul li.first {
  47. padding-top: 44px;
  48. }
  49. ul.fancy-nav li ul li.cap {
  50. background: url(sub_nav_bottom_cap.png) 100% 100% no-repeat;
  51. height: 18px;
  52. }
  53. ul.fancy-nav li ul li a {
  54. text-indent: 0;
  55. }

Added to header file

  1. <?php
  2. Loader::element('header_required');
  3. $html = Loader::helper('html');
  4. echo $html->javascript('superfish.js');
  5. echo $html->javascript('jquery.hoverIntent.minified.js');
  6. ?>
  7. <script>
  8. $(document).ready(function(){
  9. $('ul.fancy-nav').superfish();
  10. });
  11. </script>

Final Output

Hrm, I guess looking at this it's not really working here in my page, but the test ones I was doing at work seemed OK.  It's partially there anyway.  It seems to be working for the very last element in the nav, the projects page.  On that one there's no other div that it's running into to cut it off. 

Anyway, I think this is enough to start with, hopefully just some more css love and moving it around a bit to fit in the header instead of just right there in the post and it will work.  I won't be trying to put 55 pages in the drop down like I'm doing with the blog posts, either. 

If there's anyone that can think of a better way to do this I'd like to know about it, I'm not entirely happy with how this is working out so far.

blog comments powered by Disqus