IE7:hover ghosts bug

Microsoft Internet Explorer 7 brought a number of new features and made it significantly easier to work both with code overall and - especially - with cascading stylesheets. Unfortunately, new problems have arrived alongside the new possibilities.

One of the most important new features of IE7 is the implementation of the :hover CSS pseudoclass for arbitrary (X)HTML elements. Thus in IE7, any element can change its appearance depending on whether or not the mouse cursor lies above it. This support makes various web design tasks simpler, especially the creation of drop-down menus, and it also makes possible a wide range of new special effects.

Unfortunately, the implementation of :hover in IE7 suffers from a serious problem. If you nest several elements inside each other and you tie the display of child elements to the :hover of the parent element, it can happen that after a first, correct display of the nested elements, these elements will stay displayed in IE7 even if the element directly above them does not have :hover! (And if simultaneously an indirect-parent element does have it.)

It would seem that what we have here is yet another expression of the internal mechanism of MSIE entitled hasLayout, which determines whether or not the given element "has layout". The processing of a whole spectrum of other standard CSS values depends on the boolean value of this property. For more information on the hasLayout mechanism, see On having layout.

The IE7:hover ghosts bug appears wherever elements that are only supposed to appear when their direct parent has :hover inherit from the CSS definition a property that flips the default state of hasLayout = false over to hasLayout = true.

Because it is difficult to re-flip a hasLayout = true property back over the hasLayout = false once true has been set, the best solution is to prevent the propagation of this property. That is, to define all CSS properties that flip the state of the hasLayout property no earlier than when their direct-parent elements already have :hover.

With this of all things being the best solution, it's clear that the IE7:hover ghosts bug makes work with CSS code significantly less efficient and also makes said code less universal, harder to read, and less reusable.

Note: I recommend using the IE Development Toolbar for code development and debugging. If an element "has layout," then the IE Development Toolbar will display the value of the hasLayout property as "-1"; otherwise, it will display it as "0".

Tests and workarounds:

  1. Nested DIVs ghosts
  2. Nested lists ghosts
  3. Drop-down menu ghosts

Nested-DIVs ghosts

The following examples show three nested div's - "Alpha," "Beta," and "Gamma" - with Alpha being displayed nonstop, Beta being displayed when Alpha has :hover, and Gamma being displayed when Beta has :hover.

The same XHTML code is used in both examples; the only difference between them is in the CSS.

<div class="alpha">Alpha
	<div class="beta">Beta
		<div class="gamma">Gamma</div>
	</div>

</div>
		

Nested DIVs ghosts demo

To make the IE7:hover ghosts bug kick in, move the cursor over the Alpha div, from there to the Beta div, and from there to the Gamma div. Then move the cursor outside the last of the div's (making sure not to cross the Beta and Alpha elements along the way). Now once again move the mouse cursor to the Alpha div - IE7 will display the Beta and Gamma div's (without content). Look! A ghost!

Alpha
Beta
Gamma

For this example, the CSS code displayed below was used. This code uses inheritance, in harmony with the rules of CSS, to efficiently define the properties of div elements. That will cause hasLayout = true to be set even for elements that do not have :hover.

div {
	height: 50px;
	width: 50px;
	position: relative;
}

div div {
	display: none;
	position: absolute;
	left: 49px;
	top: 0px;
}

.alpha {
	background-color: #CCC;
}
.beta {
	background-color: #888;
}
.gamma {
	background-color: #BBB;
}

.alpha:hover .beta {
	display: block;
}
.beta:hover .gamma {
	display: block;
}
		

Nested DIVs ghosts solved demo

Repeat the procedure mentioned in the Nested DIVs ghosts demo example - you'll note that the IE7:hover ghosts bug will not be activated. No ghost here!

Alpha
Beta
Gamma

The CSS code displayed below was used for this example. This code restricts the use of inheritance to just the defining of elements' div properties, and thereby it avoids setting hasLayout = true for elements that do not have :hover.

.alpha {
	height: 50px;
	width: 50px;
	position: relative;
}

.beta, .gamma {
	display: none;
}

.alpha {
	background-color: #CCC;
}
.beta {
	background-color: #888;
}
.gamma {
	background-color: #BBB;
}

.alpha:hover .beta {
	display: block;
	height: 50px;
	width: 50px;
	position: absolute;
	left: 50px;
	top: 0px;
}
.alpha:hover .beta:hover .gamma {
	display: block;
	height: 50px;
	width: 50px;
	position: absolute;
	left: 50px;
	top: 0px;
}
		

Nested lists ghosts

The following pair of examples shows three levels of nested lists, with the first level being displayed nonstop and the others being displayed when the li element under which they are nested receives :hover.

The same XHTML code is used in both examples; only the CSS differs.

<ul class="first-level">
	<li>Alpha
		<ul class="second-level">
			<li>Alpha Alpha</li>

			<li>Alpha Beta</li>
			<li>Alpha Gamma
				<ul class="third-level">
					<li>Alpha Gamma Alpha</li>
					<li>Alpha Gamma Beta</li>

					<li>Alpha Gamma Gamma</li>
				</ul>
			</li>
		</ul>
	</li>

	<li>Beta
		<ul class="second-level">
			<li>Beta Alpha</li>
			<li>Beta Beta</li>
			<li>Beta Gamma
				<ul class="third-level">

					<li>Beta Gamma Alpha</li>
					<li>Beta Gamma Beta</li>
					<li>Beta Gamma Gamma</li>
				</ul>

			</li>
		</ul>
	</li>
	<li>Gamma
		<ul class="second-level">
			<li>Gamma Alpha</li>

			<li>Gamma Beta</li>
			<li>Gamma Gamma
				<ul class="third-level">
					<li>Gamma Gamma Alpha</li>
					<li>Gamma Gamma Beta</li>

					<li>Gamma Gamma Gamma</li>
				</ul>
			</li>
		</ul>
	</li>

</ul>
		

Nested lists ghosts demo

In order to activate the IE7:hover ghosts bug in this example, move the mouse cursor to, for example, the Alpha list item, from there to the Alpha Gamma list item, and from there to, for example, the Alpha Gamma Gamma list item. Next, move the mouse cursor outside the last list item (making certain NOT to return past any parent lists). Now return the mouse cursor to the Alpha list item - IE7 will display the second- and third-level lists (without content). Look! A ghost!

For this example, the CSS code displayed below was used. In harmony with the rules of CSS, it uses inheritance to efficiently define the properties of the ul and li properties. Doing so sets hasLayout = true even on elements that do not have :hover.

ul.first-level {
	background-color: #CCC;
}
ul.second-level {
	background-color: #888;
}
ul.third-level {
	background-color: #BBB;
}

ul {
	list-style-type: none;
	margin: 0;
	padding: 0;
	width: 12em;
}
ul li ul {
	display: none;
}
ul li:hover>ul {
	display: block;
	position: absolute;
	left: 10.5em;
	top: -1px;
}

li {
	position: relative;
	width: 10em;
	padding: 0.25em;
	border: 1px solid black;
}
		

Nested lists ghosts solved demo

Repeat the procedure given in the Nested lists ghosts demo - the IE7:hover ghosts bug will not be activated. No ghost here!

For this example, the CSS code mentioned below was used, which restricts the use of inheritance to just defining the properties of the ul and li elements, thereby preventing it from setting hasLayout = true for elements that do not have :hover.

ul.first-level {
	background-color: #CCC;
}
ul.second-level {
	background-color: #888;
}
ul.third-level {
	background-color: #BBB;
}

ul {
	display: none;
	list-style-type: none;
	margin: 0; padding: 0;
}
ul.first-level {
	display: block;
	width: 12em;
}
ul li:hover>ul {
	display: block;
	width: 12em;
	position: absolute;
	left: 10.5em;
	top: -1px;
}

li {
	position: relative;
	width: 10em;
	padding: 0.25em;
	border: 1px solid black;
}
		

The following examples show three levels of nested lists, with the first level being displayed nonstop and the others being displayed when the li element that is their parent receives :hover. The top-level list is formatted such that this bundle of code creates the ever-popular "drop-down" or "pop-up" style menu.

In both examples, the same XHTML code is used; only the CSS code differs. Notice how the XHTML code is the same as in for Nested lists ghosts - thanks to the use of the advanced CSS features newly implemented in IE7, this code is universal!

<ul class="first-level">
	<li>Alpha
		<ul class="second-level">
			<li>Alpha Alpha</li>

			<li>Alpha Beta</li>
			<li>Alpha Gamma
				<ul class="third-level">
					<li>Alpha Gamma Alpha</li>
					<li>Alpha Gamma Beta</li>

					<li>Alpha Gamma Gamma</li>
				</ul>
			</li>
		</ul>
	</li>

	<li>Beta
		<ul class="second-level">
			<li>Beta Alpha</li>
			<li>Beta Beta</li>
			<li>Beta Gamma
				<ul class="third-level">

					<li>Beta Gamma Alpha</li>
					<li>Beta Gamma Beta</li>
					<li>Beta Gamma Gamma</li>
				</ul>

			</li>
		</ul>
	</li>
	<li>Gamma
		<ul class="second-level">
			<li>Gamma Alpha</li>

			<li>Gamma Beta</li>
			<li>Gamma Gamma
				<ul class="third-level">
					<li>Gamma Gamma Alpha</li>
					<li>Gamma Gamma Beta</li>

					<li>Gamma Gamma Gamma</li>
				</ul>
			</li>
		</ul>
	</li>

</ul>
		

To activate the IE7:hover ghosts bug, move the mouse cursor over, for example, the Alpha list item, from there to the Alpha Gamma list item, and from there to the Alpha Gamma Gamma list item. Then move the mouse cursor outside the last item in the list (making sure not to return past the parent lists). Finally, move the cursor once again over the Alpha list item - IE7 will display the second- and third-level lists (without content). Look! A ghost!

For this example, the CSS code displayed below was used. This code, in harmony with CSS rules, makes use of inheritance to efficiently define the properties of the ul and li elements, which causes hasLayout = true to be set even for elements that do not have :hover.

ul.first-level {
	background-color: #CCC;
	z-index: 1;
}
ul.second-level {
	background-color: #888;
	z-index: 2;
}
ul.third-level {
	background-color: #BBB;
	z-index: 3;
}

ul {
	list-style-type: none;
	margin: 0;
	padding: 0;
}
ul li ul {
	display: none;
	position: absolute;
	left: 10.5em;
	top: -1px;
	width: 12em;
}
ul li:hover>ul {
	display: block;
}

ul.first-level>li:hover>ul {
	left: -1px;
	top: 1.75em;
}

li {
	float: left;
	white-space: nowrap;
	position: relative;
	padding: 0.25em;
	border: 1px solid black;
}
ul ul li {
	float: none;
	width: 10em;
}
		

Repeat the procedure mentioned in the Drop-down menu ghosts demo example - no activation of the IE7:hover ghosts bug will occur. No ghost here!

For this example, the below-mentioned CSS code was used. It restricts the use of inheritance to just defining the properties of the ul and li elements, and thereby avoids setting hasLayout = true for elements that do not have :hover.

ul.first-level {
	background-color: #CCC;
	z-index: 1;
}
ul.second-level {
	background-color: #888;
	z-index: 2;
}
ul.third-level {
	background-color: #BBB;
	z-index: 3;
}

ul {
	list-style-type: none;
	margin: 0;
	padding: 0;
}
ul li ul {
	display: none;
	left: 10.5em;
	top :-1px;
}
ul li:hover>ul {
	display: block;
	position: absolute;
	width: 12em;
}

ul.first-level>li:hover>ul {
	position: absolute;
	left: -1px;
	top: 1.75em;
	width: 12em;
}

li {
	float: left;
	white-space: nowrap;
	position: relative;
	padding: 0.25em;
	border: 1px solid black;
}
ul ul li {
	float: none;
	width: 10em;
}
		

Thanks...

2007-05-26: Maryo for notice about nested divs ghosts.


Note: I will appreciate it if you send me your comments, references, or anything else of interest ;-)

(c) 2007 - Vilém Málek (vilem.malek@murphy.cz, http://murphy.cz)


See Murphy.cz.