I got a little stumped this week and turned to the fountain of software knowledge, also known as Stack Overflow, with a question about Missing popout class in ASP.NET menu for nodes without a URL. The problem is simply this; let’s take the following Web.sitemap file:
<?xml version="1.0" encoding="utf-8" ?> <siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0"> <siteMapNode url="" title="" description=""> <siteMapNode title="Top 1" url="~/Top1.aspx"> <siteMapNode title="Sub 1" url="~/Sub1.aspx" /> </siteMapNode> <siteMapNode title="Top 2"> <siteMapNode title="Sub 2" url="~/Sub2.aspx" /> </siteMapNode> </siteMapNode> </siteMap>
Now for an absolute bare bones implementation of a sitemap data source and menu control:
<asp:Menu runat="server" DataSourceID="menuDs" Orientation="Horizontal" /> <asp:SiteMapDataSource ID="menuDs" runat="server" ShowStartingNode="false" />
So here’s the question: will the sitemap node titled “Top 2” display a pop out icon to indicate there’s content beneath it or not? Note that it doesn’t have a URL defined.
The answer depends on the version of .NET the code runs against or more specifically, what version the control rendering runs against. Here’s how it looks when running against .NET3.5 or against .NET4 with the controlRenderingCompatibilityVersion attribute set to 3.5:
And now using the new .NET4 control rendering:
See the difference? The second image completely loses the pop out icon to the right of the “Top 2” menu item. This might not seem like a big deal, but from a usability perspective, it’s a mess. No icon means no indication of the content which lies beneath it so discovery happens purely by exploration alone. The menu still behaves identically – you get “Sub 2” popping out when you hover over it, it’s just the icon that’s missing.
The problem appears to lie with the changes to control rendering between versions. Prior to .NET4, the menu control generated a huge diatribe of tables, mouse over events and web resource requests for the icons. However with .NET4, control rendering has been greatly improved with far more native HTML semantics using unordered lists with hyperlinks. Here’s how it’s explained in the ASP.NET 4 and Visual Studio 2010 Web Development Overview.
Menu controls render markup that is semantically correct and compliant with accessibility guidelines.
Each model still has some CSS for styling and JavaScript to drive the behaviour but the HTML itself is vastly different. In fact it’s so different that in the above example, the .NET3.5 version comes in at 5.33KB while the newer version is less than half the size at 2.48KB. There’s more info about the changes on the same site under menu control improvements.
Back to the point of this post; none of the resources talk about the change in pop out icon behaviour. The reason it’s happening is that in previous rendering models, every menu item with children (regardless of whether it has a URL attribute or not in the sitemap), explicitly gets a separate table cell with an image in it. Here’s how it looks (ellipses inserted in places for the sake of brevity):
<td id="ctl02n1" onkeyup="Menu_Key(event)" onmouseout=..." onmouseover="..."> <table width="100%" cellspacing="0" cellpadding="0" border="0"> <tbody> <tr> <td style="white-space: nowrap;"> <a style="cursor: text;" href="#" class="ctl02_1">Top 2</a> </td> <td style="width: 0pt;"> <img style="..." alt="Expand Top 2" src="/WebResource.axd?d=..."> </td> </tr> </tbody> </table> </td>
Now fast forward to the new rendering mode. Here’s how the “Top 1” menu item looks, take careful note of the CSS classes on the hyperlink:
<li class="has-popup static" aria-haspopup="..." role="menuitem" style="..."> <a href="/Top1.aspx" class="popout level1 static" tabindex="-1">Top 1</a> </li>
And here’s how the “Top 2” item looks, again take note of the CSS on the <a> tag:
<li class="has-popup static" aria-haspopup="..." role="menuitem" style="..."> <a tabindex="-1" class="static">Top 2</a> </li>
See that? The “popout” class has been dropped. Now that’s a bit of a problem because here’s what’s in the style:
a.popout { background-image: url("/WebResource.axd?d=..."); background-position: right center; background-repeat: no-repeat; padding-right: 14px; }
No popout class means no background image which means no arrow. Furthermore, none of the multitude of CSS classes that can be defined against the menu control seem to allow the definition of a style that persist across both URL and non-URL sitemap nodes. The explicit dynamic and static pop out image URLs on the same control also don’t apply to the URL-less nodes.
You can set the URL attribute to “#” which then displays the icon but it’s a bit misleading because the cursor behaviour then suggests it’s a real link plus it will actually link to the current page. You could then implement some JavaScript to void the link but you’re setting down a slippery slope and you’re moving away from one of the core intentions of the markup change which as the quote above explains, is to create more semantically correct and accessible HTML.
I’m calling this one as a breaking change and a bug. Yes, the rendering has changed but so has the rendering of many other controls in ASP.NET4, that’s not the problem. My issue is that the behaviour has changed and that it hasn’t been documented anywhere. And that’s a problem.
I’ve now logged this as an issue on the Microsoft Connect site. Feel free to jump over and vote it up if this causes you grief as well.