text-align: center on absolutely-positioned child behaves like left: 50% - css

If I have a position: absolute inline child inside a parent with text-align: center, the result is exactly as if I had written left: 50%, rather than actually centering the div.
Granted this is an unusual case since the two are not meant to be used together, and I'm not going to rely on them for production, but I'm trying to understand why this is the case.
CodePen
.centered,
.positioned {
border: 1px solid blue;
margin-bottom: 20px;
height: 200px;
position: relative;
}
.centered {
text-align: center;
}
.centered .child,
.positioned .child {
width: 100px;
height: 100px;
border: 1px solid red;
position: absolute;
display: inline-block;
}
.positioned .child {
left: 50%;
transform: translate(-50%, 0);
}
<div class="centered">
<div class="child"></div>
</div>
<div class="positioned">
<div class="child"></div>
</div>
When you use left: 50% you either need a negative margin-left equal to half the element's width, or you need a transform of -50%, because the dom is positioning the left edge at 50% of the parent, meaning the div is more right-aligned rather than centered.
What's odd is that text-align: center on the parent, and position: absolute on the child behave like this too. Is text-align: center not able to take into account the width of the child? Even if it's specifically set?

This is a known issue that appears to be deliberate, at least in Chrome:
The breakage was caused by http://wkrev.com/126911 which fixed an earlier issue, and according to the commit (and commits/bugs it references) this is the intentional behavior, making this an error on the website itself.
Talking to Robert Hogan, author of the patch, it's not entirely certain what should happen here, and I can't find where in CSS 2.1 this is defined. It boils down to whether absolutely positioned elements, which makes them block level per section 9.7 of CSS 2.1, should ever be laid out in a line. WebKit has quite some machinery for this, so the behavior definitely is deliberate.
Furthermore, Firefox matches what Chromium does here, and shows the same result on http://m.csl-sofas.co.uk. I can test Opera and IE tonight. As we match other browsers I'd be inclined to say that the website should fix it, but I don't yet know where in the specification this is defined.
While investigating this, John and I did find an interesting discrepancy in the computed style output, namely that while rendering of the block changed while flipping between display: block and inline-block, the computed style (rightfully) continued showing display: block, as is implied by position: absolute.
Only IE behaves "as expected".
I'm guessing that the reason this isn't defined in CSS is simply because CSS assumes that all absolutely positioned elements are blockified by the time their layout is determined since you can never have an absolutely positioned element inline — the computed value for display for any absolutely positioned element is always the block-level counterpart to its specified value.
Since this behavior is interoperable across all browsers except IE (including Microsoft Edge), chances are that it will be special-cased in css-position (which currently makes the same assumptions as CSS2), css-text, css-display, or a combination thereof, instead of being corrected and potentially breaking any sites that have come to rely on it.

Related

How to stop mobile safari from setting fixed positions to absolute on input focus?

Disclaimer - I understand there exists questions around fixed elements in safari, and fixed elements weren't supported, but now are and so forth. However I can't find a question that addresses this exact question.
Given the simplest of fixed sidebars, something like:
.sidebar {
position: fixed;
top: 10px;
right: 10px;
}
And a relatively long page, with input elements.
When an input element is focused, any fixed element becomes absolute - I understand the why, safari is trying to declutter the viewport - thats fine, but not always appropriate. I ask that I get to choose the best experience for the user (i know best naturally).
So the Question..
Is there any way to leave fixed elements as fixed even when input elements are focused?
I have attempted to do a bit of $(window).on('scroll', magic and position elements manually on scroll, but its quite jittery on the ipad.
Safari has supported position: fixed since at least version 9.2, but if you're seeing difficult issues, you can fully create the fixed position effect by making the document element and body full screen and then using absolute positioning. Scrolling then occurs in some main container element rather than the body. Your "fixed" elements can exist anywhere in the markup using this method.
jsfiddle here
html,
body,
.mainContainer {
height: 100%;
width: 100%;
overflow: hidden;
margin: 0;
}
.mainContainer {
overflow: auto;
}
.fixed {
position: absolute;
bottom: 20px;
left: 20px;
}
In order to achieve the effect you desire you need to change your approach to the layout. Instead of positioning the sidebar with position:fixed you need to use position:absolute within a position:relative container that is set to the height of the viewport within that position:relative container you need another div that uses overflow-y: scroll and -webkit-overflow-scrolling : touch
Caveat: I generally avoid using position fixed on tablet & mobile if possible although the browser support is there, in my experience it'll be janky and javascript solutions leave a lot to be desired, my first response would be to challenge the pattern with the designer. If I'm given designs that include a position fixed element when there are input elements, I'm more likely to seek a design solution than a development one as the focus issues you're describing are difficult to circumvent and maintain a quality user experience.
THE MARKUP:
<div class="outer">
<div class="sidebar">
<ul>
<li>Dummy list nav or something</li>
</ul>
</div>
<div class="container">
<input type="text" />
<!-- I added 10000 inputs here as a demo -->
</div>
</div>
THE CSS:
html,body{
-webkit-overflow-scrolling : touch !important;
overflow: auto !important;
height: 100% !important;
}
.outer {
position: relative;
overflow: hidden;
/* I'm using Viewport Units here for ease, but I would more likely check the height of the viewport with javascript as it has better support*/
height: 100vh;
}
.sidebar {
position: absolute;
top: 10px;
right: 10px;
/*added bg colour for demo */
background: blue;
}
.container {
height: 100vh;
overflow-y: scroll;
-webkit-overflow-scrolling: touch
}
input {
display: block;
}
Here's a CodePen for you to open in your simulator (presentation view):
https://codepen.io/NeilWkz/full/WxqqXj/
Here's the editor view for the code:
https://codepen.io/NeilWkz/pen/WxqqXj

Why is overflow interacting with z-index?

I am trying to understand the rules behind z-index and how it interacts with the overflow property.
I have this html:
<body>
<div class="cell">
Here is some text to keep things interesting
<div class="boxy"></div>
</div>
</body>
And this css:
.boxy {
position: absolute;
z-index: 9999;
top:70px;
width: 50px;
height: 50px;
background: #0FF;
}
.cell {
border: 2px solid #F00;
position: relative;
/* comment these two lines out and the box appears */
/* or change them both to 'visible' */
/* changing only one of them to 'visible' does not work */
overflow-y: auto;
overflow-x: auto;
}
I would have expected that the cyan box appears even though it is out of the size of the div.cell because its z-index and its position are set.
However, the only way to make the cyan box appear is to comment out the overflow-x and -y lines.
My question is: How can I make the cyan box appear on the screen while keeping the overflow as either hidden or auto? But more importantly, I'm looking to understand why this is happening. What are the css and layout rules being applied here?
See my Plunkr. This example, is of course a much simplified version of the HTML/CSS I am actually working with.
EDIT
There seems to be some confusion in the answers below because I didn't explain things well enough. If you comment the two overflow lines out, you can see that the cyan box appears. It appears outside of the border of .cell. Why does this happen? How can I make the cyan box appear, while still hiding overflow and z-index?
The reason the cyan box appears only when overflow-x and overflow-y are visible, and disappears otherwise, is simply because the cyan box is overflowing the cell box. overflow: visible simply means "paint this box even if it is overflowing its containing block" — the cell box is the containing block of the cyan box because its position is relative. Any other values of overflow cause overflowing content to be clipped from view. There is nothing special going on here; in particular, the z-index is completely irrelevant and there is no such interaction as the question title alludes to (and there really is no reason to set it to such a huge number unless you're worried about scripts injecting other elements into the cell).
The only way to allow the cyan box to appear while the cell has a non-visible overflow is to remove position: relative from the cell and apply that declaration to the parent of the cell (in your example, it's the body). Like this:
body {
position: relative;
}
.boxy {
position: absolute;
z-index: 9999;
top: 70px;
width: 50px;
height: 50px;
background: #0FF;
}
.cell {
border: 2px solid #F00;
overflow: auto;
}
<div class="cell">
Here is some text to keep things interesting
<div class="boxy"></div>
</div>
Absolute-positioned elements do not contribute to the dimensions of their parents.
Therefore, the .cell DIV has no content that affects its dimensions, making it 100% wide by 0px high.
To make the element appear, you'll have to add a height to .cell that will encompass the DIV, in this case 120px (70px top + 50px height).
The Parent Class cell need to be set it's height. because height of absolute element doesn't affect it;s parent.
.boxy {
position: absolute;
z-index: 9999;
top:70px;
width: 50px;
height: 50px;
background: #0FF;
}
.cell {
border: 2px solid #F00;
position: relative;
/* comment these two lines out and the box appears */
/* or change them both to 'visible' */
/* changing only one of them to 'visible' does not work */
overflow-y: auto;
overflow-x: auto;
min-height: 120px; /* height 70px(Top)+50px*/
}
Your problem
Your problem is related to cell node that hides boxy when overflow is specified on cell node.
The reason
The reason behind is that boxy with position absolute does not contribute to height of cell and overflow hides it.
Why is it shown without overflow?
By default overflow is visible, which for browser means do not do anything special for overflow functionality and it does not need to render overflow => does not hide boxy.
Z-indices are local inside their clipping hierarchical parent context. This is very non-intuitive. They have their own z-stack context, which normally parallels that of the enclosure hierarchy. But they're still subject to clipping! Which can be a real pain if you're intuitively expecting the z-indices to be absolute.
Note that some jquery containers, such as accordion, quietly specify overflow: auto. Even if it's not explicitly in your code. (This can be overridden locally after it's found.)
Also note that if overflow-x: visible is set, but overflow-y is set to a non-visible, then the rendering engine quietly internally changes overflow-x to be the same as overflow-y for your amusement. But you found this out already.
You probably should be able to circumvent the unwanted non-"visible" overflow clipping, even with your high z-index, by invoking transform: translate(0,0); [or whatever desired offset, % or pixels] inside the style of the div that you want to levitate. Transform should create a new local z-stack for that element and its children. Which will let you get around an overly-restrictive parent or grandparent.

IE display: table-cell child ignores height: 100%

I need to dynamically build a table to hold some data.
I've followed the usual approach of using divs with display: table, display: table-row and display: table-cell:
.tab {
display: table;
height:100%;
width: 200px;
}
.row {
height: 100%;
display: table-row;
}
.elem {
border: 1px solid black;
vertical-align: top;
display: table-cell;
height:100%;
background: blue;
}
.content {
height: 100%;
background: greenyellow;
}
<div class="tab">
<div class="row">
<div class="elem">
<div class="content">
Content
</div>
</div>
<div class="elem">
<div class="content">
Longer content that will need to wrap around eventually you know and don't you hate it when things don't end as you expect them octopus
</div>
</div>
</div>
</div>
Or view on Jsfiddle.
In most browsers I get the expected output:
However, in IE8 (and possibly later versions, I haven't tested later versions), I get the following:
The height: 100% set on the div surrounding "Content" is ignored.
According to CanIUse, IE8 should offer full support for the related display properties.
I've looked through a number of similar questions on SO without finding a working solution: most solutions either rely on Javascript (which I'm looking to avoid), use a fixed height (ibid previous) or don't work on IE8.
Unfortunately, the effect of percentage values for height on display: table-row and display: table-cell elements is undefined according to the spec:
CSS 2.1 does not define how the height of table cells and table rows is calculated when their height is specified using percentage values.
So while a browser may claim to offer full support for table layout, certain aspects such as percentage heights may not be consistently implemented across all browsers because there is no correct behavior. You could try raising an issue on Microsoft Connect in hopes that they will change the behavior to be interoperable, but in the meantime you will need to find a different workaround (and even then you can't guarantee the behavior will remain interoperable, even in other browsers).
To make matters worse, I just tested and this affects all versions of IE up to and including 11, which means an IE-specific hack will fall short here. If you need to use a CSS table layout, as evidenced by the fact that you need to support IE8, then a pure CSS workaround is probably not feasible.
For Internet Explorer 8-10 table-cells with height: 100%; have to be wrapped by table-row with height: 100%;.
Html for IE should be like:
table > table-row > table-cell
While other browsers will work properly with
table > table-row
or
table > table-cell
[edit] I reviewed the question again, and noticed You want to set 100% height not to the table-cells, but on the content inside it.
solution 1: So for Internet Explorer content-height is related to closest element with height set in absolute units, such as pixels, em's, if you want to use % height, you may also need to set 100% height on all parent elements, this will be html and body.
working example
solution 2: Simply add
.content {
padding-bottom: 9999px;
margin-bottom: -9999px;
}
.elem {
overflow: hidden;
}
You don't need to set height on Any of the parent elements in this case.
working example.
Hope this helps.

Pseudo element on parent hidden behind child image on IE8

Why in IE8, is the background color of a pesudo element flowing behind children of the parent? The text flows in front, but the background-color does not. Z-index did not seem to help any.
I haven't been able to determine if this is a bug in IE8 or not. It seems like this would have been a pretty common use-case, but I couldn't find many blog posts or SO questions related to it.
http://jsfiddle.net/VAg2E/
<div id="parent">
<img src="http://placehold.it/200x200">
</div>
#parent{ padding: 20px; }
#parent:before{
content: 'Behind the image';
position: absolute;
top: 0;
left: 0;
width: 100px;
height: 100px;
background-color: red;
}
Edit : A related Stack Overflow Question about Stacking Order
This is definitely a bug in IE8; since your :before pseudo-element is positioned, it should create a new stacking context and always be drawn on top of the img unless you give it a negative z-index (even then, the entire element should be drawn behind it, not just its background).
This issue also seems specific to stacking between :before and :after pseudo-elements and replaced elements like img. It looks like IE8 is treating replaced content differently in terms of stacking, but whatever it is doing, it's definitely not conforming to the spec.
As you're probably aware, this is fixed in IE9.
Have your exact same issue, the only thing you can do is force the stacking order via CSS and z-index. The only catch is that z-index is placed on child element starting from parent element, so you wont be able to do a proper logic order as #parent-element {z-index: 2} and #child-element {z-index: 1}, the z-index for the #child-element will just be set to level 1 as a separate stack order inside the #parent-element.
You can still set z-index for the #child-element with a -1 value, it will just get back the whole #parent-element stacking order.
So to recap:
#parent-element { z-index: 99;} /* or any arbitrary number fitting */
#child-element {z-index: -1;}
Also remember to give both elements a position: relative/absolute to enable the stacking order fo z-index
IE8 only supports pseudos if <!DOCTYPE> is declared. Source
#parent { padding: 20px; z-index: 2; }
#parent:before {
content: 'Behind the image';
position: absolute;
top: 0;
left: 0;
width: 100px;
height: 100px;
background-color: red;
z-index: -1;
}​

Why is my position absolute child placed relative to the browser window instead of its position relative parent?

I have these two divs, one inside another, and I have the styles defined. The encapsulating one is relative and the child is absolute.
Isn't the child supposed to be positioned according to the left top corner of the outer div, #RightSection?
Instead, it's doing it according to the browser window, any leads?
<div id="RightSection">
<div id="Panels">
</div>
</div>
#RightSection
{
position: relative;
}
#Panels
{
position: absolute;
background-color: Blue;
width: 100px;
height: 100px;
z-index: 9000;
}
I have also found that if I do not declare the top and left css parameters for absolutely positioned elements it seems to ignore a parent above it and just jump to the body of the page.
Try just giving it top and left parameters, see if it helps,
#Panels
{
position: absolute;
top: 0;
left: 0;
background-color: Blue;
width: 100px;
height: 100px;
z-index: 9000;
}
It should look just fine once you add in those default parameters.
Absolute positioning inside of relative positioned elements is supposed to do what you describe, but it's not always supported behaviour. What browser are you use and what DTD are you serving?
See http://www.brainjar.com/css/positioning/default4.asp for details. It also has a demo of the positioning so that you can verify it works or not in your browser.
I can confirm that this does not work in IE6. I can't vouch for other browsers while I'm at work, though. Brief searching online leads me to believe that this problem exists in IE7 too, and would conceivably be an issue in IE8 as long as it's rendering in IE7 mode.

Resources