Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to stick the footer to the bottom of the page while moving it upward in a parallax-like effect?

I have a project where the requirement is to move the footer ( #footer ) upward while scrolling down the page in a parallax-like effect. When you start scrolling down the page, the footer should start moving upward only until it's visible in the (bottom part of the) viewport.

The footer should have covered most of the preceding <div> half way up and in full when it has reached the top of the viewport.

The page may have a similar html structure like this :

<body>
    <div id="sectionA" class="div">First section</div>
    <div id="sectionB" class="div">Second section</div>
    <div id="sectionC" class="div">Third section
        <div class="box"></div>
        <div class="box"></div>
        <div class="box"></div>
    </div>
    <div id="footer" class="div cf">Footer</div>
</body>

The parallax-like effect is achieved via javascript/jQuery adding a dynamic negative value to the top CSS property of the (relative positioned) footer. Here is the code for what it matters :

var $window = jQuery(window),
    $footer = jQuery("#footer"),
    $viewport = window.innerHeight,
    $startEffect = $footer.offset().top - $viewport;

function footerParallax() {
    var $scrollPos = $window.scrollTop() - $startEffect,
        $ratio = 0.6;
    $footer.css({
        top: -($scrollPos * $ratio)
    });
}

$window.scroll(function () {
    footerParallax();
});

The (obvious) issue is that as soon as the top property starts getting a negative value, the footer starts moving away from the bottom of the page.

I have prepared a JSFIDDLE and assigned colors to each section and body to make it clearer. The body (dark-red) is visible under the footer after scrolling to the bottom.

What have I tried?

  • modifying the margin-top instead of the top property:  this does the trick, however the preceding <div> that has to be covered by the footer (#sectionC in the example above) overlaps the contents of the footer and breaks its layout regardless that it is not visible due to its z-index property (added some floating boxes in the fiddle to make it evident.... a clearfix hack didn't help either.)
  • setting a static position to the footer: neither top or margin-top have effect over a static element.
  • Changing/reducing dynamically the height of #sectionC instead of top of footer to produce the effect of moving the second upwards :  the footer stops moving as soon as height is equal to 0 (neither negative size or negative paddings are allowed)
  • Changed the height dynamically of the html and/or body tags to no avail.

I have also tried some parallax plugins like skrollr and skrollr-stylesheets and some others.

The problem with this solution (same with others) is that it relays in an specific (offset) position of the footer measured in px and set in a data attribute, but if the content changes dynamically, for example using the masonry plugin to arrange elements in another section of the document, the measures become inaccurate and the footer may start moving too early or too late.

By the way, other CSS sticky-footer techniques won't work because, well, they actually push the footer to the bottom of the page, and here we are doing the opposite.

I guess the question is either :

  • how to keep the footer stick to the bottom of the page while it is moved upwards? - or -
  • how to reduce the gap to 0 between the end of the document and the bottom edge of the footer?

I am starting to think that this issue has not a real solution the way it is, or maybe I am already too tired to see the obvious. I am interested in learning alternative solutions or hacks via CSS / javascript / jQuery or all of the above.

Bear in mind that I am not asking how to create the parallax effect UNLESS a totally different approach (or tweaks to the existing js code) solves the position issue.

IMPORTANT : Please consider that this is a WP site with an XHTML 1.0 Transitional DOCTYPE, and has installed many other jQuery plugins like masonry, scrollTo, jQuery UI, etc. I may have not control to change many things from the original structure (and I don't want to) so the idea is to implement this without breaking too many things and from a modular script.

EDIT #1 : Added a graphic to clarify the question.

  • Figure A. shows a regular web page scrolled down to the end. The red square represents the viewport and the footer (grey) is slighted moved to the right for illustration purposes. The body has a reddish background color (not visible in normal conditions) just for illustration purposes too. NOTE: the height of each section as well as the height of the footer is determined by their content (forms, images, text, etc.) so is NOT fixed.

  • Figure B. shows the current issue: If footer slides up in a parallax-like effect (see JSFIDDLE for reference) while scrolling down the page, it starts covering any preceding section above it (WITHOUT modifying neither its own height or the height of the preceding sections) AND it also starts separating itself from the bottom of the page, therefore the body's color background becomes visible. NOTE: the bigger the viewport is (fullscreen mode for instance) the higher the footer is moved upward (and more content is covered by it)

  • Figure C. is the expected result: the footer should be stuck to the bottom of the page, in other words, it should be the last visible element after the page has been totally scrolled down (and not the body background as in Figure B.) Notice that the contents and the size of each section (including the footer) should (ideally) remain untouched. Having said that, adding padding bottom to the footer or increasing its height is not the expected result since it would break its original visual layout.

enter image description here

like image 644
JFK Avatar asked Aug 29 '13 04:08

JFK


2 Answers

Updated Version

Below is an updated version that should better matches your requirements.

This version goes back to relative positioning for the footer element and uses margin-top to position it.

margin-top is calculated off of the previous elements offset, height and current window scroll position. It then uses either

  1. the viewport height if the footer starts offscreen
  2. the initial top value of the footer element ($startEffect) if the footer started onscreen

to determine the actual value for margin-top.

To help keep the footer's layout from being affected by this, wrapping the content of the footer in an absolutely positioned div did the trick for the sample code provided.

Example Fiddle

CSS:

#footer > div {
    position: absolute;
    top: 0;
    left: 0;
    ...
}

HTML:

<div id="footer" class="div cf"><div>Footer</div></div>

Code:

var $window = jQuery(window),
    $footer = jQuery("#footer"),
    $viewport = window.innerHeight,
    $startEffect = $footer.offset().top;
    $prev = $footer.prev(),
    $useStartEffect = $startEffect < $viewport;


function footerParallax() {
    var $scrollPos = $window.scrollTop() - $startEffect,
        $ratio = 0.6;

    var prevOffset = $prev.offset().top + $prev.height() - $window.scrollTop();
    var marginTop = 0;

    if(prevOffset < $viewport && prevOffset < $startEffect) {
        if($useStartEffect) {
            marginTop = (prevOffset - $startEffect)*$ratio;
        } else {
            marginTop = (prevOffset - $viewport)*$ratio;       
        }
    }

    $footer.css({
        "margin-top": marginTop + 'px'
    });
}

$window.scroll(function () {
    footerParallax();
});

footerParallax();
like image 123
dc5 Avatar answered Sep 18 '22 10:09

dc5


How was it solved?

As I mentioned in my question, I was too tired to see the obvious but @dc5's answer put me on the right track :

To help keep the footer's layout from being affected, 
wrapping the content of the footer in an absolutely 
positioned div does the trick

Based on that comment, the answer became simpler than the whole code he proposed needing only :

  • (dynamically) wrapping the content of the footer in an absolutely positioned div using jQuery's .wrapInner() method
  • animating the footer by setting the margin-top property instead of the top property

So this extra CSS :

#footerInnerWrapper {
    position: absolute;
    top:0;
    left:0;
    width: 100%;
    background-color: #666 /* same as footer */
}

and the tweaked original code

var $window = jQuery(window),
    $footer = jQuery("#footer"),
    $viewport = window.innerHeight,
    $startEffect = $footer.offset().top - $viewport;

// add inner wrapper
$footer.wrapInner('<div id="footerInnerWrapper" />');

function footerParallax() {
    var $scrollPos = $window.scrollTop() - $startEffect,
        $ratio = 0.6;
    $footer.css({
        // top: -($scrollPos * $ratio) 
        marginTop: -($scrollPos * $ratio)
    });
}

$window.scroll(function () {
    footerParallax();
});

did the trick. See JSFIDDLE

like image 34
JFK Avatar answered Sep 20 '22 10:09

JFK