<h1>Reading progress bar</h1>
<p>This is a demo of a jQuery “sticky” reading progress bar which I recently developed for <a href="http://logikcull.com/white-papers/dont-get-hacked">Logikcull</a>. It hides when the viewport is too narrow, sticks to the top of the viewport on scroll, displays reading progress by section and also within each section. It can also be used for navigation.</p>
<p>The HTML for the progress bar is generated dynamically on pageload based on the elements used to define each section (in this case, H2 headers).</p>
<p>The JS could be neater, I'm sure...!</p>
<h2>Gouda</h2>
<p>Ad possit debitis sea, ea hinc discere dissentiet mel, persius posidonium sadipscing pri no. Vix fugit nusquam fierent in, cu probo congue ius. Cu regione viderer pro, quot veniam appetere et vix. Cum at nisl praesent. No nam nostro audire, no sit populo tibique, id ceteros appetere platonem pri. An graecis salutatus sed. His an dico efficiendi, novum eruditi at nec, vel ei affert mucius consequat.</p>
<p>Ex prompta constituam vis, an eos deleniti tractatos, nam alii aliquando temporibus eu. Duo ei nulla animal elaboraret. Meis adhuc id vix, ne nam nihil graecis antiopam. Vel ea eligendi perfecto repudiandae, no pri tation deterruisset. Ei unum nihil sea, tantas minimum invidunt vel an, nominavi delicata posidonium cum no. Porro adversarium cum eu, admodum lucilius nec at, eu magna definitionem has. Nam lorem movet tollit in.</p>
<p>Harum euismod assentior ei has, vidisse principes consetetur ne vel. No iriure eripuit quo, integre persequeris ex vis, ne has autem probatus. Elit copiosae efficiendi te sea, duo ad aeterno graecis, an populo assueverit sit. Vis an fabulas graecis expetenda, natum quaestio at vim. Cu sed nisl utroque vituperatoribus, ne vim stet ocurreret.</p>
<p>Nihil iracundia usu in, sit ex admodum liberavisse necessitatibus, menandri dissentiunt at his. Pri at saepe tamquam expetenda, cu eros mollis blandit eos, possit quaestio ut nec. Te expetenda evertitur vis, semper dictas eleifend cum at. Mea deserunt maiestatis percipitur ex, te sea habeo percipitur, qui ne harum tritani persequeris. Eos eu quod dolore. Ius erat illum eu, tractatos mediocrem vim ea.</p>
<h2>Brie</h2>
<p>Sea in singulis delicatissimi, alia prima ei cum. Sed in quas dissentias reprehendunt. Paulo semper corpora ne pri, vis no iriure luptatum. Alii error diceret ius te, dolorum complectitur definitionem vix ea. Fugit atqui debitis ad his. Perfecto delicata senserit id has. Tempor inimicus erroribus eam ex.</p>
<p>Pro falli eruditi sapientem ei, lucilius periculis ius ea. Eum veri meliore mentitum ex, ne erat graece per. In pro habeo simul vituperatoribus, pro minim noster utroque an. Partem delicatissimi cum et, no aeque nonumy urbanitas est. Sonet vivendum in nam. Nec porro ubique scripta in, veri saperet graecis vis cu, has cu dicat sanctus impedit. Mei in duis timeam invenire.</p>
<h2>Manchego</h2>
<p>Eos ex erant latine sanctus, at sea labore adolescens ullamcorper. Ut aeque veritus vis, no cum doming civibus adipiscing. Te mel eius quidam blandit, iudico nusquam pri ex.</p>
<p>Probo dicunt his eu, eum et eirmod labitur accommodare. Duo tempor euripidis ut, his nostrud persecuti in. Tantas oportere ullamcorper sed no, id paulo vitae voluptatibus sea. Quo te aliquid nominati signiferumque. Ad has graecis gubergren, cu iracundia voluptatibus pri.</p>
<p>Vero mediocrem salutatus ne mel. Ex prima justo volumus mei. Te sed nemore praesent interesset, eam evertitur similique comprehensam in. Ei modus minimum menandri mel, tollit perpetua eloquentiam qui ea. No modus iriure aliquip quo.</p>
<h2>Camembert</h2>
<p>Erroribus cotidieque ex quo, iisque habemus ex eos, vocent apeirian necessitatibus sit at. Sint partem invidunt te qui. His eros integre saperet at, vidit aliquip eos no. Qui ea veniam soleat periculis.</p>
<p>Has idque volutpat percipitur ea, his ne elitr sanctus detraxit. Cu eos assum dolorum recusabo, eu mei omnes suscipiantur, at eos timeam regione. Duo quas explicari cu, habemus deterruisset vix ne. An quo ferri dicam forensibus, eu populo labore corpora nec, facete commune ancillae ut pro. Nam iusto placerat te.</p>
<p>Antiopam mnesarchum temporibus cu nam, ei duo affert commune efficiendi. Usu inani efficiantur ex. Natum docendi tincidunt ex eum, qui ut diam veniam volutpat, ferri utinam munere eu vel. Ei cum liber perpetua gubergren.</p>
<h2>Edam</h2>
<p>Lorem ipsum dolor sit amet, in viderer periculis nam, delectus accusamus pro no. Sea nusquam partiendo consequat id, has tation temporibus consequuntur id. Fastidii liberavisse usu ad, prima civibus expetendis ex eum, est ex posse lucilius repudiare. Eu delenit sapientem molestiae nec, his ei cibo autem aliquando, impetus appellantur sea cu. Reque audiam ad his, case dignissim posidonium an duo, ex deleniti noluisse deterruisset usu.</p>
<p>Et impetus aliquip mel, per quod probo accumsan et. Ea solum pertinacia eloquentiam nec, et iisque sensibus scriptorem mei. Mazim laoreet repudiare ex nam, possit platonem te mei, his brute impetus adversarium ne. Usu vero autem inermis no, id wisi viris democritum vix.</p>
<p>Mandamus hendrerit scribentur eu vis, et nam aperiri volumus. Ut vix graecis legendos eleifend, ad quas mucius per. Justo nonumes offendit at mel, ut mea prompta recusabo neglegentur, at possim noluisse usu. Ad quidam aliquando vis, eam quidam ancillae pertinax eu, vis eu solum nonumy nostrud. Et cum saepe homero aeterno, graece inermis te qui, sea ut legimus urbanitas intellegat.</p>
<h2>Gorgonzola</h2>
<p>Pri antiopam volutpat te, an unum menandri quo, mei eu error comprehensam. Cu debitis scribentur sea, id errem regione percipit eos, omnium aliquando efficiendi his id. Mei et quot neglegentur, senserit indoctum his te. Commodo delectus eu usu, vel te atqui cetero nostrud. No nobis explicari cum, quo dico simul no.</p>
<p>Corpora euripidis te nam. At ius nihil indoctum sadipscing, qui at stet docendi constituto. Porro consequuntur an quo, et quodsi aliquando usu, an nec tamquam sensibus. Ea ius dictas quodsi cetero, natum praesent necessitatibus id vim, dissentiet ullamcorper ea sed. At mel menandri comprehensam, ad eos velit moderatius.</p>
<p>Prima dolorum id his, vix te volumus persecuti. Usu in solum integre, vocibus oporteat eu vix. Ei mel meis facete. Vix an copiosae electram. Te dicta laudem usu, vim mandamus referrentur ne.</p>
<p>Mel sensibus maiestatis dissentiunt te, cum cu atomorum inimicus, has indoctum mediocrem gloriatur ea. Eu modus interpretaris pro, ea duo epicuri salutatus principes. Sit cu everti bonorum torquatos. At nominati inimicus rationibus eum. No ius facer similique, ei pri alia eius vulputate. Mea ex mazim ludus scriptorem, sea lucilius constituam ei.</p>
<p>In cum munere habemus reformidans, vis iriure consequat an, vel te zril iisque. Error accusam duo ad, ei mel posse utamur omnesque. Sea tempor facilisi at, graeco conceptam eos cu. Ridens detraxit complectitur has ut, no atomorum oportere dignissim sit. Sed solum laoreet torquatos cu, per ea efficiendi comprehensam.</p>
<p>Per prompta nostrud an. Labitur iuvaret constituam nec ne, per meliore vituperata repudiandae ad. Ut per accusata oportere, cu delenit convenire contentiones eos. Ut usu elaboraret percipitur. Nam efficiendi disputando an. Eum et erat atqui omnesque, omnes appetere cu vel, et eos nusquam delicata.</p>
$background: #D6E1E5;
$subtle: #B6D1DA;
$h1: #DB5B33;
$h2: #4598B5;
$body: #6B7072;
$dark: #222;
*, *:before, *:after {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.clearfix:before,
.clearfix:after {
content: " ";
display: table;
}
.clearfix:after {
clear: both;
}
.clearfix {
*zoom: 1;
}
body {
background-color: $background;
color: $body;
font-size: 100%;
margin-bottom: 10em;
@media only screen and (min-width: 700px) { font-size: 110%; }
@media only screen and (min-width: 800px) { font-size: 120%; }
}
h1, h2, p {
padding: 0 50px;
line-height: 1.75;
@media only screen and (min-width: 800px) { padding: 0 100px; }
margin: 0 auto;
max-width: 1100px;
}
h1, h2 {
font-family: 'Open Sans';
font-weight: 300;
font-size: 250%;
line-height: 1.5;
}
h1 {
margin-top: 1.5em;
margin-bottom: 1.5em;
color: $h1;
}
h2 {
margin-top: 2em;
margin-botttom: 1em;
color: $h2;
}
p {
font-family: 'Merriweather';
font-weight: 300;
font-size: 100%;
margin-top: 2em;
margin-bottom: 2em;
}
a {
color: darken($body, 25%);
text-decoration: underline;
transition: color 100ms ease-in;
&:hover, &:focus {
color: $h1;
}
}
.progressbar {
display: none;
@media only screen and (min-width: 650px) { display: block; }
margin: 4em 0;
.shim {
display: none;
width: 100%;
}
.holder {
position: relative;
font-size: 85%;
@media only screen and (min-width: 750px) { font-size: 90%; }
@media only screen and (min-width: 900px) { font-size: 95%; }
padding: 1.8em 0 0 0;
background-color: $background;
box-shadow: 0 .5em 1.5em $background;
.bar {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 2px;
background-color: $subtle;
.indicator {
position: absolute;
top: 0;
left: 0;
height: 100%;
background-color: $h2;
}
}
.labels {
max-width: 1280px;
margin: 0 auto;
padding: 0 2em;
text-align: center;
i {
display: block;
position: relative;
float: left;
cursor: pointer;
&::before {
position: absolute;
bottom: 0;
left: 50%;
display: block;
content: '';
width: .9em;
height: .9em;
border-radius: 50%;
border: solid 3px $subtle;
background-color: $background;
transform: translateX(-50%) translateY(50%);
transition:
border-color 100ms ease-in,
background-color 150ms ease-in;
}
&::after {
display: block;
content: attr(data-label);
position: relative;
top: 0;
padding-bottom: 1.8em;
font-family: 'Open Sans';
font-weight: 400;
color: $h2;
transition:
color 150ms ease-in,
top 100ms ease-out;
}
&:hover, &:focus {
&::before {
background-color: $subtle;
}
&::after {
top: -.2em;
}
}
&.read {
&::before {
border-color: $h2;
}
&:hover, &:focus {
&::before {
background-color: $h2;
}
}
}
&.reading {
&::after {
color: $dark;
}
&:hover, &:focus {
&::after {
top: 0;
}
}
}
}
}
}
&.fixed {
.holder {
position: fixed;
top: 0;
left: 0;
width: 100%;
z-index: 1;
}
.shim {
display: block;
}
}
}
View Compiled
jQuery.extend(jQuery.easing, {
easeInOutCubic: function (x, t, b, c, d) {
if ((t/=d/2) < 1) return c/2*t*t*t + b;
return c/2*((t-=2)*t*t + 2) + b;
}
});
$(document).ready(function(){
// set up and create progress bar in DOM
$('h2').eq(0).before('<div class="progressbar"></div>');
var container = $('.progressbar');
container.append('<div class="shim"></div>');
var shim = $('.progressbar .shim');
container.append('<div class="holder clearfix"></div>');
var holder = $('.progressbar .holder');
holder.append('<div class="bar"></div>');
var bar = $('.progressbar .bar');
bar.append('<div class="indicator"></div>');
var indicator = $('.progressbar .indicator');
holder.append('<div class="labels"></div>');
var labels = $('.progressbar .labels');
$('h2').each(function(){
var code = '<i data-label="'+$(this).text()+'"></i>';
labels.append(code);
});
var points = labels.find('i');
points.css('width', 100/$('h2').length+'%');
// match height of shim
// stop layout jumping when progress bar fixes to / unfixes
// from top of viewport
function setShimHeight(){
shim.css('height', container.height()+'px');
}
setShimHeight();
$(window).resize(function(){ setShimHeight(); });
// position indicator bar so it starts at first dot
function setIndicatorX(){
var point = points.eq(0);
var xpos = point.offset().left + (point.width() / 2);
indicator.css('left', xpos+'px');
}
setIndicatorX();
$(window).resize(function(){ setIndicatorX(); });
// fix/unfix progress bar to top of viewport
function fixPosition(){
if(container.is(':visible')) {
if(!container.hasClass('fixed')) {
if(holder.offset().top <= $(window).scrollTop()) {
container.addClass('fixed');
}
}
else {
if(shim.offset().top > $(window).scrollTop()) {
container.removeClass('fixed');
}
}
}
}
fixPosition();
$(window).scroll(function(){ fixPosition() });
$(window).resize(function(){ fixPosition(); });
// set trigger point
// i.e. how far down viewport is the "eye line"
var triggerPoint = 0;
function setTriggerPoint(){
triggerPoint = $(window).height() * .18;
}
setTriggerPoint();
$(window).resize(function(){ setTriggerPoint(); });
// update progress bar
function setPosition(){
if(container.is(':visible')) {
var section = false;
var sectionIndex = 0;
var currentPosition = $(window).scrollTop() + triggerPoint;
// dots
// if before first section
if(currentPosition < $('h2').eq(0).offset().top) {
points.removeClass('reading read');
section = -1;
}
// if after first section
else {
$('h2').each(function(){
var sectionTop = $(this).offset().top;
if(currentPosition >= sectionTop) {
points.removeClass('reading');
points.eq(sectionIndex).addClass('reading');
points.eq(sectionIndex).addClass('read');
section = sectionIndex;
}
else {
points.eq(sectionIndex).removeClass('read');
}
sectionIndex++;
});
}
// bar
var barWidth = 0;
// if before start
if(section == -1) {
var point = points.eq(0);
barWidth = point.offset().left + (point.width() / 2);
}
// if after end
else if(section >= (points.length - 1)) {
var point = points.eq((points.length - 1));
barWidth = point.offset().left + (point.width() / 2);
}
// if within document
else {
var startPoint = points.eq(section);
var startPointX = startPoint.offset().left;
var startPointWidth = startPoint.width();
var startSection = $('h2').eq(section);
var endSection = $('h2').eq(section+1);
var startSectionY = startSection.offset().top;
var endSectionY = endSection.offset().top;
var sectionLength = endSectionY - startSectionY;
var scrollY = currentPosition - startSectionY;
var sectionProgress = scrollY / sectionLength;
barWidth = startPointX + (startPointWidth / 2) + (startPointWidth * sectionProgress);
}
barWidth -= indicator.offset().left;
indicator.css('width', barWidth+'px');
}
}
setPosition();
$(window).scroll(function(){ setPosition(); });
$(window).resize(function(){ setPosition(); });
// on click, scroll to target section
points.click(function(){
var sectionIndex = points.index($(this));
var targetY = $('h2').eq(sectionIndex).offset().top - (triggerPoint * .92);
$('html, body').animate({scrollTop:targetY}, 600, 'easeInOutCubic');
});
});