Sticky header & internal links - css

I have used css to make a "sticky header" that is always visible at the top of the page and the other content placed below it.
In the header I have some internal links. The problem is that when a link is clicked then the page is scrolled so that the target is positioned at the top of the page - hidden by my sticky header - instead of just below it.
Any suggestions on how to solve this problem?
css:
#header {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 3.5em;
padding:0;
margin: 0;
}
#container {
width: 100%;
margin: 3.5em 0 0 0;
padding: 0;
overflow:auto;
}
#content {
padding: 0 4em;
margin: 0;
}
html:
<body>
<div id="header">
<div id="content">
<p>
XYZ
</p>
</div> <!--end content-->
</div> <!--end header-->
<div id="container">
<div id="content">
<p>A lot of text.</p>
<a name="xyz"></a>
<p>A lot of text</p>
</div><!--end content-->
</div><!--end container-->
</body>

At first, it's better to use blocks with id instead of name — it's more standard way.
Then, add class to an anchor and then make it have absolute position + move it with a negative top margin equal to the header's height.
Look at this fiddle: http://jsfiddle.net/kizu/gfXJJ/
Or, alternatively, for browsers that support pseudo-elements, you can add one with the desired height and compensate it's height by negative top margin, so it would amount as the start of the block to which you'd make a link. Doing so you can add ids to already existent elements rather than creating extra ones.
Here is a version with pseudo-element: http://jsfiddle.net/kizu/gfXJJ/2/
Or you can add top padding and negative margin to an element with id itself: http://jsfiddle.net/kizu/gfXJJ/2/ — but in that case there can be problems with backgrounds on it, 'cause the block is physically extended at the top.

A little bit of javascript (jQuery used here) can do it:
$('#header a').click(function (e) {
e.preventDefault();
var offset = $('a[name=' + $(this).attr('href').substr(1) + ']').offset();
$('html, body').animate({ scrollTop: offset.top - $('#header').outerHeight() }, 'fast');
});
This finds the element with a name attribute that matches the href attribute of the link clicked, and then animates a scroll to that element's position less the height of the header.
http://jsfiddle.net/blineberry/bTa8b/

I believe this question overlaps the one in this link: offsetting an html anchor to adjust for fixed header which offers even more possible solutions. I believe the questions could be merged, but I don't have the priviliges to do that. (Which is the same reason I'm entering this as an answer allthough I know it should have been a comment. But I'm not allowed to comment.)

You could also set a top-margin as high as the sticky header for your xyz-fragment in your CSS file. That might result in an empty gap in your result HTML-page though...

One simple thing to do is to place the <a name="xyz"></a> slightly higher up the text.
This would require some trial and error, but it might be the quickest/easiest thing to do.
Another thing you could do is to dynamically add margin or padding when the link is clicked.

Start with getting unique ID's for the content part in the header and inside the container.
Maybe you can put an ul inside the header for the internal links.
Set the position of the content inside the wrapper absolute, et voila!
Is there any place we can get a look at your problem?

Pseudo elements don't appear to work in all scenarios. I had luck just applying a negative top margin and a positive top padding to offset to negative margin.
eg. <a id="xyz" style="margin-top:-50px; padding-top:50px">Title</a>

Related

position:sticky is not working

I have this HTML code:
<div class="header">
<div class="desc">Description</div>
<div class="logo"><img src=""/></div>
<div class="navbar"></div></div>
.header has a height of 150px. .navbar has a height of 20px. When the user scrolls, I want .navbar to stick at the top. So I went to the CSS and set position:sticky and top:0. But this didn't work. I initially thought that firefox is not supporting position:sticky, but that's not the case because I was able to see a working demo of it. I googled about it but found nothing helpful. Anyone knows why this is not working?
Position sticky was not working for me due to the body element having overflow-x: hidden; set.
The 2 most common culprits why position: sticky; might not work are:
You haven't defined top: 0;, bottom: 0;, left: 0 or something similar
One of the parents of your sticky element has overflow (x or y) set to hidden, scroll or auto.
For me it was the first one.
It works fine if you move the navbar outside the header. See below. For the reason, according to MDN:
The element is positioned according to the normal flow of the document, and then offset relative to its flow root and containing block based on the values of top, right, bottom, and left.
For the containing block:
The containing block is the ancestor to which the element is relatively positioned
So, when I do not misunderstand, the navbar is positioned at offset 0 within the header as soon as it is scrolled outside the viewport (which, clearly, means, you can't see it anymore).
.navbar {
background: hotpink;
width: 100%;
height: 50px;
position: sticky;
top: 0;
}
.header {
height: 150px;
background: grey;
}
body {
height: 800px;
position: relative;
}
<div class="header">
<div class="desc">Description</div>
<div class="logo"><img src="" /></div>
</div>
<div class="navbar"></div>
To expand from the answers above and some information to make it work with flexbox parent and overflow other than visible (the examples below assume you use vertical - sticky with either top or bottom set to a certain value and position set to sticky):
The most frequent case is you have an ancestor element (not just immediate parent) with overflow property set to something other than visible and as a result there is no space is left to stick around.
To quickly find out if this is the case, you can run this script in the browser console (please make sure you change the .your-sticky-element class to your element's selector):
var stickyElement = document.querySelector('.your-sticky-element');
var parent = stickyElement.parentElement;
while (parent) {
var hasOverflow = getComputedStyle(parent).overflow;
if(hasOverflow != 'visible') {
console.log(hasOverflow, parent);
}
parent = parent.parentElement;
}
SOLUTION:
a) If you found there is overflow set, and you can remove it, this should solve it
b) If you have to keep your overflow setting, you have to make the parent element's height higher than the sticky element's height. If the parent element has no height or the sticky element fills up all the height, it means there is simply no place to stick within when the page is scrolled. It doesn't need to an explicit height (vertical), but you can inspect to see if your sticky element has extra space left after itself.
Parent is not higher than the sticky element to leave extra space. This particular case can be caused by different circumstances but the solution to this is the same above, please see 1.b
If your sticky element's parent is a flexbox (align-items has default value of normal) or grid, and if the sticky element itself doesn't have a proper align-self set, there will be no space left for the sticky element to hold when scrolling (for example, if it is align-self: stretch or auto [default value]). This is because the child element is stretched to fill up the height of the parent.
SOLUTION:
In this case, align-self: flex-start set for the sticky element can fix the problem because in the element will stand at the start, leaving extra space after itself.
Guide: There are much more complex circumstances both in the case of flexboxes and without it, but the general rule of thumb is your sticky element needs space within the parent to be sticky when scrolled.
Somehow your code only works when the .navbar element is not inside another container like the header. I moved it out and then it works fine. I created a codepen snippet for that, check it out
<header>
<div class="logo">Logo</div>
<div class="description"><div>Lorem ipsum dolor sit amet consectetur adipisicing elit. Quo, veritatis.</div></div>
</header>
<div class="navbar">
<ul>
<li>navitem1</li>
<li>navitem2</li>
<li>navitem3</li>
<li>navitem4</li>
</ul>
</div>
Right now position:sticky is supported quite good as you can see on canIuse. Of course IE currently has no support but the new Edge version will bring full support for this! I found some interesting articles about this topic:
Working demo (chrome,firefox 👍) https://codepen.io/simevidas/pen/JbdJRZ
Caniuse refernce: http://caniuse.com/#search=sticky
sticky article on MDN including latest browser support table https://developer.mozilla.org/en-US/docs/Web/CSS/position#Sticky_positioning
But there are good news on the horizon. I think better browser support will follow the next time.
Adding more content after nav inside header provides sticky behavior, but only for a moment - if the user scrolls down too much, nav will disappear with header, since it can't jump out below header's bottom border.
Thus, the only solution with pure CSS is to put nav inside element that is partially visible even after the user scrolls to the bottom of the page (directly inside body or inside some sort of container that spans to the bottom of the page or at least to the footer).
If this solution is not possible, the other way is to use JavaScript. Before transitioning to CSS, I used the following code (found similar jQuery solution somewhere long time ago, don't remember where, so the credit goes to the anonymous author; Vanilla JS can be easily obtained from this):
$(document).ready(function () {
var sticky_navigation_offset_top = $('nav').offset().top;
var sticky_navigation = function () {
var scroll_top = $(window).scrollTop();
if (scroll_top > sticky_navigation_offset_top) {
$('nav').css({
'position': 'fixed',
'top': 0,
'left': 0,
'right': 0,
'margin-left': 'auto',
'margin-right': 'auto'
});
} else {
$('nav').css({
'position': 'relative'
});
}
};
sticky_navigation();
$(window).scroll(function () {
sticky_navigation();
});
});
Looks like if you try to set sticky a container which has many children nodes inside, instead of them being wrapped in a div, and the parent of sticky container is flex, then it will not sticky. Just wrap the childs in a div fixed it for me.
Your HTML code as it is and write CSS class for navigation bar:
.header {
height: 150px;
background-color: #d1d1d1;
}
.navbar {
background: #999;
border-bottom: 1px solid #333;
border-top: 1px solid #333;
color: #FFF;
margin: 0;
padding: 2px 0 0 12px;
position: sticky;
top: -1px;
}
<div class="header">
<div class="desc">Description</div>
<div class="logo"><img src="" /></div>
<div class="navbar"></div>
</div>
Hope this will help
Met some not evident behaviour of horizontal sticky: if width is 100%, then sticky does not work. Width should be less, then container size.
My sticky header would only partly work ... after a couple of scrolls it would disappear but would work initially
It appears the problem was that I had the parent set to height 100%.
I didn't actually need this as the body one was enough so I removed and it and all was good.. sticks forever
Although this now breaks my footer from staying on the bottom when their is no content!
No huge compromises of the HTML structure need to be made to fix this issue. Simply add display: inline; to all of the sticky element's parents up until you get to the element you wish the sticky element to stick to.
Just to add something to #user56reinstatemonica8 great point...
If immediate parent of sticky node has display: flex sticky positioning could not work.
My guess is that culprit is align-items: stretch as default.
In a flex-direction: row scenario, align-items: stretch let children's height grow so that they are equal height.
So, to overcome this and make sticky work as expected with display: flex you can:
define align-items as center | start | baseline to immediate parent that has display: flex.
define align-self as center | start | baseline to sticky node.
define an explicit height to sticky node.

Position fixed with variable height

I'm looking for a way to make sure the height of a scrollable, fixed element adapts to fit all the whitespace down until the footer.
Please see the following fiddle which is the layout I'm working on.
Been stuck on this for 2 days, it's about time to move on.
Better to see the fiddle in firefox, sidebar scrollbar not scrolling in chrome for some reason but that's a different issue.
<header></header>
<div id="platformContainer">
<section id="platformContent">
<div id="platformBody">
<ul class="mainList">
...
</ul>
</div>
</section>
<section id="toolBarContainer">
<div id="toolBarContent">
<ul id="toolBarList">
...
</ul>
</div>
</section>
<footer></footer>
Assuming you want the toolBarList container 100% height - this is what you already have. The sidebar is 100% height. The list within, however, is only set at 200px:
#platformContainer #toolBarContainer #toolBarContent ul#toolBarList{
height: 200px;
...
}
Changing that to height:100%; makes it fill the entire height of the document. The problem now is accounting for the header and footer. This is a common question, however, and I've answered it myself here: https://stackoverflow.com/a/14892331/1317805 as have many other people. You'll need to ensure that the header and footer aren't hidden by or covering the content area.
I think you might need javascript to do this – 9edge
Not at all!
Also, please note when using section tags:
Use of the element is not to be thought of as outlining content that needs to be styled visually in a particular way. If this is the case the author may be best advised to just use a semantically neutral div.
Your #platformContent and #toolBarContainer styling may yield unexpected results.
http://www.w3.org/WAI/GL/wiki/Using_HTML5_section_elements
In fact, your styling of those sections can be completely replaced with:
#platformBody, #toolBarContent {
position:relative;
height:100%;
top: 70px;
width: 100%;
}

Is there a way to jump along the scrollbar using CSS only?

Let´s say I have following mark up and CSS:
HTML:
<div id="Container">
<div id="Content">
[* some text *]
</div>
</div>
CSS:
#Container {
height: 400px;
overflow: scroll;
}
#Content {
height: 800px;
}
Obviously this set up invokes a scrollbar to possibly scroll down 400px. I created a jsFiddle for a better understanding.
Is there a way to jump to the second paragraph by CSS only?
I added a javascript command to demonstrate what I want to achieve. Just uncomment and run it.
There are two things that I have tried so far, but in both cases I was not able to scroll up anymore:
Setting the margin-top attribute of the inner div container to -180px
Setting the inner div container to position: absolute and top: -180px
Note: I do not care for the paragraph or any content. This is just an example. I want to jump to an arbitrary position.
Edit:
Anchor tags are not an option. I do not want to flood my mark up with unnecessary tags.
How about the humble 'a' tag?
jump to one
jump to two
<a name="one">this is one</a>
<a name="two">this is two</a>
Not with CSS, but with standard HTML/anchors.
http://jsfiddle.net/r6vn7/3/
paragraph 2
Give your paragraph an ID and use the URL hash to say where to go to. I used an anchor as an example how to make it jump to the second paragraph.

Escape the bounds of a div container

Alright, I understand that the purpose of a DIV is to contain its inner elements - I didn't want to upset anyone by saying otherwise. However, please consider the following scenario:
My web page (which only takes up a width of 70% of the entire page) is surrounded by a container (a div). However, under my navigation bar which is at the top of the page, I would like to create w banner that takes up 100% of the width of the entire page (which means it will have to extend outside the bounds of its container as the container is only taking up 70% of the page's width).
This is the basic idea that I am trying to accomplish: http://www.petersonassociates.biz/
Does anyone have any suggestions for how I could accomplish this? I'd appreciate any help.
Evan
If you just want the background of the element to extend across the whole page this can also be achieved with negative margins.
In a nutshell (correction from comment):
.bleed {
padding-left: 3000px;
margin-left: -3000px;
padding-right: 3000px;
margin-right: -3000px;
}
That gives you horizontal scroll bars which you remove with:
body {overflow-x: hidden; }
There is a guide at http://www.sitepoint.com/css-extend-full-width-bars/.
It might be more semantic to do this with psuedo elements: http://css-tricks.com/full-browser-width-bars/
EDIT (2019):
There is a new trick to get a full bleed using this CSS utility:
width: 100vw;
margin-left: 50%;
transform: translateX(-50%);
I guess all solutions are kind of outdated.
The easiest way to escape the bounds of an element is by adding:
margin-left: calc(~"-50vw + 50%");
margin-right: calc(~"-50vw + 50%");
discussion can be found here and here. There is also a nice solution for the upcoming grid-layouts.
If I understood correctly,
style="width: 100%; position:absolute;"
should achieve what you're going for.
There are a couple of ways you could do this.
Absolute Positioning
Like others have suggested, if you give the element that you want to stretch across the page CSS properties of 100% width and absolute position, it will span the entire width of the page.
However, it will also be situated at the top of the page, probably obscuring your other content, which won't make room for your now 100% content. Absolute positioning removes the element from the document flow, so it will act as though your newly positioned content doesn't exist. Unless you're prepared to calculate exactly where your new element should be and make room for it, this is probably not the best way.
Images: you can also use a collection of images to get at what you want, but good luck updating it or making changes to the height of any part of your page, etc. Again, not great for maintainability.
Nested DIVs
This is how I would suggest you do it. Before we worry about any of the 100% width stuff, I'll first show you how to set up the 70% centered look.
<div class="header">
<div class="center">
// Header content
</div>
</div>
<div class="mainContent">
<div class="center">
// Main content
</div>
</div>
<div class="footer">
<div class="center">
// Footer content
</div>
</div>
With CSS like this:
.center {
width: 70%;
margin: 0 auto;
}
Now you have what appears to be a container around your centered content, when in reality each row of content moving down the page is made up of a containing div, with a semantic and descriptive class (like header, mainContent, etc.), with a "center" class inside of it.
With that set up, making the header appear to "break out of the container div" is as easy as:
.header {
background-color: navy;
}
And the color reaches to the edges of the page. If for some reason you want the content itself to stretch across the page, you could do:
.header .center {
width: auto;
}
And that style would override the .center style, and make the header's content extend to the edges of the page.
Good luck!
The more semantically correct way of doing this is to put your header outside of your main container, avoiding the position:absolute.
Example:
<html>
<head>
<title>A title</title>
<style type="text/css">
.main-content {
width: 70%;
margin: 0 auto;
}
</style>
</head>
<body>
<header><!-- Some header stuff --></header>
<section class="main-content"><!-- Content you already have that takes up 70% --></section>
<body>
</html>
The other method (keeping it in <section class="main-content">) is as you said, incorrect, as a div (or section) is supposed to contain elements, not have them extend out of bounds of their parent div/section. You'll also face problems in IE (I believe anything 7 or below, this might just be IE6 or less though) if your child div extends outside the parent div.

When doing equal height columns in CSS, is there a way to get internal anchor links to still function correctly?

I've used the last example on this page for equal height columns.
http://www.ejeliot.com/blog/61
The problem is, when you click an internal anchor link, the content is shifted up, and the overflow is making the top part of the page disappear.
For example, click this link
http://www.noosanativeplants.com.au/~new/articles/botany-words/
Then click a letter to jump to that section. You will notice what I am describing.
Is there a way to combat this, or is this a short coming of the technique? Do you recommend I use the background image technique for faux equal height columns? I'd rather not use this, as one page has a different background, and would require a bit of reworking to do the background for this page.
Thanks
I really recommend you to use the fail-safe faux columns method. If you are not a layout expert (no offence, seriously), stay away from the padding/margin/overflow magic and the one true layout technique. The latter is elegant but it can cause unwanted side-effects if you are to do heavy JS/DOM manipulations and all (see the problems listing).
As slink said you have two overflow: hidden rules in your css:
#main-container {
overflow:hidden;
}
And
#content {
overflow:hidden;
}
If you disable/remove these you will able to use your scrollbars again. Unfortunately the padding / negative margin "hack" will be visible. I recommend you to completely remove this solution and use faux columns. Faux columns background can be added to your #main-content or even the #content div (not just like the example in the ALA article that sets the background image to the body tag).
Good luck!
Update: Sorry, let me correct myself: to use faux columns in your case it is better to set the current background to the html element and the faux background to body element.
Assuming your equal height columns are the left menu and right content in that example, you could just use a margin-left property on the right-column and set the background colour of the container to the desired left-column colour. This would assume your right content always has a greater height than the left, but there are other ways round this.
#container {
width: 960px;
background-color: #000;
}
#menu {
float:left;
width: 240px;
}
#content {
float:right:
margin-left: 240px;
background-color: #fff;
}
<div id="container">
<div id="menu">
<ul>
<li>Home</li>
</ul>
</div>
<div id="content">
stuff goes here
</div>
</div>
The problem is caused by two overflow: hidden; rules defined on elements #content and #main-contaniner.

Resources