I am trying to create a simple web page with a navigation bar and a title centered in the page. However, the margin of the title div somehow affected the positioning of the navigation bar.
I think this is caused by the margin collapsing of two adjacent block-level boxes? I have tried to fix this problem by adding a <br> after the navigation bar, it worked, but I think it is not elegant.
Is there a better method to achieve what I want?
Below is my simplified HTML code:
<header id='navbar-bg'>
<div id='navbar'>
<ul>
<li>Foo</li>
<li>Bar</li>
</ul>
</div>
</header>
<div id='body'>
<h1 id='search-title'>This is a title</h1>
</div>
And the CSS style:
#navbar-bg {
position: fixed;
width: 100%;
}
#navbar {
display: flex;
align-items: center;
}
#body {
margin-top: 200px;
}
Since the position of #navbar-bg is fixed, I want the navigation bar as a whole to be fixed, and the margin-top of #body should not affect the navigation bar. Yet the margin-top moved both the #body and the navigation bar down, which is strange.
I want to fix this problem using an elegant solution, not adding a <br> after header.
You have to set a top: 0px to the #navbar-bg element. According to Mozilla:
The element is removed from the normal document flow, and no space is created for the element in the page layout. It is positioned relative to the initial containing block established by the viewport, except when one of its ancestors has a transform, perspective, or filter property set to something other than none (see the CSS Transforms Spec), in which case that ancestor behaves as the containing block. (Note that there are browser inconsistencies with perspective and filter contributing to containing block formation.) Its final position is determined by the values of top, right, bottom, and left.
So, when you don't use top for the #navbar-bg element, it will fall back to it's initial values, which is relative to body. So the body margin is also present in that element.
For a navbar it's probably best to be on the left of the body, so you could do this in your CSS:
#navbar-bg {
position: fixed;
width: 20%;
float: left;
}
#navbar {
display: flex;
align-items: center;
}
#body {
margin-top: 200px;
float: right;
width: 80%;
}
Related
For a simple landing page I wanted to let some text box overlap an header image. To make it simple, I just have a structure like:
<header>
<img src="path/to/img.png" />
<h1>Awesome headline</h1>
</header>
All elements are set to display:block and the h1 is dragged inside the image with a negative margin. I also gave the headline some padding and background:white.
Now the problem: The headline text is shown on top of the image but the background colour is behind it! You can see an example here: https://jsfiddle.net/cv12evLn/
My guess is, that a browser renders all sibling blocks in layers, starting with all backgrounds and borders, then rendering images (img-tags) and finally text on top of everything else.
Is that right? And why the actual… I mean, that seems crazy unexpected to me.
To solve the issue, I've put the headline in a wrapper and set this to position:absolute. See here for a live example: https://jsfiddle.net/f5sd1u6o/
Use position:relative rather than negative margin. Then the z-index works automatically.
#container {
width: 500px;
height: 300px;
margin: auto;
}
#container img {
display: block;
width: 100%;
}
#container h1 {
display: block;
width: 50%;
height: 1em;
margin: auto;
padding: .5em 1em 1em;
font-size: 3rem;
background: yellow;
text-align: center;
border: 1px solid red;
position: relative;
top: -4.6rem;
}
<div id="container">
<img src="//placekitten.com/500/300">
<h1>
headline
</h1>
</div>
To get the Z-index to work, you need to apply position:relative anyway but you can still use negative margin if that is a design requirement.
JSfiddle demo (with negative margin)
Basically, backgrounds are rendered first before anything else (as I understand it) so they always come at the bottom of the stacking order. You just need to create a new stacking context and changing the position property does that.
As it happens so does changing the opacity of the element so a quick fix is to set opacity:.9999;
JSfiddle Demo (opacity 'hack')
I have a website with a side-bar navigation and a main content pane, wrapped in a container element. The content has a background of its own, while the menu borrows that of the parent container.
In cases where the sidebar is longer than the content, I need the content element to stretch all the way down to cover the same height, so that the content background makes up the majority of the screen space to keep the design from looking silly.
This works beautifully by giving the container display: tableand both children display: table-cell. This will place them next to each other and ensure that they're always the same height.
However, now I want to have two lesser navigation bars in the content. One fixed to the top, one to the bottom of the pane. I use position: relative on the content and position: absolute on the nav bars to achieve this.
This solution works perfectly on modern browsers, but Firefox versions before 31 do not accept position: relative on table-cell elements, and the navbars are positioned relatively to the document body.
Can I somehow make this solution work on all common browsers? I need to maintain that the content element stretches down with the sidebar.
Links:
JSFiddle demonstrating the issue
Live website suffering the problem
A simplified code example:
div {
display: table;
width: 100%;
}
nav {
vertical-align: top;
display: table-cell;
width: 25%;
}
main {
display: table-cell;
position: relative;
}
main header,
main footer {
position: absolute;
}
main header {
top: 0;
}
main footer {
bottom: 0;
}
<div>
<nav>
Sidebar
</nav>
<main>
<header>
Top navbar
</header>
Content
<footer>
Bottom navbar
</footer>
</main>
</div>
According to this answer: https://stackoverflow.com/a/8312358/3929902, the only way to solve this is by adding a <div> around the elements displayed as table-cells, with position relative, like in this fiddle http://jsfiddle.net/qp7vhtk8/6/
In your example that just means adding:
div {
position: relative;
}
I have two elements, both with display: inline-block, and the parent has white-space: nowrap.
When the screen is resized, the div on the right side don't resize, like this.
I'm trying to make only the blue div resize.
Full source (jsfiddle)
The structure of the html is like this:
<div class="container">
<div class="header">...</div> <!-- red -->
<div class="aside">...</div> <!-- pink -->
<article>...</article> <!-- blue -->
</div>
Relevant css:
* {
box-sizing: border-box;
}
div.container {
margin: 0 auto;
max-width: 40em;
padding: 0;
white-space: nowrap;
}
div.container > * {
white-space: normal;
}
.aside {
display: inline-block;
max-width: 15em;
vertical-align: top;
}
.article {
display: inline-block;
max-width: 25em;
}
Old question, but for the sake of knowledge of anyone who reads this and also has the doubt:
What I've found is that setting position: relative on the .container
and position: absolute on the .article does what I want.
An absolute positioned element is positioned relative to the nearest positioned ancestor, where a positioned element means anything with a position property different to static, the default; if does not found any positioned element, uses the body element.
The absolute positioned elements, if has their width and heigth in auto, resizes to fit its content, and limits the maximun sizes by its positioned ancestor. You can check this putting a short string instead a large one: the element will shrink to the length of text. If you remove the positioning from div.container, the article (if still positioned absolute) will grow (depending on its content) to cover the space between previous element and body width.
And, related to the aforementioned and to add some utility to this delayed answer, a not-very-know bonus: if you define the right and left properties of a absoluted positioned element, and leave the width in auto, the element will cover the horizontal size between the right and left defined. This way you could put something like
article {
background-color: #a0f4ec;
display: inline-block;
position: absolute;
right: 0;
left: 30%;
}
div.aside {
background-color: #faf;
display: inline-block;
max-width: 15em;
width: 30%;
}
This trick also applies in a vertical sense, but with height, top and bottom properties.
There are a few ways to do it.
Method 1:
two divs the same line, one dynamic width, one fixed
Method 2 (negative margins)
http://alistapart.com/article/negativemargins
Unfortunately, Narxx's answers require the divs to be floated. I'm sure that's what you should do if you're building a real site, but in my case, I'm trying not to use it.
What I've found is that setting position: relative on the .container and position: absolute on the .article does what I want.
Simplified fiddle
If anyone can explain why, I'll mark it as an answer.
Here is my jsFiddle
I just have 3 divs. The 2nd div is floated to the right, and 3rd div appears just below the 2nd.
In the 3rd div, I am setting margin-top property, and this property does not have any effect on the layout.
Question: Can someone explain me understanding this behavior of float?
HTML
<div class="header">
</div>
<div class="sidebar">
</div>
<div class="footer">
</div>
CSS
.header {
width: 100%;
height: 80px;
background-color: #abcdef;
}
.sidebar {
margin-top: 15px;
float: right;
width: 200px;
height: 200px;
background-color: #abcdef;
display: block;
}
.footer {
clear: both;
margin-top: 20px;
width: 100%;
height: 60px;
background-color: #000000;
}
This is not unexpected at all. The .sidebar is removed from regular flow layout by its float property, as such it doesn't take up any space anymore. The .footer has a clear rule, so it is forced underneath any floats, but that automatically puts it 215px (margin+height of the sidebar) behind the last element that is part of the flow layout. As such its margin requirement of 20px is completely satisfied, and it appears at its logical current position. You can verify this by setting the top margin to 220px instead, it will appear 5px (220-215) below the sidebar.
You can easily achieve the effect you desire by putting margin-bottom:20px on the sidebar since it will then be required to keep that distance to the footer, pushing it down.
The issue is related to the clear rule.
W3C - An element that has had clearance applied to it never
collapses its top margin with its parent block's bottom margin.
Baiscally, if you want to use clear, the general rule is to add an element between the two floated divs to ensure you can correctly space them.
The top margin of the footer div is being collapsed, http://www.w3.org/TR/CSS21/box.html#collapsing-margins
If you add margin-bottom to the sidebar instead of the top of the footer it will work.
This is caused by the fact that floated elements aren't really there with respect to margin calculations. Your .footer is below whatever unfloated elements are above it, (with a margin of 20px). This issue is caused because margins with respect to floats are calculated relative to other floats, (not all other elements).
So to get the desired effect add a margin-bottom element to .sidebar, have a meaningless float added to the .footer, or add a
<div style="clear:both"></div>
between the .footer and .sidebar
I'll let you see the code first then tell you what my problem is:
Tinkerbin: http://tinkerbin.com/x8iGCFsZ
<style>
div.container{
height: 200px;
width: 200px;
background-color:red;
margin-top: 10px;
}
div.subContainer{
position: relative;
text-align: center;
}
div.inner{
position: absolute;
background-color:yellow;
width: 150px;
}
</style>
<div class="container">
<div class="subContainer">
<div class="inner">bananas for breakfast</div>
</div>
</div>
So, according to the textbook, text-align: center;, when applied to a parent element, only centers its child elements if they have display: inline;.
Therefore, and as you'd expect, since a <div> has by default display set to block (display:block;) the text-align: center; applied to the parent div.subContainer doesn't do anything to its child div.inner.
Everything fine so far. Nothing weird.
My problem arouses when I try using <span>, instead of <div> on the .inner element, and I position it absolutely (position: absolute;) — which, as you know force changes the display, from its default inline, to block.
Take a look:
<style>
div.container{
height: 200px;
width: 200px;
background-color:red;
margin-top: 10px;
}
div.subContainer{
position: relative;
text-align: center;
}
span.inner{
position: absolute;
background-color:yellow;
width: 150px;
}
</style>
<div class="container">
<div class="subContainer">
<span class="inner">bananas for breakfast</span>
</div>
</div>
What happens is weird. In spite of having the forced display value of block (thanks to the position: absolute;) the span is still centered. And even more, the centering is actually weird. It takes the left side of the block and aligns it with the center of the containing element, instead of, as usual, aligning both center.
The behavior is fixed — starts acting like a block — when I manually set the display on the span.inner to block.
span.inner{
position: absolute;
display: block;
background-color:yellow;
width: 150px;
}
So, what's happening here? Does the absolutely positioning not force change the display to block? Why is the centering weird?
When you set it to position: absolute, it does become a block, but it gets removed from the flow of content at the point it would have originally appeared. Since you are not using top, left, bottom, and right, this is much more noticeable.
When using a <div>: A division, by default, is block-level and will take up the entire width possible. So it would, by default, start at the left side of the box and expand to the right. Positioning this absolutely would keep it at the top left corner where it originally appeared, the only noticeable difference being the width of the box.
When using a <span>: A span, by default, is an inline element which is affected by the text-align property of it's parent. The text cursor starts at the center of the element, and as text is entered, the characters are added and re-centered to adjust to the width of the text. When you remove the span from the flow, it starts at the point where the text would have started. The text cursor is at the very center (horizontally) of the element, since now there is no text in the actual parent.
You've already found the solution: adding display: block will force the element to actually be a block-level element. The thing is, just because position: absolute "forces" this property, it's not an actual property, only a computed value for the element. That being said, it only gets applied to the element's position when its explicitly set in its CSS definitions.