How to Make a Floating Footer Share Bar

Cnet Social Share

In a recent redesign of one of our websites, I was tasked with recreating a footer share bar that would dock/undock as the footer came into view. Now, I may not have done a tremendous job explaining that, so I’d like to point you to Cnet to give you an idea of what I wanted to implement. There is a screenshot to the right.

Do you notice the black share bar at the bottom of the window? Now, scroll to the bottom of the page and notice how the share bar docks at the top of the footer. Cool, right?

After a couple of hours, I was able to replicate this functionality closely and I ended up with this below.

Optiniche Social Share

Since it’s been so long since I have posted a code tutorial, I wanted to show you guys how to do this for your own website!

Let’s Start With HTML

Before we get into any Javascript or CSS, let’s go ahead and insert our HTML so that we can have something to work with. Now, since we are wanting to dock this share bar to our footer when the footer is within view, be sure to enter this code¬†directly before the footer on your website. I have included a screenshot of to show proper placement of this code.

Here is the code:

<div class="socialFooterWrap">
	<div class="social-links">
		<div class="socialClose"></div>
		<div class="fb">
		<script src="http://connect.facebook.net/en_US/all.js#xfbml=1"></script>
		<script type="text/javascript">
		document.write('<fb:like layout="button_count" show_faces="false" width="100"></fb:like>')
		</script></div>

		<div class="googplus">
		<g:plusone size="medium"></g:plusone></div>
		<div class="stumble">
		<script src="http://www.stumbleupon.com/hostedbadge.php?s=2"></script></div>
		<div class="tweet">
		<a href="http://twitter.com/share" data-url="<?php the_permalink(); ?>" class="twitter-share-button" data-count="horizontal">Tweet</a><script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script></div>
	</div>
</div>

Let’s Get Pretty With CSS

The HTML alone doesn’t look that pretty, does it? So, let’s go ahead and spruce it up with some CSS! Below is the CSS that I used to style the social share footer:

.social-links { margin:20px 0px 10px 0px; height:35px; }
.fb { float: left; margin-right : 10px; }
.googplus { float: left; }
.tweet { float: left; }
.linkedinshare { float: left; margin-right : 10px; }
.stumble { float: left; margin-top: 2px; margin-right: 10px; }
.socialFooterWrap {
	width: 100%;
	position: relative;
	bottom: 0;
	left: 0;
	z-index: 999;
	display: none;
}
.social-links {
	margin: 0 auto;
	background: url(**Your Logo Image Here**) no-repeat 20px center #000;
	border-radius: 10px 10px 0px 0px;
	box-shadow: 0 -1px 1px #524c4c;
	border-top: 1px solid #999;
	width: 268px;
	max-width: 100%;
	position: relative;
	padding: 12px 0 0 140px;
}
.socialClose {
	background: url(http://i1067.photobucket.com/albums/u440/Eric_Binnion/close-dark_zpsc44ffa70.png);
	height: 31px;
	width: 31px;
	position: absolute;
	top: -14px;
	right: -14px;
	cursor: pointer;
}
.show {
	display: inline;
}
.fixedPosition {
	position: fixed;
}

This CSS should be able to be added anywhere. Now, we’re only missing some jQuery to make this bad boy work!

jQuery Makes Things Cool

First things first, you need to make sure that you have added the jQuery library to your website. Your theme may have an option to include it, but if not, here is an awesome snippet from Chris Coyier (Think CSS-Tricks) that will add the jQuery to your website. This snippet needs to be added to the functions.php file of your WordPress website.

if (!is_admin()) add_action("wp_enqueue_scripts", "my_jquery_enqueue", 11);
function my_jquery_enqueue() {
   wp_deregister_script('jquery');
   wp_register_script('jquery', "http" . ($_SERVER['SERVER_PORT'] == 443 ? "s" : "") . "://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js", false, null);
   wp_enqueue_script('jquery');
}

Once you have added jQuery, you now need to create a custom.js file in your theme directory so that you can add some other javascript. Within that file, you are to add the code below:

/**
 * author Remy Sharp
 * url http://remysharp.com/2009/01/26/element-in-view-event-plugin/
 */
(function ($) {
    function getViewportHeight() {
        var height = window.innerHeight; // Safari, Opera
        var mode = document.compatMode;

        if ( (mode || !$.support.boxModel) ) { // IE, Gecko
            height = (mode == 'CSS1Compat') ?
            document.documentElement.clientHeight : // Standards
            document.body.clientHeight; // Quirks
        }

        return height;
    }

    $(window).scroll(function () {
        var vpH = getViewportHeight(),
            scrolltop = (document.documentElement.scrollTop ?
                document.documentElement.scrollTop :
                document.body.scrollTop),
            elems = [];

        // naughty, but this is how it knows which elements to check for
        $.each($.cache, function () {
            if (this.events && this.events.inview) {
                elems.push(this.handle.elem);
            }
        });

        if (elems.length) {
            $(elems).each(function () {
                var $el = $(this),
                    top = $el.offset().top,
                    height = $el.height(),
                    inview = $el.data('inview') || false;

                if (scrolltop > (top + height) || scrolltop + vpH < top) {
                    if (inview) {
                        $el.data('inview', false);
                        $el.trigger('inview', [ false ]);                        
                    }
                } else if (scrolltop < (top + height)) {
                    if (!inview) {
                        $el.data('inview', true);
                        $el.trigger('inview', [ true ]);
                    }
                }
            });
        }
    });

    // kick the event to pick up any elements already in view.
    // note however, this only works if the plugin is included after the elements are bound to 'inview'
    $(function () {
        $(window).scroll();
    });
})(jQuery);
/*!
 * jQuery Cookie Plugin
 * https://github.com/carhartl/jquery-cookie
 *
 * Copyright 2011, Klaus Hartl
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.opensource.org/licenses/GPL-2.0
 */
(function($) {
    $.cookie = function(key, value, options) {

        // key and at least value given, set cookie...
        if (arguments.length > 1 && (!/Object/.test(Object.prototype.toString.call(value)) || value === null || value === undefined)) {
            options = $.extend({}, options);

            if (value === null || value === undefined) {
                options.expires = -1;
            }

            if (typeof options.expires === 'number') {
                var days = options.expires, t = options.expires = new Date();
                t.setDate(t.getDate() + days);
            }

            value = String(value);

            return (document.cookie = [
                encodeURIComponent(key), '=', options.raw ? value : encodeURIComponent(value),
                options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
                options.path    ? '; path=' + options.path : '',
                options.domain  ? '; domain=' + options.domain : '',
                options.secure  ? '; secure' : ''
            ].join(''));
        }

        // key and possibly options given, get cookie...
        options = value || {};
        var decode = options.raw ? function(s) { return s; } : decodeURIComponent;

        var pairs = document.cookie.split('; ');
        for (var i = 0, pair; pair = pairs[i] && pairs[i].split('='); i++) {
            if (decode(pair[0]) === key) return decode(pair[1] || ''); // IE saves cookies with empty string as "c; ", e.g. without "=" as opposed to EOMB, thus pair[1] may be undefined
        }
        return null;
    };
})(jQuery);

(function($){

  var settings = {
        speed: 350 //animation duration
      , easing: "linear" //use easing plugin for more options
      , padding: 10
      , constrain: false
    }
    , $window = $(window)
    , stickyboxes = []
    , methods = {

          init:function(opts){
            settings = $.extend(settings,opts);
            return this.each(function () {
              var $this = $(this);
              setPosition($this);
              stickyboxes[stickyboxes.length] = $this;
              moveIntoView();
            });
          }

        , remove:function(){
            return this.each(function () {
              var sticky = this;
              $.each(stickyboxes, function (i, $sb) {
                if($sb.get(0) === sticky){
                reset(null, $sb);
                stickyboxes.splice(i, 1);
                return false;
                }
              });
            });
          }

        , destroy: function () {
            $.each(stickyboxes, function (i, $sb) {
              reset(null, $sb);
            });
            stickyboxes=[];
            $window.unbind("scroll", moveIntoView);
            $window.unbind("resize", reset);
            return this;
          }

      };

  var moveIntoView = function () {
    $.each(stickyboxes, function (i, $sb) {
      var $this = $sb
        , data = $this.data("stickySB");
      if (data) {
        var sTop = $window.scrollTop() - data.offs.top
          , currOffs = $this.offset()
          , origTop = data.orig.offset.top - data.offs.top
          , animTo = origTop;
        //scrolled down out of view
        if (origTop < sTop) {
					//make sure to stop inside parent
          if ((sTop + settings.padding) > data.offs.bottom)
            animTo = data.offs.bottom;
          else animTo = sTop + settings.padding;
        }
        $this
          .stop()
          .animate(
              {top: animTo}
            , settings.speed
            , settings.easing
        );
      }
    });
  }

  var setPosition = function ($sb) {
    if ($sb) {
      var $this = $sb
        , $parent = $this.parent()
        , parentOffs = $parent.offset()
        , currOff = $this.offset()
        , data = $this.data("stickySB");
      if (!data) {
        data = {
            offs: {} // our parents offset
          , orig: { // cache for original css
                top: $this.css("top")
              , left: $this.css("left")
              , position: $this.css("position")
              , marginTop: $this.css("marginTop")
              , marginLeft: $this.css("marginLeft")
              , offset: $this.offset()
            }
        }
      }
      //go up the tree until we find an elem to position from
      while (parentOffs && "top" in parentOffs
        && $parent.css("position") == "static") {
        $parent = $parent.parent();
        parentOffs = $parent.offset();
      }
      if (parentOffs) { // found a postioned ancestor
        var padBtm = parseInt($parent.css("paddingBottom"));
        padBtm = isNaN(padBtm) ? 0 : padBtm;
        data.offs = parentOffs;
        data.offs.bottom = settings.constrain ?
          Math.abs(($parent.innerHeight() - padBtm) - $this.outerHeight()) :
          $(document).height();
      }
      else data.offs = { // went to far set to doc
          top: 0
        , left: 0
        , bottom: $(document).height()
      };
      $this.css({
          position: "absolute"
        , top: Math.floor(currOff.top - data.offs.top) + "px"
        , left: Math.floor(currOff.left - data.offs.left) + "px"
        , margin: 0
        , width: $this.width()
      }).data("stickySB", data);
    }
  }

  var reset = function (ev, $toReset) {
    var stickies = stickyboxes;
    if ($toReset) { // just resetting selected items
      stickies = [$toReset];
    }
    $.each(stickies, function(i, $sb) {
      var data = $sb.data("stickySB");
      if (data) {
        $sb.css({
            position: data.orig.position
          , marginTop: data.orig.marginTop
          , marginLeft: data.orig.marginLeft
          , left: data.orig.left
          , top: data.orig.top
        });
        if (!$toReset) { // just resetting
          setPosition($sb);
          moveIntoView();
        }
      }
    });
  }

  $window.bind("scroll", moveIntoView);
  $window.bind("resize", reset);

  $.fn.stickySidebar = function (method) {

    if (methods[method]) {
      return methods[method].apply(
          this
        , Array.prototype.slice.call(arguments, 1)
      );
    } else if (!method || typeof method == "object") {
      return methods.init.apply(this, arguments);
    }

  }

})(jQuery);

jQuery(document).ready(function($) {

	if( $.cookie('first') == null ) { 
	    $(".socialFooterWrap").addClass("show");
	    $(".socialFooterWrap").addClass("fixedPosition");
	 }
	$('.socialClose').click(function(){
		$.cookie( 'first', '1',  { expires: 28, path: '/' } );
		$(".socialFooterWrap").removeClass("show");
	}); 
	$('#footer').bind('inview', function (event, visible) {
	  if (visible == true) {
	    // element is now visible in the viewport
	    $(".socialFooterWrap").removeClass("fixedPosition");
	  } else {
	    // element has gone out of viewport
	    $(".socialFooterWrap").addClass("fixedPosition");
	  }
	});

});

There’s a lot of code right here, but let me explain. First, we have two plugins – a jQuery cookie plugin that makes it easy to work with cookies and a plugin that checks if element are in the viewport.

Then, within the document ready function, we do several things.

  1. Check if there is a cookie name socialFooter. If this cookie exists, that means that the visitor has clicked the hide button in the past. Therefore, we don’t want to show the social share footer.
  2. Second, we have a piece of code that will drop the socialFooter cookie if the close button is clicked.
  3. Lastly, we toggle between position fixed and position relative/station when #footer is in view. This is how we create the effect of the share bar ‘docking’ with the bottom when the footer is in view.

Conclusion

This code is not exactly trivial if you’ve never played around with jQuery. But, don’t be afraid since much of this is copy and paste. I did the searching for you :)

In this example, I left about 140px for your logo to be included in the social share footer. Be sure to include an image and update the CSS when you include this in your website.

Are you having issues or would like to suggest any features? Leave a comment below and let me know what’s on your mind.

Sign Up Now for Free Updates and Exclusive Content:

Learn how to write killer content, get more traffic, make money, and more by entering your email below:

Written by Eric Binnion

Eric Binnion is a computer science student at Midwestern State University. When Eric is not online, he is usually volunteering in his community or enjoying time with his family. You can find Eric on Twitter.

Comments