The problem rests in the fact that the jQuery-UI Dialog has created a new stacking context and so changes to the z-index
of the #content
element will only show up relative to it's current stacking context.
For proof, add the following css, and you'll still see the #content
div display above the dialog, which has a z-index
of 100
. --> i.e. Z-indexes are not absolute! ... jsFiddle
#content { z-index: 0 !important; }
From What No One Told You About Z-Index:
Every stacking context has a single HTML element as its root element. When a new stacking context is formed on an element, that stacking context confines all of its child elements to a particular place in the stacking order. That means that if an element is contained in a stacking context at the bottom of the stacking order, there is no way to get it to appear in front of another element in a different stacking context that is higher in the stacking order, even with a z-index of a billion!
New stacking contexts can be formed on an element in one of three ways:
- When an element is the root element of a document (the element)
- When an element has a position value other than static and a z-index value other than auto
- When an element has an opacity value less than 1
The problem is Bootstrap Tour doesn't really have a good way of identifying this. When they start a tour on an element, they apply:
- A full screen black
tour-backdrop
with a z-index of 1009
,
- A white
tour-step-background
behind the object with a z-index of 1010
- and apply a z-index to the current tour element of
1011
...but the last z-index
has no effect since our object is already in a stacking context. It is just positioned higher than any of the other elements within that stack (which it already was). Incidentally, that's why removing the z-index
from ui-front
results in the proper appearance because it frees the element from its current stack.
Here's what Bootstrap Tours should be doing, and we'll do manually with DOM manipulation.
The problem is that they are trying to position the elements relative to one another with z-indexes, but anytime the tour object is in a stacking context different from the .tour-step-background
and .tour-backdrop
, this will fail.
Solution? Place the elements you're generating inside the same stacking context!
Here's a simplified markup tree
- Body
- Tour background (normally goes here)
- New stacking context
- Tour object
- Tour background (should go here)
To override this, we'll plugin to the tours onShown
method and move the backdrops ourselves:
In JavaScript, when creating a tour, we'll do the following:
var t = new Tour({
backdrop: true,
onShown: function(tour) {
var stepElement = getTourElement(tour);
$(stepElement).after($('.tour-step-background'));
$(stepElement).after($('.tour-backdrop'));
}
});
function getTourElement(tour){
return tour._steps[tour._current].element
}
This uses jQuery's .after()
DOM manipulation to move the backdrop to the same stacking context as our tour object.
Also, we'll need to position both our tour backdrop elements as fixed
instead of absolute
, so they don't try to squeeze inside of our dialog. We can do that with the following CSS:
.tour-step-background,
.tour-backdrop {
position: fixed;
}
Which should look like this: