Inline elements shifting when made bold on hover

0 votes
asked Feb 17, 2009 by billy

I created a horizontal menu using a HTML lists and CSS. Everything works as it should except when you hover over the links. You see, I created a bold hover state for the links, and now the menu links shift because of the bold size difference.

I encounter the same problem as this SitePoint post. However, the post does not have proper solution. I've searched everywhere for a solution and can't find one. Surely I can't be the only one trying to do this.

Does anyone have any ideas?

P.S: I don't know the width of the text in menu items so I cannot just set the width of the li items.

This is my code:

HTML:

<ul class="nav">
    <li class="first"><a href="#">item 0</a></li>
    <li><a href="#">item 1</a></li>
    <li><a href="#">item 2</a></li>
    <li><a href="#">item 3</a></li>
    <li><a href="#">item 4</a></li>
</ul>

CSS:

.nav { margin: 0; padding: 0; }
.nav li { 
    list-style: none; 
    display: inline; 
    border-left: #ffffff 1px solid; 
}
.nav li a:link, .nav li a:visited { 
    text-decoration: none; 
    color: #ffffff; 
    margin-left: 8px; 
    margin-right: 5px; 
}
.nav li a:hover{ 
    text-decoration: none; 
    font-weight: bold; 
}
.nav li.first { border: none; }

18 Answers

0 votes
answered Jan 3, 2009 by alex-grin

If you're not averse to using Javascript, you can set the proper width once the page is shown. Here's how I did it (using Prototype):

$$('ul.nav li').each(this.setProperWidth);

setProperWidth: function(li)
{
  // Prototype's getWidth() includes padding, so we 
  // have to subtract that when we set the width.
  var paddingLeft = li.getStyle('padding-left'),
      paddingRight = li.getStyle('padding-right');

  // Cut out the 'px' at the end of each padding
  paddingLeft = paddingLeft.substring(0,paddingLeft.length-2);
  paddingRight = paddingRight.substring(0,paddingRight.length-2);

  // Make the li bold, set the width, then unbold it
  li.setStyle({ fontWeight: 'bold' });
  li.setStyle({ width: (li.getWidth() - paddingLeft - paddingRight) + 'px'});
  li.setStyle({ fontWeight: 'normal', textAlign: 'center' });
}
0 votes
answered Jan 17, 2009 by andrew-vit

If you cannot set the width, then that means the width will change as the text gets bold. There is no way to avoid this, except by compromises such as modifying the padding/margins for each state.

0 votes
answered Jan 17, 2009 by stevenvh

I would advice against switching fonts(°) on hover. In this case it's just the menu items moving a bit, but I've seen cases where the complete paragraph gets reformatted because the widening causes an extra word wrap. You don't want to see this happen when the only thing you do is move the cursor; if you don't do anything the page layout should not change.

The shift can also happen when switching between normal and italic. I would try changing colors, or toggle underline if you have room below the text. (underlining should stay clear from the bottom border)

I would be boo'd if I used switching fonts for my Form Design class :-)

(°) The word font is often misused. "Verdana" is a typeface, "Verdana normal" and "Verdana bold" are different fonts of the same typeface.

0 votes
answered Jan 29, 2010 by alex

One line in jquery:

$('ul.nav li a').each(function(){
    $(this).parent().width($(this).width() + 4);
});

edit: While this can bring about the solution, one should mention that it does not work in conjunction with the code in the original post. "display:inline" has to be replaced with floating-parameters for a width-setting to be effective and that horizontal menu to work as intended.

0 votes
answered Jan 4, 2012 by monk

You could use somehting like

<ul>
   <li><a href="#">Some text<span><br />Some text</span></a></li>
</ul>

and then in your css just set the span content bold and hide it with visibility: hidden, so that it keeps its dimensions. Then you can adjust margins of the following elements to make it fit properly.

I am not sure if this approach is SEO friendly though.

0 votes
answered Jan 7, 2012 by martin-horvath

You can work with the "margin" property:

li a {
  margin: 0px 5px 0px 5px;
}

li a:hover {
  margin: 0;
  font-weight: bold;
}

Just make sure that the left and right margin are big enough so the extra space can contain the bold text. For long words, you might choose different margins. It's just another workaround but doing the job for me.

0 votes
answered Jan 3, 2013 by stefan-jestanoff

A compromised solution is to fake bold with text-shadow, for example:

a:hover{
  text-shadow:0 0 1px black, 0 0 1px black, 0 0 1px black
}

Repeating 3 times the shadow makes it appear not that much blured.

0 votes
answered Jan 4, 2013 by daniele-b

I just solved the problem with the "shadow" solution. It seems the most simple and effective.

nav.mainMenu ul li > a:hover, nav.mainMenu ul li.active > a {
    text-shadow:0 0 1px white;
}

No need to repeat the shadow three times (result was the same for me).

0 votes
answered Jan 13, 2013 by jules-colle

here's an idea for a jquery plugin:

for all of the elements that you want bold mouse-over:
    - on mouseover:
        - calculate the width W and center C of the element
        - copy the element and hide the original with visibility:hidden (so it still takes up the same space)
        - make the text of the copied element bold and calculate the new width W2
        - position the element absolute left at C-(W2/2)

You could easily extend this for height as well, making it possible to slightly increase the font-size on mouseover without having to worry about jumping content.

0 votes
answered Feb 27, 2013 by 350d

li, a {
    display:inline-block;
    text-align:center;
    font: normal 14px Open Sans;
    text-transform: uppercase;
}
a:hover {
    font-weight:bold;
}
a::after {
    display: block;
    content: attr(title);
    font-weight: bold;
    height: 0;
    overflow: hidden;
    visibility: hidden;
}
<ul>
    <li><a href="#" title="height">height</a></li>
    <li><a href="#" title="icon">icon</a></li>
    <li><a href="#" title="left">left</a></li>
    <li><a href="#" title="letter-spacing">letter-spacing</a></li>
    <li><a href="#" title="line-height">line-height</a></li>
</ul>
Welcome to Q&A, where you can ask questions and receive answers from other members of the community.
Website Online Counter

...