JQuery: Unable scroll to bottom of page when using addClass & removeClass - javascript

I have the following code that is adding the class "sticky" on scroll and removing the class "sticky" when the element reaches the anchor "#search-anchor".
However, I'm unable to scroll all the way down to see the full footer. Any ideas why?
https://jsfiddle.net/coldfusion/6z6q3kxm/2/
<style>
header,footer {height:50px;background-color:red;}
section {height: 50px; background-color: yellow;}
#search-anchor {background-color:blue;}
.body {height:1600px;background-color:black;}
.sticky {
position: fixed;
bottom: 0px;
width: 1000px;
margin-right: auto !important;
margin-left: auto !important;
z-index: 999;
}
</style>
<script>
jQuery(document).ready(function() {
var a = jQuery(".inline-search").offset().top,
n = function() {
var n = jQuery(window).scrollTop(),
n = n + 10;
n > a ? jQuery(".inline-search").addClass("sticky") : jQuery(".inline-search").removeClass("sticky")
};
n(), jQuery(window).scroll(function() {
n()
})
}),
jQuery(document).ready(function() {
var search = $(".inline-search");
$(window).scroll(function() {
var anchorPosition = $("#search-anchor").offset().top;
var navHeight = $(".inline-search").height();
var navPosition = $(".inline-search").offset().top;
if ((navPosition + navHeight) >= anchorPosition) {
search.removeClass('sticky');
}
})
});
</script>
<header>HEADER</header>
<section class="inline-search">
</section>
<div class="body"></div>
<section id="search-anchor">
</section>
<footer>FOOTER</footer>

If you use the instruction debugger to pause the execution of JavaScript in the console, you get exactly what #mike-mccaughan is explaining.
You script adds and removes the element .inline-searchfrom the document flow because of the CSS position:fixed;.
If you scroll down and re-add .inline-search to the document flow, the document height changes again and the scrollbar has some space, so your code that detects if we're at the bottom of the page doesn't work anymore. What happens is an infinite and very fast loop of adding and re-adding your fixed positioning: because of that, it seems as if the page "stops" scrolling before getting to the footer.
In order to create such an effect (fixed positioning upon scroll), I'd recommend using Bootstrap's affix jQuery plugin. Even further: Bootstrap recently announced that they're dropping Affix because of sticky positioning coming to CSS, so the real "future-proof" recommendation here would be to use sticky positioning with a polyfill to allow older browsers to have the same effect.
Bootstrap affix : https://github.com/twbs/bootstrap/blob/master/js/affix.js,
http://getbootstrap.com/javascript/#affix
Bootstrap drops affix : https://github.com/twbs/bootstrap/blob/87751da48232ac4ca1964c819aaf89e78a3f9e64/docs/migration.md
Sticky positioning : http://html5please.com/#sticky
Sticky positioning demo : http://html5-demos.appspot.com/static/css/sticky.html - Sticky positioning availability accross browsers : http://caniuse.com/#feat=css sticky
And a sticky positioning polyfill for older browsers : https://github.com/filamentgroup/fixed-sticky

Related

How to fix/stick an header row on scroll?

i would like to fix/stick the header "top" row (NOT middle and bottom rows) during the scroll of the page. I also created a class for the top row. This is my header code:
enter image description here
Please add your code in the questions and do not paste the link to a screenshot. It would help to get better and faster answers, if you also add your css code, because the styling is done within the stylesheet.
But I think I got your problem.
If your header is at the top of your page, it is a simple css property you add to your class in the stylesheet:
.riga_sup {
position: fixed;
top: 0;
}
Take a look at the example: https://www.w3schools.com/howto/tryit.asp?filename=tryhow_css_fixed_menu
If your header is not at the top of your page, but you want it to be fixed if it is at the top position after scrolling, you will need a little bit of javascript to add the class with the "position: fixed;" styling when the scroll reaches the the element. In the example the header has a ID of "myHeader".
<script>
window.onscroll = function() {myFunction()};
var header = document.getElementById("header");
var sticky = header.offsetTop;
function myFunction() {
if (window.pageYOffset > sticky) {
header.classList.add("sticky");
} else {
header.classList.remove("sticky");
}
}
</script>
In your CSS you give the properties to the "sticky" class:
.sticky {
position: fixed;
top: 0;
}
Here is an example: https://www.w3schools.com/howto/howto_js_sticky_header.asp

Play animation when content get into view

I've been searching on many posts but almost all of them are confusing.
I'm working with animate.css into a which is at the middle of my page.
For default the animation is played when the page is loaded, but i want that it play when i reach the (when i'm scrolling).
Please, don't say about JS Reveal, i'd like to use the animation from animate.css
What i was trying:
HTML
<!-- Others div above -->
<div class="row sf-medida" id="sf-medida" onscroll="Animar();">
<!-- Others div below -->
JS
function Animar() {
setTimeout(function(){
document.getElementById("sf-medida").style.visibility = "visible";
$("#titulo-general").addClass("animated fadeInLeft");
$(".sub-titulo").addClass("animated bounceInRight");
$(".titulo-izquierda").addClass("animated swing");
$(".texto-1").addClass("animated fadeIn");
$(".texto-2").addClass("animated fadeIn");
},1000)
}
But it doesn't work, however, i've tried adding
window.addEventListener("scroll", Animar);
But what it does is that the animation is played whenever i scroll on the page,
This can be very easily done using little jquery. All you need to do is listen to the scroll event, then check if user have scrolled to the target element. If the user did, then add animation class from your animate.css. Adjust your if condition according to your desires. Check the below code and fiddle https://jsfiddle.net/15z6x5ko/ for reference
$(document).ready(function(){
$(document).scroll(function(evt){
var v2 = Math.abs($('.box').position().top - $(window).height()/2);
var v1 = $(this).scrollTop();
if( v1 > v2 ){
console.log('in');
$('.box').addClass('animated flip')
}
});
});
So as per your request, let me try to explain the code line by line
$(document).ready(function(){
This is easy to understand. It just waits for browser to load all HTML & CSS first and when everything is loaded, the javascript code inside this function will run.
$(document).scroll(function(evt){
This is an event handler, our callback function will run whenever user scrolls on document. Remember change $(document) according whatever the parent is of your target element. So if your target div is inside another div whose class is .parent then use $('.parent').scroll . As for my code I am listening the event on document. When my document scrolls, my event will trigger.
var v1 = $(this).scrollTop();
This code will get the amount of scrolling user had done in pixels.
var v2 = Math.abs($('.box').position().top - $(window).height()/2);
This is a simple math that checks the position of my target div from its parent element subtracting the half of the size of window from it. This will return the pixel positing of your target div. So when user reaches this pixel positing while scrolling, your animation will start.
$('.box').addClass('animated flip')
Now this code simply adds the animation css classes into the target div as soon as user scrolls to the target div.
I'm using "WoW.js" for my scroll reveal library. It's pretty easy to use, like for real. One line of code
<div class="wow fadeIn">content</div>
Here, take a look: http://mynameismatthieu.com/WOW/docs.html
Here's an example using Jquery.
In it we use .scrollTop and .height to measure the videos container from the top of the page so that we know when it comes into view when scrolling. (it's actually set to load when it reaches 100px below the bottom of the viewable area, a sort of preload. you can adjust it to whatever you like.)
The video load is done by copying the url from data-src= into src= when the video container is at the desired spot on the page. (in this case, 100px below the viewable area)
fiddle
note, the video won't load on stack so be sure to view the fiddle
https://jsfiddle.net/Hastig/xszu6b1p/
I scraped it together from these two answers..
Youtube Autoplay
Ladyload Images
$(window).scroll(function() {
$.each($('iframe'), function() {
if ( $(this).attr('data-src') && $(this).offset().top < ($(window).scrollTop() + $(window).height() + 100) ) {
var source = $(this).data('src');
$(this).attr('src', source);
$(this).removeAttr('data-src');
}
})
})
body {
margin: 0;
}
.filler {
display: flex;
justify-content: center;
align-items: center;
height: 800px;
}
.filler-top { background-color: blue }
.filler-btm { background-color: green; }
.video-container {
/* css tricks - responsive iframe video */
/* https://css-tricks.com/NetMag/FluidWidthVideo/Article-FluidWidthVideo.php */
position: relative;
padding-bottom: 56.25%; /* 16:9 */
padding-top: 25px;
height: 0;
display: flex;
justify-content: center;
background-color: red;
}
.video-container iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="filler filler-top">filler top</div>
<div class="video-container">
<iframe data-src="https://www.youtube.com/embed/f0JDs4FY8cQ?rel=0&autoplay=1"></iframe>
</div>
<div class="filler filler-btm">filler bottom</div>

Single page webapp screen transitions with maintaining the scroll position

I'm build a single page web application for mobile phones. The application should implement transitions between "screens" (like any other mobile app e.g. Facebook, Twitter) and these transitions should be animated (slide left-right). Each screen has to preserve its scroll position between transitions.
One obvious solution that comes in mind is this:
Viewport
+----------+
|+--------+| +--------+ +--------+ +--------+
|| DIV1 || | DIV2 | | DIV3 | | DIV4 |
|| || | | | | | |
|| || | | | | | |
|| || | | | | | |
|| || | | | | | |
|+--------+| +--------+ +--------+ +--------+
+----------+
The different screens are put into containers (DIV1, DIV2, ...) which are styled to fit the screen (position: absolute; width: 100%; height: 100%; top: 0) and have overflow-x: scroll. The containers are positioned next to each other and the transition is as easy as animating their left property.
Easy so far.
The problem is the following: in this implementation the address bar doesn't disappear in the mobile browser when the user scrolls down.
I'm talking about this feature:
It's because mobile browsers do this only if the user scrolls the body - not a container in the body. There are several suggestions for solution but they don't work in all targeted platforms (Android Chrome and native browser, iPhone Safari) and are quite hacky. I'd like to preserve the original behavior of the browser as it is.
For that reason - apparently - need to leave the scrolling on the body. This means that containers have to be "full-length" (and not overflow-scroll), still positioned next to each other. This is where transitions become difficult if you think about it.
My current solution has the following steps when transitioning from DIV1 to DIV2:
position top of DIV2 to the current scrollTop of the window
animate DIV1's and DIV2's left property so that DIV2 fills the screen
move DIV2's top to 0 once the animation has finished so that the user can't scroll back further than the top of this screen
Move the window's scrollTop to 0
Hide DIV1 (in case it's longer than DIV2)
Transitioning back to DIV1 is similar, in reverse. This actually works quite nice (although it's insanely complex and uses transition event listeners) but unfortunately there's a really ugly flickering effect between step 3 and 4 under iOS Safari because it renders the page right after step 3 without waiting for step 4.
I am looking for a framework-independent (vanilla JS) solution.
your approach was quite right. you probably get the flickering due to the scroll change position. the trick is to change the div's to position: fixed when scrolling and, than change them back afterwards.
the steps are:
save the current scroll vertical position
change the div's to position: fixed
change the div's scrollTop to 0 - scrollPosition
start horizontal transition
after the transition:
change the window's scroll position with scrollTo()
revert position: fixed on the div's so the natural browser behavior works.
here is a plain vanilla javascript example (also as fiddle):
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title></title>
<style type="text/css">
body {
margin: 0;
padding: 0;
}
.container {
position: absolute;
overflow: hidden;
width: 320px;
height: 5000px;
}
.screen {
position: absolute;
width: 320px;
height: 5000px;
transition: left 0.5s;
}
#screen1 {
background: linear-gradient(red, yellow);
}
#screen2 {
left: 320px;
background: linear-gradient(green, blue);
}
#button {
position: fixed;
left: 20px;
top: 20px;
width: 100px;
height: 50px;
background-color: white;
color: black;
}
</style>
</head>
<body>
<div class="container">
<div id="screen1" class="screen"></div>
<div id="screen2" class="screen"></div>
</div>
<div id="button">transition</div>
<script type="text/javascript">
var screenActive = 1;
var screen1 = document.getElementById('screen1');
var screen2 = document.getElementById('screen2');
var screen1ScrollTop = 0;
var screen2ScrollTop = 0;
function onClick()
{
console.log('getting the event');
if ( screenActive === 1 ) {
// will show screen 2
screen1ScrollTop = document.body.scrollTop;
screen1.style.position = 'fixed';
screen2.style.position = 'fixed';
screen1.style.top = (0 - screen1ScrollTop) + 'px';
screen2.style.top = (0 - screen2ScrollTop) + 'px';
screenActive = 2;
screen1.style.left = '-320px';
screen2.style.left = '0px';
}
else {
// will show screen 1
screen2ScrollTop = document.body.scrollTop;
screen1.style.position = 'fixed';
screen2.style.position = 'fixed';
screen1.style.top = (0 - screen1ScrollTop) + 'px';
screen2.style.top = (0 - screen2ScrollTop) + 'px';
screenActive = 1;
screen1.style.left = '0px';
screen2.style.left = '320px';
}
}
function onTransitionEnd(event)
{
if ( screenActive === 1 ) {
window.scrollTo(0, screen1ScrollTop);
}
else {
window.scrollTo(0, screen2ScrollTop);
}
screen1.style.position = 'absolute';
screen1.style.top = '0px';
screen2.style.position = 'absolute';
screen2.style.top = '0px';
}
screen1.addEventListener('webkitTransitionEnd', onTransitionEnd);
document.getElementById('button').addEventListener('click', onClick);
</script>
</body>
</html>
in this example i used the transitionEnd event. have in mind that if you have this event on both animating div's the event will fire twice. solutions to this are:
if the timings are identical just use the event on one div (used in the example)
use the event an all div's but just do changes respective to the event's div
animate a container with all the div's inside. so you will just need one event.
if you can not use the transitionEnd event use requestAnimationFrame() and animate manually via js
i also use a fixed height container for the transitions in this example. if you have div's with different height's you will have to change the containers height after the transition. ideally before reverting from position: fixed.
have in mind that changing a div to position: fixed will show it even if it is in a container with overflow: hidden. in the case of a mobile webapp this will not be an issue because the div's are outside of the screen. on a pc you might have to put another div over the other to hide the one transitioning in.
You can do something like this if you jquery is loaded
$(document).ready(function() {
if (navigator.userAgent.match(/Android/i)) {
window.scrollTo(0,0); // reset in case prev not scrolled
var nPageH = $(document).height();
var nViewH = window.outerHeight;
if (nViewH > nPageH) {
nViewH -= 250;
$('BODY').css('height',nViewH + 'px');
}
window.scrollTo(0,1);
}
});
For Iphone you have to do something like mentioned in below link
http://matt.might.net/articles/how-to-native-iphone-ipad-apps-in-javascript/
and for safari
https://developer.apple.com/library/safari/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/MetaTags.html
Hope it helps you somehow!!
Using window.scrollTo(0,1); you can make the navigation bar disappear. It's a hack, but it works.
Why not:
<body>
<div id=header>Header</div>
<div id=content>Scrollable Content</div>
<div id=footer>Footer</div>
</body>
Then the CSS:
#header,#footer,#content{
left:0%;
right:0%;
}
#header,#footer{
position:fixed;
}
#header{
top:0%;
height:30px;
}
#footer{
bottom:0%;
height:30px;
}
#content{
position:absolute;
top:30px;
height:1000px; /*Whatever you need it to be*/
}
The touch screen responds to the <body> tag, not the <div>, so setting position:fixed on #header and #footer allow them to maintain position relative to the window, regardless of scroll position, and then when the user scrolls the content, they scroll the <body>
EDIT: I have implemented this as an example:
https://www.museri.com/M
Visit on your mobile device.
I think I figured it out, it's tricky.
In short: in the question I describe my current solution that flickers in iOS. At point 3 you need to add position: fixed to DIV2. That way it's gonna "stick" and you avoid the flickering at point 4. Then you need to delay point 4 a couple of milliseconds (setTimeout, 500ms worked for me but probably could be smaller) and set position: absolute again to DIV2 right after window.scrollTo. I'm not sure that's the reason you need the delay, but without it the screen still flickers.
If there's interest I can post a PoC later.
As a side note, I found it pretty disappointing that most if the people who answered did not read the question entirely or just completely ignored some criteria (framework-independence, keeping original scroll behavior). Most of them suggested solutions that I already specifically linked in the question as not acceptable. Some of them even reclaim when get downvoted.
EDIT: dreamlab answered just a couple of minutes before I posted my solution. Both solutions use position: fixed. His solution is more detailed too. He deserves the bounty.

Sticky header after some scrolling?

okay here's an example of what i am trying to ask,
the nav bar of usatoday.
I'm using bootstrap affix. here's my code
<div class="header">
<div class="header-1">
<h1>this is some logo</h1>
</div>
<div class="header-2">
<h3>this is some heading</h3>
</div>
</div>
<div class="content" style="height:2500px;">
</div>
<div class="footer">
this is a footer
</div>
JavaScript
$('.header-2').affix({
});
how can I make the div header-2 to be fixed on the top, (when there is some scrolling and the div header-2 just reach the top position) as of the site I've mentioned earlier?
I would love to see the header-1 and header-2, but some scrolling should hide header-1 and stick header-2 to the top most.
thanks
See this Jsfiddle
you can check the position of the slider and add class accordingly
$(window).scroll(function () {
if( $(window).scrollTop() > $('#header-2').offset().top && !($('#header-2').hasClass('posi'))){
$('#header-2').addClass('posi');
} else if ($(window).scrollTop() == 0){
$('#header-2').removeClass('posi');
}
});
use jquery look at this example
http://jsfiddle.net/5n5MA/2/
var fixmeTop = $('.fixme').offset().top; // Get initial position
$(window).scroll(function() { // Assign scroll event listener
var currentScroll = $(window).scrollTop(); // Get current position
if (currentScroll >= fixmeTop) { // Make it fixed if you've scrolled to it
$('.fixme').css({
position: 'fixed',
top: '0',
left: '0'
});
} else { // Make it static if you scroll above
$('.fixme').css({
position: 'static'
});
}
});
Bootstrapped answer using Bootstrap.affix()
$('.header-2').affix({
offset: {
top: function () {
return (this.top = $(".header-2").offset().top);
}
}
});
This also needs CSS for the fixed positioning (see the Docs).
The affix plugin toggles between three classes, each representing a
particular state: .affix, .affix-top, and .affix-bottom. You must
provide the styles for these classes yourself (independent of this
plugin) to handle the actual positions.
.header-2.affix {
top: 0;
}
Working example at Bootply: http://www.bootply.com/S03RlcT0z0
<style>
.header {
top: 0%;
left: 0%;
width: 100%;
position:fixed;
}
</style>
I'm sorry didnt look at your problem carefully.
This may helps you Issue with Fixed Header and Bootstrap Affix / Scrollspy - Not jumping to correct location

offsetting an html anchor to adjust for fixed header [duplicate]

This question already has answers here:
Fixed page header overlaps in-page anchors
(38 answers)
Closed 7 years ago.
I am trying to clean up the way my anchors work. I have a header that is fixed to the top of the page, so when you link to an anchor elsewhere in the page, the page jumps so the anchor is at the top of the page, leaving the content behind the fixed header (I hope that makes sense). I need a way to offset the anchor by the 25px from the height of the header. I would prefer HTML or CSS, but Javascript would be acceptable as well.
You could just use CSS without any javascript.
Give your anchor a class:
<a class="anchor" id="top"></a>
You can then position the anchor an offset higher or lower than where it actually appears on the page, by making it a block element and relatively positioning it. -250px will position the anchor up 250px
a.anchor {
display: block;
position: relative;
top: -250px;
visibility: hidden;
}
I found this solution:
<a name="myanchor">
<h1 style="padding-top: 40px; margin-top: -40px;">My anchor</h1>
</a>
This doesn't create any gap in the content and anchor links works really nice.
I was looking for a solution to this as well. In my case, it was pretty easy.
I have a list menu with all the links:
<ul>
<li>one</li>
<li>two</li>
<li>three</li>
<li>four</li>
</ul>
And below that the headings where it should go to.
<h3>one</h3>
<p>text here</p>
<h3>two</h3>
<p>text here</p>
<h3>three</h3>
<p>text here</p>
<h3>four</h3>
<p>text here</p>
Now because I have a fixed menu at the top of my page I can't just make it go to my tag because that would be behind the menu.
Instead, I put a span tag inside my tag with the proper id.
<h3><span id="one"></span>one</h3>
Now use 2 lines of CSS to position them properly.
h3{ position:relative; }
h3 span{ position:absolute; top:-200px;}
Change the top value to match the height of your fixed header (or more).
Now I assume this would work with other elements as well.
FWIW this worked for me:
[id]::before {
content: '';
display: block;
height: 75px;
margin-top: -75px;
visibility: hidden;
}
As this is a concern of presentation, a pure CSS solution would be ideal. However, this question was posed in 2012, and although relative positioning / negative margin solutions have been suggested, these approaches seem rather hacky, create potential flow issues, and cannot respond dynamically to changes in the DOM / viewport.
With that in mind I believe that using JavaScript is still (February 2017) the best approach. Below is a vanilla-JS solution which will respond both to anchor clicks and resolve the page hash on load (See JSFiddle). Modify the .getFixedOffset() method if dynamic calculations are required. If you're using jQuery, here's a modified solution with better event delegation and smooth scrolling.
(function(document, history, location) {
var HISTORY_SUPPORT = !!(history && history.pushState);
var anchorScrolls = {
ANCHOR_REGEX: /^#[^ ]+$/,
OFFSET_HEIGHT_PX: 50,
/**
* Establish events, and fix initial scroll position if a hash is provided.
*/
init: function() {
this.scrollToCurrent();
window.addEventListener('hashchange', this.scrollToCurrent.bind(this));
document.body.addEventListener('click', this.delegateAnchors.bind(this));
},
/**
* Return the offset amount to deduct from the normal scroll position.
* Modify as appropriate to allow for dynamic calculations
*/
getFixedOffset: function() {
return this.OFFSET_HEIGHT_PX;
},
/**
* If the provided href is an anchor which resolves to an element on the
* page, scroll to it.
* #param {String} href
* #return {Boolean} - Was the href an anchor.
*/
scrollIfAnchor: function(href, pushToHistory) {
var match, rect, anchorOffset;
if(!this.ANCHOR_REGEX.test(href)) {
return false;
}
match = document.getElementById(href.slice(1));
if(match) {
rect = match.getBoundingClientRect();
anchorOffset = window.pageYOffset + rect.top - this.getFixedOffset();
window.scrollTo(window.pageXOffset, anchorOffset);
// Add the state to history as-per normal anchor links
if(HISTORY_SUPPORT && pushToHistory) {
history.pushState({}, document.title, location.pathname + href);
}
}
return !!match;
},
/**
* Attempt to scroll to the current location's hash.
*/
scrollToCurrent: function() {
this.scrollIfAnchor(window.location.hash);
},
/**
* If the click event's target was an anchor, fix the scroll position.
*/
delegateAnchors: function(e) {
var elem = e.target;
if(
elem.nodeName === 'A' &&
this.scrollIfAnchor(elem.getAttribute('href'), true)
) {
e.preventDefault();
}
}
};
window.addEventListener(
'DOMContentLoaded', anchorScrolls.init.bind(anchorScrolls)
);
})(window.document, window.history, window.location);
Pure css solution inspired by Alexander Savin:
a[name] {
padding-top: 40px;
margin-top: -40px;
display: inline-block; /* required for webkit browsers */
}
Optionally you may want to add the following if the target is still off the screen:
vertical-align: top;
My solution combines the target and before selectors for our CMS. Other techniques don't account for text in the anchor. Adjust the height and the negative margin to the offset you need...
:target::before {
content: '';
display: block;
height: 180px;
margin-top: -180px;
}
This takes many elements from previous answers and combines into a tiny (194 bytes minified) anonymous jQuery function. Adjust fixedElementHeight for the height of your menu or blocking element.
(function($, window) {
var adjustAnchor = function() {
var $anchor = $(':target'),
fixedElementHeight = 100;
if ($anchor.length > 0) {
$('html, body')
.stop()
.animate({
scrollTop: $anchor.offset().top - fixedElementHeight
}, 200);
}
};
$(window).on('hashchange load', function() {
adjustAnchor();
});
})(jQuery, window);
If you don't like the animation, replace
$('html, body')
.stop()
.animate({
scrollTop: $anchor.offset().top - fixedElementHeight
}, 200);
with:
window.scrollTo(0, $anchor.offset().top - fixedElementHeight);
Uglified version:
!function(o,n){var t=function(){var n=o(":target"),t=100;n.length>0&&o("html, body").stop().animate({scrollTop:n.offset().top-t},200)};o(n).on("hashchange load",function(){t()})}(jQuery,window);
For modern browsers, just add the CSS3 :target selector to the page. This will apply to all the anchors automatically.
:target {
display: block;
position: relative;
top: -100px;
visibility: hidden;
}
You can do it without js and without altering html. It´s css-only.
a[id]::before {
content: '';
display: block;
height: 50px;
margin: -30px 0 0;
}
That will append a pseudo-element before every a-tag with an id. Adjust values to match the height of your header.
I had been facing a similar issue, unfortunately after implementing all the solutions above, I came to the following conclusion.
My inner elements had a fragile CSS structure and implementing a position relative / absolute play, was completely breaking the page design.
CSS is not my strong suit.
I wrote this simple scrolling js, that accounts for the offset caused due to the header and relocated the div about 125 pixels below. Please use it as you see fit.
The HTML
<div id="#anchor"></div> <!-- #anchor here is the anchor tag which is on your URL -->
The JavaScript
$(function() {
$('a[href*=#]:not([href=#])').click(function() {
if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'')
&& location.hostname == this.hostname) {
var target = $(this.hash);
target = target.length ? target : $('[name=' + this.hash.slice(1) +']');
if (target.length) {
$('html,body').animate({
scrollTop: target.offset().top - 125 //offsets for fixed header
}, 1000);
return false;
}
}
});
//Executed on page load with URL containing an anchor tag.
if($(location.href.split("#")[1])) {
var target = $('#'+location.href.split("#")[1]);
if (target.length) {
$('html,body').animate({
scrollTop: target.offset().top - 125 //offset height of header here too.
}, 1000);
return false;
}
}
});
See a live implementation here.
For the same issue, I used an easy solution : put a padding-top of 40px on each anchor.
As #moeffju suggests, this can be achieved with CSS. The issue I ran into (which I'm surprised I haven't seen discussed) is the trick of overlapping previous elements with padding or a transparent border prevents hover and click actions at the bottom of those sections because the following one comes higher in the z-order.
The best fix I found was to place section content in a div that is at z-index: 1:
// Apply to elements that serve as anchors
.offset-anchor {
border-top: 75px solid transparent;
margin: -75px 0 0;
-webkit-background-clip: padding-box;
-moz-background-clip: padding;
background-clip: padding-box;
}
// Because offset-anchor causes sections to overlap the bottom of previous ones,
// we need to put content higher so links aren't blocked by the transparent border.
.container {
position: relative;
z-index: 1;
}
Solutions with changing position property are not always possible (it can destroy layout) therefore I suggest this:
HTML:
<a id="top">Anchor</a>
CSS:
#top {
margin-top: -250px;
padding-top: 250px;
}
Use this:
<a id="top"> </a>
to minimize overlapping, and set font-size to 1px. Empty anchor will not work in some browsers.
Borrowing some of the code from an answer given at this link (no author is specified), you can include a nice smooth-scroll effect to the anchor, while making it stop at -60px above the anchor, fitting nicely underneath the fixed bootstrap navigation bar (requires jQuery):
$(".dropdown-menu a[href^='#']").on('click', function(e) {
// prevent default anchor click behavior
e.preventDefault();
// animate
$('html, body').animate({
scrollTop: $(this.hash).offset().top - 60
}, 300, function(){
});
});
The above methods don't work very well if your anchor is a table element or within a table (row or cell).
I had to use javascript and bind to the window hashchange event to work around this (demo):
function moveUnderNav() {
var $el, h = window.location.hash;
if (h) {
$el = $(h);
if ($el.length && $el.closest('table').length) {
$('body').scrollTop( $el.closest('table, tr').position().top - 26 );
}
}
}
$(window)
.load(function () {
moveUnderNav();
})
.on('hashchange', function () {
moveUnderNav();
});
* Note: The hashchange event is not available in all browsers.
You can achieve this without an ID using the a[name]:not([href]) css selector. This simply looks for links with a name and no href e.g. <a name="anc1"></a>
An example rule might be:
a[name]:not([href]){
display: block;
position: relative;
top: -100px;
visibility: hidden;
}
Instead of having a fixed-position navbar which is underlapped by the rest of the content of the page (with the whole page body being scrollable), consider instead having a non-scrollable body with a static navbar and then having the page content in an absolutely-positioned scrollable div below.
That is, have HTML like this...
<div class="static-navbar">NAVBAR</div>
<div class="scrollable-content">
<p>Bla bla bla</p>
<p>Yadda yadda yadda</p>
<p>Mary had a little lamb</p>
<h2 id="stuff-i-want-to-link-to">Stuff</h2>
<p>More nonsense</p>
</div>
... and CSS like this:
.static-navbar {
height: 100px;
}
.scrollable-content {
position: absolute;
top: 100px;
bottom: 0;
overflow-y: scroll;
width: 100%;
}
There is one significant downside to this approach, however, which is that while an element from the page header is focused, the user will not be able to scroll the page using the keyboard (e.g. via the up and down arrows or the Page Up and Page Down keys).
Here's a JSFiddle demonstrating this in action.
This was inspired by the answer by Shouvik - same concept as his, only the size of the fixed header isn't hard coded. As long as your fixed header is in the first header node, this should "just work"
/*jslint browser: true, plusplus: true, regexp: true */
function anchorScroll(fragment) {
"use strict";
var amount, ttarget;
amount = $('header').height();
ttarget = $('#' + fragment);
$('html,body').animate({ scrollTop: ttarget.offset().top - amount }, 250);
return false;
}
function outsideToHash() {
"use strict";
var fragment;
if (window.location.hash) {
fragment = window.location.hash.substring(1);
anchorScroll(fragment);
}
}
function insideToHash(nnode) {
"use strict";
var fragment;
fragment = $(nnode).attr('href').substring(1);
anchorScroll(fragment);
}
$(document).ready(function () {
"use strict";
$("a[href^='#']").bind('click', function () {insideToHash(this); });
outsideToHash();
});
I'm facing this problem in a TYPO3 website, where all "Content Elements" are wrapped with something like:
<div id="c1234" class="contentElement">...</div>
and i changed the rendering so it renders like this:
<div id="c1234" class="anchor"></div>
<div class="contentElement">...</div>
And this CSS:
.anchor{
position: relative;
top: -50px;
}
The fixed topbar being 40px high, now the anchors work again and start 10px under the topbar.
Only drawback of this technique is you can no longer use :target.
Adding to Ziav's answer (with thanks to Alexander Savin), I need to be using the old-school <a name="...">...</a> as we're using <div id="...">...</div> for another purpose in our code. I had some display issues using display: inline-block -- the first line of every <p> element was turning out to be slightly right-indented (on both Webkit and Firefox browsers). I ended up trying other display values and display: table-caption works perfectly for me.
.anchor {
padding-top: 60px;
margin-top: -60px;
display: table-caption;
}
I added 40px-height .vspace element holding the anchor before each of my h1 elements.
<div class="vspace" id="gherkin"></div>
<div class="page-header">
<h1>Gherkin</h1>
</div>
In the CSS:
.vspace { height: 40px;}
It's working great and the space is not chocking.
how about hidden span tags with linkable IDs that provide the height of the navbar:
#head1 {
padding-top: 60px;
height: 0px;
visibility: hidden;
}
<span class="head1">somecontent</span>
<h5 id="headline1">This Headline is not obscured</h5>
heres the fiddle: http://jsfiddle.net/N6f2f/7
You can also add an anchor with follow attr:
(text-indent:-99999px;)
visibility: hidden;
position:absolute;
top:-80px;
and give the parent container a position relative.
Works perfect for me.
A further twist to the excellent answer from #Jan is to incorporate this into the #uberbar fixed header, which uses jQuery (or MooTools). (http://davidwalsh.name/persistent-header-opacity)
I've tweaked the code so the the top of the content is always below not under the fixed header and also added the anchors from #Jan again making sure that the anchors are always positioned below the fixed header.
The CSS:
#uberbar {
border-bottom:1px solid #0000cc;
position:fixed;
top:0;
left:0;
z-index:2000;
width:100%;
}
a.anchor {
display: block;
position: relative;
visibility: hidden;
}
The jQuery (including tweaks to both the #uberbar and the anchor approaches:
<script type="text/javascript">
$(document).ready(function() {
(function() {
//settings
var fadeSpeed = 200, fadeTo = 0.85, topDistance = 30;
var topbarME = function() { $('#uberbar').fadeTo(fadeSpeed,1); }, topbarML = function() { $('#uberbar').fadeTo(fadeSpeed,fadeTo); };
var inside = false;
//do
$(window).scroll(function() {
position = $(window).scrollTop();
if(position > topDistance && !inside) {
//add events
topbarML();
$('#uberbar').bind('mouseenter',topbarME);
$('#uberbar').bind('mouseleave',topbarML);
inside = true;
}
else if (position < topDistance){
topbarME();
$('#uberbar').unbind('mouseenter',topbarME);
$('#uberbar').unbind('mouseleave',topbarML);
inside = false;
}
});
$('#content').css({'margin-top': $('#uberbar').outerHeight(true)});
$('a.anchor').css({'top': - $('#uberbar').outerHeight(true)});
})();
});
</script>
And finally the HTML:
<div id="uberbar">
<!--CONTENT OF FIXED HEADER-->
</div>
....
<div id="content">
<!--MAIN CONTENT-->
....
<a class="anchor" id="anchor1"></a>
....
<a class="anchor" id="anchor2"></a>
....
</div>
Maybe this is useful to somebody who likes the #uberbar fading dixed header!
#AlexanderSavin's solution works great in WebKit browsers for me.
I additionally had to use :target pseudo-class which applies style to the selected anchor to adjust padding in FF, Opera & IE9:
a:target {
padding-top: 40px
}
Note that this style is not for Chrome / Safari so you'll probably have to use css-hacks, conditional comments etc.
Also I'd like to notice that Alexander's solution works due to the fact that targeted element is inline. If you don't want link you could simply change display property:
<div id="myanchor" style="display: inline">
<h1 style="padding-top: 40px; margin-top: -40px;">My anchor</h1>
</div>
Here's the solution that we use on our site. Adjust the headerHeight variable to whatever your header height is. Add the js-scroll class to the anchor that should scroll on click.
// SCROLL ON CLICK
// --------------------------------------------------------------------------
$('.js-scroll').click(function(){
var headerHeight = 60;
$('html, body').animate({
scrollTop: $( $.attr(this, 'href') ).offset().top - headerHeight
}, 500);
return false;
});
I ran into this same issue and ended up handling the click events manually, like:
$('#mynav a').click(() ->
$('html, body').animate({
scrollTop: $($(this).attr('href')).offset().top - 40
}, 200
return false
)
Scroll animation optional, of course.

Categories