Recently we released our well known Joomla template Purity III in its latest avatar. One of the features in its documentation is that of a progressive sidebar navigation highlighting the progress of scrolling down each section of the page in question. We received few requests regarding this feature and here we are with the details on how you can have one for your own site.
Let Roll Your Sleeves Up
First, we will create a HTML document with navigation using Bootstrap markup, you can either use Bootstrap 2 or 3:
<div class="doc-container row">
<div class="doc-sidebar col-md-3">
<ul class="doc-nav nav nav-list">
<li><a href="#section-1">Section 1</a></li>
<li><a href="#section-2">Section 2</a></li>
<li><a href="#section-3">Section 3</a></li>
</ul>
</div>
<div class="doc-content col-md-9">
<section id="section-1">
<h3>Section 1</h3>
<p>Section 1 content here.</p>
...
</section>
<section id="section-2">
<h3>Section 2</h3>
<p>Section 2 content here.</p>
...
</section>
<section id="section-3">
<h3>Section 3</h3>
<p>Section 3 content here.</p>
...
</section>
</div>
</div>
Then, we add some JavaScript
If you are not familiar with adding JavaScript to T3 framework based templates, please refer to this documentation. The JavaScript that we add to the site will:
- Make the navigation stick to the top of the screen when you scroll the documentation,
- Activate the document navigation item according to the reading section,
- Update reading progress bar.
(function($){
$(document).ready(function(){
var $window = $(window),
$body = $(document.body),
$doc = $('.doc-container'),
$nav = $doc.find ('.doc-nav');
// make the document navigation affix when scroll
$nav.affix({
offset: {
top: function () {
return 200; // replace with your top position to start affix
},
bottom: function () {
return 300; // replace your bottom position to release the affix
}
}
});
// change navigation active according to scroll
$body.scrollspy({
target: '.doc-sidebar'
});
// add progress bar for navigation
$nav.find ('a').before ($('<span class="docs-progress-bar" />'));
$nav.on ('activate activate.bs.scrollspy', function () {
$body.scrollspy("refresh");
var $active = $nav.find('li.active');
$active.prevAll().find('.docs-progress-bar').css ('width', '100%');
$active.nextAll().find('.docs-progress-bar').css ('width', '0%');
});
$window.on ('scroll', function (event) {
if (this.timeout) {
clearTimeout(this.timeout);
}
this.timeout = setTimeout (function () {
var $active = $nav.find('li.active'),
$progress = $active.find('.docs-progress-bar'),
$scrollspy = $body.data('bs.scrollspy'),
scrollTop = $scrollspy.$scrollElement.scrollTop() + $scrollspy.options.offset,
scrollHeight = $scrollspy.$scrollElement[0].scrollHeight || $scrollspy.$body[0].scrollHeight,
maxScroll = scrollHeight - $scrollspy.$scrollElement.height(),
offsets = $scrollspy.offsets,
targets = $scrollspy.targets,
activeTarget = $scrollspy.activeTarget,
i;
if (scrollTop >= maxScroll) {
$progress.css ('width', '100%');
return ;
}
if (activeTarget && scrollTop <= offsets[0]) {
$progress.css ('width', '0%');
return ;
}
for (i = offsets.length; i--;) {
if (scrollTop >= offsets[i]
&& (!offsets[i + 1] || scrollTop <= offsets[i + 1])) {
var p1 = offsets[i],
p2 = scrollTop,
p3 = !offsets[i + 1] ? maxScroll : offsets[i + 1],
p = (p2-p1)/(p3-p1)*100;
$progress.css ('width', (p < 2 ? 2 : p) + '%');
return ;
}
}
}, 100);
});
});
})(jQuery);
And finish with some simple styling
.doc-nav.affix {
top: 60px;
}
.doc-nav.affix-bottom {
position: relative;
}
.doc-nav > li {
background: none repeat scroll 0 0 #F6F8FA;
margin-bottom: 1px;
width: 200px;
}
.doc-nav > li.active > a {
font-weight: bold;
}
.doc-nav .docs-progress-bar {
background: none repeat scroll 0 0 #DEE4EC;
bottom: 0;
left: 0;
position: absolute;
top: 0;
}
Give it a try and let us know if it worked for you. We had love to hear from you on this interesting feature.