Friday 25 April 2014

jQuery accordion plugin you can style how you like

The jQuery accordion plugins I have come across also come with some complex CSS. They rely on the CSS to hide elements and you have to dig your way through complex CSS elements in order to adapt the look for your own purposes. On of the the worst culprits for this is the one which comes with the jQuery UI library.
So I devised my own plugin which operates without any CSS, is easy to style, is lightweight, does not require anchors to open items, and provides flexibility to the item headers.
First the HTML.
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Accordion</title>  
    <style>
    body
    {
      font: 90%/1.618 sans-serif;
      padding-top:2em;
    }
    </style>
    <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
      <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
      <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
    <![endif]-->
  </head>
  <body>
    <div id="micksaccordion">
      <section>
        <header>
          <h1>Header 1</h1>
        </header>
        <article>
          <p>This is from article 1</p>
        </article>
      </section>
      <section>
        <header>
          <h2>Header 2</h2>
        </header>
        <article>
          <p>This is from article 2</p>
        </article>
      </section>
      <section>
        <header>
          <h3>Header 3</h3>
        </header>
        <article>
          <p>This is from article 3</p>
        </article>
      </section>
      <section>
        <header>
          <h4>Header 4</h4>
        </header>
        <article>
          <p>This is from article 4</p>
        </article>
      </section>
    </div><!-- micksaccordion -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
    <script src="accordion.plugin.js"></script>
    <script>
    (function()
    {
      /* Easy version
      $('#micksaccordion').accordion();
      */
      $('#micksaccordion').accordion(
      {
        /* 'first', 'last', false */
        show:'last'
      });
    })();
    </script>
  </body>
</html>

And now the jQuery plugin:
(function($)
{
  $.fn.extend(
  {
    accordion:function(options)
    {
      var defaults =
      {
          show:false
      }

      var options = $.extend(defaults, options);

      return this.each(function()
      {
        var o = options;              

        var articles = $(this).find('article');

        var firstSection = $(this).children().first();      
        var firstArticle = firstSection.find('article');

        var lastSection = $(this).children().last();      
        var lastArticle = lastSection.find('article');

        articles.hide();

        if(o.show == 'first')
        {
          firstArticle.show();
        }
       
        if(o.show == 'last')
        {
          lastArticle.show();
        }

        var sections = $(this).find('section');

        $(sections).on('click','header', function()
        {
          thesection = $(this).parent();
          thearticle = thesection.find('article');        
          if (thearticle.css('paddingTop') == '0px') thearticle.css({paddingTop: '1px'});
          if (thearticle.css('paddingBottom') == '0px') thearticle.css({paddingBottom: '1px'});        
          thearticle.slideToggle('slow');
        });

      });
    }
  });
})(jQuery);

Enjoy!

Monday 14 April 2014

HTML Entities jQuery plugin for the code tag

Try presenting HTML in a web page. It's a pain in the neck. Even in between the code tag, you have to create HTML Entities for greater than and less than symbols etc.
Not wanting to go through that pain ever again, I have created a small JavaScript plugin which helps.
First, here is the HTML page important differences in red:
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Convert to HTML Entities</title>  
  </head>
  <body>
    <code>Hello, <h1>world!</h1></code>
    <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
    <script src="htmlentities.plugin.js"></script>
    <script>
    (function()
    {
      $('code').htmlentities();
    })();
    </script>
  </body>
</html>
Now, htmlentities.plugin.js:
(function($)
{
  $.fn.htmlentities = function()
  {
 
    beforeString = $(this).html();  
    afterString = beforeString.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/'/g, '&#39;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
    $(this).html(afterString);
  }
})(jQuery);

Happy coding!

Wednesday 9 April 2014

Twitter Bootstrap Progress Bar example using jQuery and PHP

So you'd like to see Twitter Bootstrap Progress Bar actually work. Not too many examples out there are there. For this, after you've downloaded Twitter Bootstrap, you will need 3 more files:

  • Your demo file, index.html
  • Your post handler, posthandler.php
  • Your empty data file, datafile.dat which is writeable

So let's start with index.html. Get the basic template from http://getbootstrap.com/getting-started/
and make the following additions (in red). This file:

  • Creates a progress bar
  • Kicks off the post handler using the submit button
  • Checks the value contained in the data file
  • Uses the data gathered to increase the width of the progress bar

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap 101 Template</title>

    <!-- Bootstrap -->
    <link href="css/bootstrap.min.css" rel="stylesheet">  
    <style>
    body
    {
      padding-top:2em;
    }  
    </style>
    <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
      <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
      <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
    <![endif]-->
  </head>
  <body>
    <div class="row">
      <div class="container">
        <div class="col-md-12">
          <form role="form">
            <button type="submit" class="btn btn-default">Submit</button>
          </form><br />
          <div class="progress progress-striped active">
            <div class="progress-bar" role="progressbar" aria-valuenow="45" aria-valuemin="0" aria-valuemax="100" style="width:0%">
            </div>
          </div>
        </div>
      </div>
    </div>    
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
    <!-- Include all compiled plugins (below), or include individual files as needed -->
    <script src="js/bootstrap.min.js"></script>
    <script>
    (function()
    {
      $('form').submit(function()
      {
        $.post('posthandler.php', function(data1)
        {
          console.log(data1)
        });

        var refreshId = setInterval(function()
        {
          $.get('datafile.dat', function(data2)
          {
            $('.progress-bar').css('width',data2+'%');
            if(data2 >= 100)
            {
              console.log('finished');
              $('.progress').removeClass('active');
              $('.progress').removeClass('progress-striped');
              $('.progress-bar').text('Complete');
              clearInterval(refreshId);
            }
          });
        }, 100);
        return false;
      });      
    })();
    </script>
  </body>
</html>

Now for the posthandler.php
This script:

  • Clears the data file
  • Every so often, updates the data file contents

<?php
file_put_contents('datafile.dat', '');
for($i = 1; $i <= 100; $i++)
{
usleep(200000);
file_put_contents('datafile.dat', $i);
}
?>

Enjoy!

Monday 7 April 2014

Simple pagination plugin for Twitter Bootstrap

Let's say you have content in a Twitter Bootstrap web page and you want to paginate through it. If you've ever tried looking for examples on doing this, you would know that very few of them contain content. Or in other words the examples contain all the numbers at the bottom but none of the content at the top and how to paginate through it.
I then found plugins which looked very clever, but didn't actually work.
So I have created a jQuery plugin (which works) and an example of how it could be used.
First the HTML page which I took form getbootstrap.com, then added the necessary code for the pagination which I highlight below.
Actually, it works for any site which uses jQuery. Another strength to this simplified method is that you can name the content IDs whatever you like and the plugin with still pick them up as labels.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap 101 Template</title>

    <!-- Bootstrap -->
    <link href="css/bootstrap.min.css" rel="stylesheet">
    <style>
    body
    {
      padding-top:2em;
    }
    #mickspagination #navigation a
    {
      background:#e5e5e5;
      margin:0.1em;
      padding:0.5em;
    }
    </style>
    <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
      <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
      <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
    <![endif]-->
  </head>
  <body>
    <div class="row">
      <div class="container">
        <div class="col-md-12">
          <div id="mickspagination">
            <div id="content">
              <div id="1" class="active">
                <p>Content for 1</p>
              </div>
              <div id="2">
                <p>Content for 2</p>
              </div>
              <div id="3">
                <p>Content for 3</p>
              </div>
              <div id="4">
                <p>Content for 4</p>
              </div>
            </div>
            <div id="navigation"></div>
          </div>
        </div>
      </div>
    </div>  
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
    <!-- Include all compiled plugins (below), or include individual files as needed -->
    <script src="js/bootstrap.min.js"></script>
    <script src="pagination.plugin.js"></script>
    <script>
    (function()
    {
      $('#mickspagination').pagination();
    })();
    </script>
  </body>
</html>

Now the jQuery plugin contained in pagination.plugin.js

(function($)
{
    $.fn.extend(
    {
        pagination:function(options)
        {
            var defaults = 
            {
                showarrows:false,                
                classesext:null,
                alignment:'center',
                navigationtag:'#navigation'
            }

            var options = $.extend(defaults, options);

            return this.each(function() 
            {
                var o = options;                

                var content = $(this).find('#content');
                var contentDivs = content.find(' > div');
                var firstDiv = content.children().first();
                var lastDiv = content.children().last();    
                var leftDiv = content.children().first();    
                var rightDiv = content.children().eq(1);
                var leftArrow, rightArrow;
                var activeDiv  = content.find('.active');
                var navigation = null;
                var contentArr = Array();

                contentDivs.not('.active').hide();

                /* Provide option for navigation outside the pagination */
                if(o.navigationtag == '#navigation')
                {
                  navigation = $(this).find('#navigation');
                }
                else
                {

                  navigation = $(document).find(o.navigationtag);
                }

                /* Show the first arrows */
                if(o.showarrows == true)
                {
                  link = $(this).arrowlink(firstDiv.attr('id').replace(/ /g,''),'«', o.classesext);
                  navigation.append(link);
                  link = $(this).arrowlink(leftDiv.attr('id'),'<', o.classesext);
                  navigation.append(link);                  
                  leftArrow = navigation.find("a:contains('<')"); 
                }    

                /* Show all the pagination navigation */
                contentDivs.each(function()
                {
                  encodedID = $(this).attr('id').replace(/ /g,'');
                  $(this).attr('title', $(this).attr('id'));                  
                  $(this).attr('id', encodedID);                  
                  contentArr.push($(this).attr('id'));
                  link = $(this).buildlink(o.classesext);
                  navigation.append(link);
                });

                /* Show the last arrows */
                if(o.showarrows == true)
                {
                  link = $(this).arrowlink(rightDiv.attr('id'),'>', o.classesext);
                  navigation.append(link);
                  link = $(this).arrowlink(lastDiv.attr('id'),'»', o.classesext);
                  navigation.append(link);                  
                  rightArrow = navigation.find("a:contains('>')");                  
                }

                /* Centre the pagination navigation. May wish to set this as an option */
                navigation.css('text-align',o.alignment);

                /* Handle what happens when someone clicks on a navigation link */
                $(navigation).on('click', 'a', function()
                {
                  /* Make the currently clicked ID the active one */
                  activeDiv  = content.find('.active');
                  activeDiv.removeClass('active');
                  currentID = $(this).attr('href');
                  newActiveDiv = content.find(' > div'+currentID);
                  newActiveDiv.addClass('active');

                  /* Change what the arrows now point at */
                  if(o.showarrows == true)
                  {
                    leftArrow.updateleftlink(newActiveDiv, contentArr);
                    rightArrow.updaterightlink(newActiveDiv, contentArr);
                  }

                  /* Show the newly selected content */
                  contentDivs.not('.active').hide();
                  newActiveDiv.show();      
                });
            });
        }
    }); /* End of pagination plugin. Other function may go below this */

    /* Create the pagination navigation links */    
    $.fn.buildlink = function(classesext)
    {
        var ct = $.trim(classesext);
        link = '<a href="#';
        link += $(this).attr('id')+'"';        
        if(classesext !== null)
        {
            link += ' class="'+ct+'"';
        }
        link += '>';        
        link += $(this).attr('title');
        link += '</a>';
        return link;
    };

    $.fn.arrowlink = function(dynamicloc, linkchar, classesext)
    {
        var ct = $.trim(classesext);
        link = '<a href="#';        
        link += dynamicloc+'"';        
        link += ' class="pagarrow';
        if(classesext !== null)
        {
            link += ' '+ct;
        }        
        link += '">';
        link += linkchar;
        link += '</a>';
        return link;
    };

    /* Update the first arrow link */
    $.fn.updateleftlink = function(ad,cr)
    {
        var ov = 0;
        var hrefstring = '#';
        var ap = cr.indexOf(ad.attr('id'));
        if((ap-1) <= 0)
        {
            ov = 0;
        }
        else
        {
            ov = (ap-1);
        }
        hrefstring += cr[ov];
        $(this).attr('href',hrefstring);    
    };

    $.fn.updaterightlink = function(ad,cr)
    {
        var ov = 0;
        var hrefstring = '#';
        var ap = cr.indexOf(ad.attr('id'));    
        if((ap+1) >= cr.length)
        {
            ov = cr.length-1;      
        }
        else
        {
            ov = (ap+1);
        }
        hrefstring += cr[ov];    
        $(this).attr('href',hrefstring);    
    };

})(jQuery);

Monday 17 February 2014

Twitter Bootstrap single page website without the need for yet another plugin

The code below creates a page using Twitter Bootstrap. It contains one of those fashionable single page navigations. I needed to create one for a site I was developing for a client recently. When I started to look into it all the examples seemed to use a plugin. For performance purposes, I didn't want to use another plugin. Especially since jQuery already comes with the 'animate' class. See code below for solution.

<!DOCTYPE html>
<html>
  <head>
    <title>Single page website</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <!-- Bootstrap -->
    <link href="css/bootstrap.min.css" rel="stylesheet">
    <style>  
    #about, #contact
    {
      padding-top:4em;
    }
    #contact
    {
      margin-bottom:40em;
    }
    </style>
    <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
      <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
      <script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
    <![endif]-->
  </head>
  <body>
    <div class="navbar navbar-default navbar-fixed-top" role="navigation">
      <div class="container">
        <div class="navbar-header">
          <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>        
        </div>
        <div class="navbar-collapse collapse">        
          <ul class="nav navbar-nav navbar-right">
            <li><a href="#home">Home</a></li>
            <li ><a href="#about">About</a></li>
            <li><a href="#contact">Contact</a></li>
          </ul>
        </div><!-- nav-collapse -->
      </div><!-- container -->
    </div><!-- navbar -->

    <div id="home">
      <div class="row">
        <div class="container">
          <article class="col-md-4">
            <h2>Header 2</h2>
            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quid ad utilitatem tantae pecuniae? Age, inquies, ista parva sunt. Quibus ego vehementer assentior. Quae cum essent dicta, discessimus.</p>    
            <p>Invidiosum nomen est, infame, suspectum. Duo Reges: constructio interrete. Quibus ego vehementer assentior. Non igitur bene.</p>
          </article>
          <article class="col-md-4">
            <h2>Header 2</h2>
            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quid ad utilitatem tantae pecuniae? Age, inquies, ista parva sunt. Quibus ego vehementer assentior. Quae cum essent dicta, discessimus.</p>    
            <p>Invidiosum nomen est, infame, suspectum. Duo Reges: constructio interrete. Quibus ego vehementer assentior. Non igitur bene.</p>
          </article>
          <article class="col-md-4">
            <h2>Header 2</h2>
            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quid ad utilitatem tantae pecuniae? Age, inquies, ista parva sunt. Quibus ego vehementer assentior. Quae cum essent dicta, discessimus.</p>    
            <p>Invidiosum nomen est, infame, suspectum. Duo Reges: constructio interrete. Quibus ego vehementer assentior. Non igitur bene.</p>
          </article>
        </div><!-- container -->
      </div><!-- row -->
    </div><!-- section -->

    <div id="about">
      <div class="row">
        <div class="container">
          <figure class="col-md-4">    
            <img src="http://lorempixel.com/400/200/sports" alt="sports" class="img-responsive" />
            <figcaption>Invidiosum nomen est, infame, suspectum. Duo Reges: constructio interrete. Quibus ego vehementer assentior. Non igitur bene.</figcaption>
          </figure>
          <figure class="col-md-4">    
            <img src="http://lorempixel.com/400/200/sports" alt="sports" class="img-responsive" />
            <figcaption>Invidiosum nomen est, infame, suspectum. Duo Reges: constructio interrete. Quibus ego vehementer assentior. Non igitur bene.</figcaption>
          </figure>
          <figure class="col-md-4">    
            <img src="http://lorempixel.com/400/200/sports" alt="sports" class="img-responsive" />
            <figcaption>Invidiosum nomen est, infame, suspectum. Duo Reges: constructio interrete. Quibus ego vehementer assentior. Non igitur bene.</figcaption>
          </figure>
        </div><!-- container -->
      </div><!-- row -->
    </div><!-- section -->

    <div id="contact">
      <div class="row">
        <div class="container">
          <article class="col-md-6">
            <h2>Header 2</h2>
            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quid ad utilitatem tantae pecuniae? Age, inquies, ista parva sunt. Quibus ego vehementer assentior. Quae cum essent dicta, discessimus.</p>    
            <p>Invidiosum nomen est, infame, suspectum. Duo Reges: constructio interrete. Quibus ego vehementer assentior. Non igitur bene.</p>
          </article>
          <aside class="col-md-6">
            <h2>Header 2</h2>
            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quid ad utilitatem tantae pecuniae? Age, inquies, ista parva sunt. Quibus ego vehementer assentior. Quae cum essent dicta, discessimus.</p>    
            <p>Invidiosum nomen est, infame, suspectum. Duo Reges: constructio interrete. Quibus ego vehementer assentior. Non igitur bene.</p>
          </aside>
        </div><!-- container -->
      </div><!-- row -->
    </div><!-- section -->
    <footer class="navbar-default navbar-fixed-bottom">
      <div class="container">
        <a href="#home">Back to top</a>
      </div>
    </footer>
    <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
    <script src="https://code.jquery.com/jquery.js"></script>
    <!-- Include all compiled plugins (below), or include individual files as needed -->
    <script src="js/bootstrap.min.js"></script>  
    <script>
    (function()
    {
      $('.nav a, footer a').click(function()
      {
        var target = $(this).attr('href');
        var targetLoc = $(target).offset().top;      
        $('html, body').stop().animate(
        {
          scrollTop:targetLoc,
          easing:'swing'
        }, 1000);
        return false;
      });
    })();
    </script>
  </body>
</html>

Twitter Bootstrap CSS snippets

When creating a website using Twitter Bootstrap you often find yourself using the same CSS code snippets. Below are some of mine.

/*Standard bootstrap colour palette*/
.gray-darker
{
  background:#222222;
  color:white;
}
.gray-dark
{
  background:#333333;
  color:white;
}
.gray
{
  background:#555555;
  color:white;
}
.gray-light
{
  background:#999999;
  color:white;
}
.gray-lighter
{
  background:#EEEEEE;
  color:#222222;
}
.brand-primary
{
  background:#428BCA;
  color:white;
}
.brand-success
{
  background:#5CB85C;
  color:white;
}
.brand-info
{
  background:#5BC0DE;
  color:white;
}
.brand-warning
{
  background:#F0AD4E;
  color:white;
}
.brand-danger
{
  background:#D9534F;
  color:white;
}

/* Remove colours of navigation bar */
.navbar-default, .navbar-inverse
{
        background-image:none;
background-color:white;
border-color:white;
box-shadow:none;
}

/* Set default colour for inactive navigation items in inverse bar */
.navbar-inverse .navbar-nav > li > a
{
  background:#222222;
  color:white;
}

/* Set hover colour for inactive navigation items in inverse bar */
.navbar-inverse .navbar-nav > li > a:hover
{
  background:#333333;
  color:white;
}

/* Set default colour for active navigation items in inverse bar */
.navbar-inverse .navbar-nav > li.active > a
{
  background:#555555;
  color:white;
}

/* Set hover colour for active navigation items in inverse bar */
.navbar-inverse .navbar-nav > li.active > a:hover
{
  background:#999999;
  color:white;
}

/* Set default colour for inactive navigation items in default bar */
.navbar-default .navbar-nav > li > a
{
  background:#222222;
  color:white;
}

/* Set hover colour for inactive navigation items in default bar */
.navbar-default .navbar-nav > li > a:hover
{
  background:#333333;
  color:white;
}

/* Set default colour for active navigation items in default bar */
.navbar-default .navbar-nav > li.active > a
{
  background:#555555;
  color:white;
}

/* Set hover colour for active navigation items in default bar */
.navbar-default .navbar-nav > li.active > a:hover
{
  background:#999999;
  color:white;
}

/* Align figure text to centre */
figure
{
  text-align:center;
}

/* Align figure images to centre */
figure img
{
  margin:0 auto;
}

/* Change size and colour of glyph icons */
span.glyphicon
{
  font-size:6em;
  color:#222222;
}

/* Make the carousel images centre and take up the whole carousel */
.carousel > .carousel-inner > .item > img
{
  margin:0 auto;
  width:100%;
}

/* Lift the arrows a little higher to vertical middle and dim */
.carousel-control > .icon-prev, .carousel-control > .icon-next, .carousel-control > .glyphicon-chevron-left, .carousel-control > .glyphicon-chevron-right
{
  top:40%;
  opacity:0.5;
}

/* Remove the awful background gradient */
.carousel-control.right, .carousel-control.left
{
  background-image:none;
}

/* Take the right arrow closer to the edge */
.carousel-control .icon-next, .carousel-control .glyphicon-chevron-right
{
  right:20%;
}

/* Take the left arrow closer to the edge */
.carousel-control .icon-prev, .carousel-control .glyphicon-chevron-left
{
  left:20%;
}

Monday 27 January 2014

Twitter Bootstrap sticky footer (without the pain)

Strangely enough, the sticky footer examples which I've seen for Twitter Bootstrap 3 seem to ignore the fact that there is now a class which takes all the pain away. It's called 'navbar-fixed-bottom', and it is usually used to create a bottom up navigation bar. However, if you add it to your footer code, it works better than any other sticky footer examples. Here's how it works:
Create a footer thus:
<footer class="navbar-default navbar-fixed-bottom">
      <div class="container">
        <p>Hello world!</p>
      </div>
</footer>
Then style it using CSS thus:
footer.navbar-default.navbar-fixed-bottom
 {
      background:red;
      color:white;
      padding:1em 0;
 }
 footer.navbar-default.navbar-fixed-bottom p
 {
      margin:0;
 }
Easy! Sticky footers all the way for me now. They're really useful for mobile web apps.