I want a reusable method to horizontally centre absolute-positioned fixed-width elements within their containers, and I want reusable code (e.g., no negative pixel margins).
I've stumbled across a combination that works perfectly:
http://jsfiddle.net/aaronadams/Mquha/
<div class="outer center">
<div class="inner center"></div>
</div>
.outer {
position: absolute;
width: 400px; height: 400px;
background: #999;
}
.inner {
position: absolute;
width: 200px; height: 400px;
background: #666;
}
.center {
left: 0; right: 0;
margin-left: auto; margin-right: auto;
}
It's clean, it's (relatively) easily understood, it appears to work in IE 8+, Chrome, Firefox, Safari… but it also appears to be an enormous, dirty, no-good positioning hack, one that could come apart at the seams at any time.
I can't find any documentation to suggest whether this positioning behaviour is correct or incorrect. Which is it? Appropriate for production, or completely unreliable?
Given the constraint that your two containers .outer and .inner are absolutely positioned, then what you have done is not only correct, but, it is also the only way to do it
when the parent container's width is not specified.
Your solution works exactly according to the CSS 2.1 specification, it is robust.
For reference, you need to review:
http://www.w3.org/TR/CSS2/visudet.html#abs-non-replaced-width
which specifies how the width of an absolutely positioned element is computed.
In your jsFiddle example, you need to realize that the containing block for the two <div>'s is the viewport or the root element. You have yet to say anything about the vertical placement by specifying the top or bottom properties. Also, since the absolutely positioned elements are out-of-flow, their intrinsic height does not factor into computing the height of the containing block, and in some applications, this is a factor that would make this approach unusable without modifications.
In addition, if you were to wrap your sample code in a relatively positioned wrapper <div>, and you applied a fixed width, then the child elements would still center correctly, as shown in this demo: http://jsfiddle.net/audetwebdesign/aNS5j/ (Keep in mind that you may need to allow sufficient height in the wrapper element.)
However, as pointed out earlier in a comment, if the width of the containing block (either the viewport or the wrapper element in my demo) is less than the width of your child elements (400px in your example), then the centering would no longer work, BUT this would still be the correct behavior according to the CSS specification.
Your question has touched upon a basic but important fundamental of how the CSS visual formatting model works.
Aside
You could still get the same effect without using absolute positioning on the .inner element, in which case, margin: 0 auto would have been sufficient. The cost savings in a slightly more concise CSS declaration.
About Negative Margins
Although negative margins do work and are implemented consistently across most modern browsers, the CSS 2.1 specification does not say how negative margins should be implemented. In theory, negative margins could break down in some user agents that still conform to the CSS specification.
I've alway's found that once you're dipping into the position:absolute; pool, you're abandoning an elegant solution. Regretfully, there are a lot of cases where absolute positioning is the only way to solve a problem.
As far as I know the industry standard is using negative margin, as you mentioned: http://jsfiddle.net/Mquha/3/
And again, this feels dirty and moreover: not reusable.
I've looked into the approach you suggested, and your way seems like a fairly modern discovery, and I'm a bit surprised that it even works. At any rate, it is a lot easier than the negative margin method and seems to be supported by all major browsers, like you say.
I'm adopting the method you proposed in the future, still not perfect but at least it's less dirty than using negative margins. I can only advise you to do the same when you're using absolute positioning.
This is my best bet
change your css to
#outer {
position: absolute;
width: 400px; height: 400px;
background: #999;
}
#inner {
position: absolute;
width: 200px; height: 400px;
background: #666;
}
.center {
margin-left: auto; margin-right: auto;
}
And your html to
<div id="outer center" class="center">
<div id="inner center" class="center"></div>
</div>
Related
I made this code that makes a responsive height, it adjusts according to the size of the viewport. (Run the snippet and resize the screen).
Whereas the html and body have a height: 100%, I set up a basic structure with 3 divs and I was handing this height: 100% between them (as you can see in the snippet). After that, I gave a position: absolute and top according to the size of each.
Well, as I assign attributes the top for each div in "hand", I got the feeling that this may be a quick fix/MacGyver on it, because as in the later, there are more divs, I have to do this calculation for top again. I think that have better ways to do this...
Thus, in what other ways I can do this? The code that I did can be considered a quick fix/MacGyver?
html, body{
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
.div1{
position: absolute;
width: 100%;
height: 10%;
background: red;
}
.div2{
position: absolute;
top: 10%;
width: 100%;
height: 75%;
background: green;
}
.div3{
position: absolute;
top: 85%;
width: 100%;
height: 15%;
background: yellow;
}
<div class="principal">
<div class="div1"></div>
<div class="div2"></div>
<div class="div3"></div>
</div>
The answer to whether what you're doing is a good solution (which I will interpret as concise, not overly complicated, and as compatible as possibe) depends on what exactly you're trying to do. Because you don't provide much detail on that end, let me give you the rundown.
The generally best approach (by the definition above)
In most cases, you won't need any special properties and can simply set the height or min-height of your three containers to the appropriate value, since their parent (body) already has a height of 100%. Because everything is underneath each other, there is no need to use the position property in any way.
Because of the way html works by default, these containers will retain their size until their content will require more space; then they will expand to accomodate the content. This is a feature, not a bug.
If you want to prevent this, set the overflow to hidden or scroll, which will help retaining the original container size (though in case of scrolling, scrollbars might mess with your plans).
Alternative solutions
Sometimes layouters get weird ideas of what you need to put on a webpage, and weird ideas might require weird solutions. Let me try to come up with a list of options to choose from:
The approach you took works if you need to split the viewport into exact shares of fix values, disregarding the container's contents entirely. It's compatible with any relevant browser by a long shot, but it will (assuming you handle the overflow so it doesn't stretch the container) likely result in cut-off content on extreme screen sizes (if you have primarily text content) or aspect ratios (if you have primarily image content); to be honest, probably both - but if you're working on a game, for example, maintaining a relative container size can easily be more important than their contents
Flexboxes will "only" give you a benefit of stretching the content over the whole screen if you're desperately trying to avoid setting a height, but it shouldn't result in any unforeseen errors, aside from the compatibility issues. As an additional bonus, you can rearrange the containers with the order property, which none of the other methods will accomplish.
Using absolute-positioned elements, you can entirely disregard height attributes and just set both top:0 and bottom:0 (while having a relative-positioned parent) to stretch a container over the entire height, then position containers inside on the top and bottom the same way. Not many cases in which this is more useful than the above two come to mind, and you won't like fixing any problems you encounter on the way, but if you're developing for browsers thathave issues with overflow properties, you could look into it.
The vh unit, apart from suffering from compatibility issues about the same, can be used, but don't pose any actual benefit over using percentage values. They are used to size elements relative to the viewport dimensions, which your percentage solution does just the same for this specific use case.
You could use a table, though that's commonly considered bad practice for various reasons and will on top of that be the most complicated solution of all of these, so I won't go into it.
So, all in all, there are many ways to accomplish what you want (and I possibly even missed some), and without providing info about the exact nature of what you're trying to do, there can be no exact recommendations other than a quick summary of what I wrote above: If you plan on putting content in the top and bottom container and you can't use the topmost solution, flexbox will work the best for you; if you need the containers to take up precisely a certain percentage, go with your original solution; and only if both aren't suitable, expand your search to the other options.
Today flex can make this really easy:
examples to run in full page:
html, body {
height:100%;
/*
}
next can be declared only for body but won't hurt if both html/body
body {
*/
display:flex;
flex-flow:column;
}
main {flex:1;
}
/* makeup */
header, footer {
background:tomato;
padding:1em;
}
main {
background:turquoise;
margin:1em;/* it can even stand away */
}
<header> header no need to set an height </header>
<main> let's fill remaining space</main>
<footer> footer no need to set an height </footer>
or use many div
html,
body {
height: 100%;
}
body {
display: flex;
flex-flow: column;
}
div {
flex: 1;
}
/* makeup */
header,
footer {
background: tomato;
padding: 1em;
}
div {
background: turquoise;
margin: 1em;/* it can even stand away */
}
div.autoH {
flex: none;
margin: 0.25em 0em;
background: brown
}
<header>header no need to set an height</header>
<div>let's fill remaining space</div>
<div class="autoH">let's fill only my needed space</div>
<header>or use header's / footer's properties</header>
<div>let's fill remaining space</div>
<footer>footer no need to set an height</footer>
I just came across something
#element {
left: 50%;
margin-left: -(elemntwidth/2)px;
}
being (elemntwidth/2) already a number like 30px, for ex.
I would like to know if this is a safe way of crossbrowsing the responsive elements positioning so I can abandon the way Im doing right now with .jQuery
$('#element').css(left: (screenwidth - element / 2) + 'px');
More than everything Im interested in a cross mobile device browsers efective solution and this css only I found it clean and simple, so simple that I need to ask if this could be true. Thanks
CSS Frameworks have this functionaility baked in.
Checkout: Foundation 3
Otherwise, you will need to rely heavily on Javascript and Media Queries to achieve pixel perfection.
Not to mention this is the first of many problems you will encounter to acheive cross devices / browser stable elements. All of these things have been carefully thought out for you alreacdy.
This is a way. For some elements it works, resposive, centered and no jQuery.
HTML
<div class="element ver1">TESTE</div>
<div class="element ver2">TESTE</div>
<div class="element ver3">TESTE</div>
<div class="element ver4">TESTE</div>
CSS
.element {
position: relative;
width: 90%;
background: black;
margin: 0 auto 10px;
text-align: center;
color: white;
padding: 20px 0;
}
.ver1{width: 80%;}
.ver2{width: 70%;}
.ver3{width: 60%;}
.ver4{width: 40%;}
Wroking Demo | Final result full screen
AFAIK this solution is browser compatible. it's even better than {margin-left:auto; margin-right:auto;} in some cases. but there is an other interesting point by centering DOM-elements this way:
e.g. if your whole page-wrapper is centered with {left:50%,...} and the browser window width is smaller than the wrapper you cannot see the whole content by scrolling to left and right. the browser cuts the content. try it...
Try to scroll left and right to see the white left- and right-border...
The other known solution is to set {margin-left:auto; margin-right:auto;} but afaik this just works together with {position:relative;}- not with {position:absolute;}-elements
It's been a long time when I started up with this unconventionally solution...
use this code snippet:
.centered {
position: fixed;
top: 50%;
left: 50%;
margin-top: -(height/2);
margin-left: -(width/2);
}
this works even if the parent dimensions change.
The code you have will work - I've used the same approach many times - so long as you know the dimensions of the element you are centering.
Note that you can use the same approach using percentage based widths to work better with responsive layouts.
You're on the right track.
Probably not the best title I've ever written, but I find it hard to formulate this question well. I'm working on a div that should cover 100% of the parent (could be body). This div should have a variable number of children, so every time the page is refreshed the children count can vary from one to ten, maybe more.
I want these children to all be equally wide and have a percentage width. So if there are five children, each child should have width: 20%. If there are two children, they should have width: 50%. I could do this with JavaScript, but I'd really prefer keeping all layout stuff in the css.
Is there a way to accomplish this without using tables?
Use display: table, display: table-cell, and table-layout: fixed.
See: http://jsfiddle.net/thirtydot/ZKXrM/
table-layout: fixed is to equally distribute the available width between any cells without an assigned width.
This works in all modern browsers. It doesn't work in IE7. If you need this to work in IE7, either use JavaScript to polyfill, or use a real <table>.
CSS:
.container {
display: table;
table-layout: fixed;
width: 100%;
}
.container > div {
display: table-cell;
border: 1px dashed red;
}
HTML:
<div class="container">
<div>1</div>
<div>2</div>
..
</div>
You could define something like
div#contains2 div
{
width: 50%;
}
div#contains3 div
{
width: 33%;
}
and so on, then apply the appropriate class to the parent div.
That said, is there a good reason why you're avoiding a table, but trying to recreate how a table works? Sure, it may not be the absolute nicest "sleep-well-at-night" way to make a page, but if the table does the job how you want it doing, and you can't think of anything else that does, go with the table.
The bottom line is that you have essentially 3 options for automatic same-width columns:
Tables, which do it out of the box and will work cross-browser with no major issues
jQuery, which will probably work cross browser, provided the user has JS enabled
CSS like I suggested above, which adds bloat to your CSS file, and fluff to your markup
You can divide the total width (100%) into the number of items (X). So W = 100/X and W=Width.
I'm trying to make a fluid grid layout and I've run into a problem with inconsistent width rendering in Opera. In Opera the width of elements are consistently smaller than the other browsers. I'm trying the fluid 960 grid system but if it wont be consistent then I may change to fixed sizes.
Does anyone know how I can get Opera to render the width the same as the other browsers?
Here is the CSS and HTML I'm using for this demo
.show_grid {
background: url(../img/grid.gif) no-repeat center top;
}
html, body{
margin: 0;
padding: 0;
}
.container {
width: 92%;
margin-left: auto;
margin-right: auto;
max-width: 936px;
padding-top: 15%;
}
.box {
width: 15.633%;
background: #006;
color: #999;
margin: 5% .55%
}
<div class="container show_grid">
<div class="box">2 column - browser name</div>
</div>
Opera rounds percent widths but it doesn't round percentage values for paddings and margins.
So, the easy way is to set the width: 15%, and add padding-right:.633%. But doing so, only the block would be bigger visually.
If you want to have it's width fair so all childs would have the same width, you'll need to add another wrapper and add the appropriate negative margin to it. It is calculated by this formula: 100/width*padding, in your case: 100/15*0.633. It would compensate the padding and everything would be cool.
Here is a fiddle with all the variants: http://jsfiddle.net/kizu/8q23d/
— fixed width in pixels, block with width:15.633%, first visual fix and the proper fix at the end.
Dealing with different box models could be very tricky and time consuming.
I definitely suggest you to avoid dirty CSS hacks that will not validate your css files.
You could try to drop the use of percentage values and go for an "elastic" layout.
In this case you specify the min-width and max-width for your block elements.
An article about elastic layout is here and something more here
In alternative you could detect the browser via javascript or via library and use conditional CSS files.
This is my favorite approach when dealing with IE.
conditional css is a library that will help you with that, but there are many more options in the web.
Good luck
Here's a question that's been haunting me for a year now. The root question is how do I set the size of an element relative to its parent so that it is inset by N pixels from every edge? Setting the width would be nice, but you don't know the width of the parent, and you want the elements to resize with the window. (You don't want to use percents because you need a specific number of pixels.)
Edit
I also need to prevent the content (or lack of content) from stretching or shrinking both elements. First answer I got was to use padding on the parent, which would work great. I want the parent to be exactly 25% wide, and exactly the same height as the browser client area, without the child being able to push it and get a scroll bar.
/Edit
I tried solving this problem using {top:Npx;left:Npx;bottom:Npx;right:Npx;} but it only works in certain browsers.
I could potentially write some javascript with jquery to fix all elements with every page resize, but I'm not real happy with that solution. (What if I want the top offset by 10px but the bottom only 5px? It gets complicated.)
What I'd like to know is either how to solve this in a cross-browser way, or some list of browsers which allow the easy CSS solution. Maybe someone out there has a trick that makes this easy.
The The CSS Box model might provide insight for you, but my guess is that you're not going to achieve pixel-perfect layout with CSS alone.
If I understand correctly, you want the parent to be 25% wide and exactly the height of the browser display area. Then you want the child to be 25% - 2n pixels wide and 100%-2n pixels in height with n pixels surrounding the child. No current CSS specification includes support these types of calculations (although IE5, IE6, and IE7 have non-standard support for CSS expressions and IE8 is dropping support for CSS expressions in IE8-standards mode).
You can force the parent to 100% of the browser area and 25% wide, but you cannot stretch the child's height to pixel perfection with this...
<style type="text/css">
html { height: 100%; }
body { font: normal 11px verdana; height: 100%; }
#one { background-color:gray; float:left; height:100%; padding:5px; width:25%; }
#two { height: 100%; background-color:pink;}
</style>
</head>
<body>
<div id="one">
<div id="two">
<p>content ... content ... content</p>
</div>
</div>
...but a horizontal scrollbar will appear. Also, if the content is squeezed, the parent background will not extend past 100%. This is perhaps the padding example you presented in the question itself.
You can achieve the illusion that you're seeking through images and additional divs, but CSS alone, I don't believe, can achieve pixel perfection with that height requirement in place.
If you are only concerned with horizontal spacing, then you can make all child block elements within a parent block element "inset" by a certain amount by giving the parent element padding. You can make a single child block element within a parent block element "inset" by giving the element margins. If you use the latter approach, you may need to set a border or slight padding on the parent element to prevent margin collapsing.
If you are concerned with vertical spacing as well, then you need to use positioning. The parent element needs to be positioned; if you don't want to move it anywhere, then use position: relative and don't bother setting top or left; it will remain where it is. Then you use absolute positioning on the child element, and set top, right, bottom and left relative to the edges of the parent element.
For example:
#outer {
width: 10em;
height: 10em;
background: red;
position: relative;
}
#inner {
background: white;
position: absolute;
top: 1em;
left: 1em;
right: 1em;
bottom: 1em;
}
If you want to avoid content from expanding the width of an element, then you should use the overflow property, for example, overflow: auto.
Simply apply some padding to the parent element, and no width on the child element. Assuming they're both display:block, that should work fine.
Or go the other way around: set the margin of the child-element.
Floatutorial is a great resource for stuff like this.
Try this:
.parent {padding:Npx; display:block;}
.child {width:100%; display:block;}
It should have an Npx space on all sides, stretching to fill the parent element.
EDIT:
Of course, on the parent, you could also use
{padding-top:Mpx; padding-bottom:Npx; padding-right:Xpx; padding-left:Ypx;}