/* * jquery one page nav plugin * http://github.com/davist11/jquery-one-page-nav * * copyright (c) 2010 trevor davis (http://trevordavis.net) * dual licensed under the mit and gpl licenses. * uses the same license as jquery, see: * http://jquery.org/license * * @version 3.0.0 * * example usage: * $('#nav').onepagenav({ * currentclass: 'current', * changehash: false, * scrollspeed: 750 * }); */ ;(function($, window, document, undefined){ // our plugin constructor var onepagenav = function(elem, options){ this.elem = elem; this.$elem = $(elem); this.options = options; this.metadata = this.$elem.data('plugin-options'); this.$win = $(window); this.sections = {}; this.didscroll = false; this.$doc = $(document); this.docheight = this.$doc.height(); }; // the plugin prototype onepagenav.prototype = { defaults: { navitems: 'a', currentclass: 'current', changehash: false, easing: 'swing', filter: '', scrollspeed: 750, scrollthreshold: 0.5, begin: false, end: false, scrollchange: false }, init: function() { // introduce defaults that can be extended either // globally or using an object literal. this.config = $.extend({}, this.defaults, this.options, this.metadata); this.$nav = this.$elem.find(this.config.navitems); //filter any links out of the nav if(this.config.filter !== '') { this.$nav = this.$nav.filter(this.config.filter); } //handle clicks on the nav this.$nav.on('click.onepagenav', $.proxy(this.handleclick, this)); //get the section positions this.getpositions(); //handle scroll changes this.bindinterval(); //update the positions on resize too this.$win.on('resize.onepagenav', $.proxy(this.getpositions, this)); return this; }, adjustnav: function(self, $parent) { self.$elem.find('.' + self.config.currentclass).removeclass(self.config.currentclass); $parent.addclass(self.config.currentclass); }, bindinterval: function() { var self = this; var docheight; self.$win.on('scroll.onepagenav', function() { self.didscroll = true; }); self.t = setinterval(function() { docheight = self.$doc.height(); //if it was scrolled if(self.didscroll) { self.didscroll = false; self.scrollchange(); } //if the document height changes if(docheight !== self.docheight) { self.docheight = docheight; self.getpositions(); } }, 250); }, gethash: function($link) { return $link.attr('href').split('#')[1]; }, getpositions: function() { var self = this; var linkhref; var toppos; var $target; self.$nav.each(function() { linkhref = self.gethash($(this)); $target = $('#' + linkhref); if($target.length) { toppos = $target.offset().top; self.sections[linkhref] = math.round(toppos); } }); }, getsection: function(windowpos) { var returnvalue = null; var windowheight = math.round(this.$win.height() * this.config.scrollthreshold); for(var section in this.sections) { if((this.sections[section] - windowheight) < windowpos) { returnvalue = section; } } return returnvalue; }, handleclick: function(e) { var self = this; var $link = $(e.currenttarget); var $parent = $link.parent(); var newloc = '#' + self.gethash($link); if(!$parent.hasclass(self.config.currentclass)) { //start callback if(self.config.begin) { self.config.begin(); } //change the highlighted nav item self.adjustnav(self, $parent); //removing the auto-adjust on scroll self.unbindinterval(); //scroll to the correct position self.scrollto(newloc, function() { //do we need to change the hash? if(self.config.changehash) { window.location.hash = newloc; } //add the auto-adjust on scroll back in self.bindinterval(); //end callback if(self.config.end) { self.config.end(); } }); } e.preventdefault(); }, scrollchange: function() { var windowtop = this.$win.scrolltop(); var position = this.getsection(windowtop); var $parent; //if the position is set if(position !== null) { $parent = this.$elem.find('a[href$="#' + position + '"]').parent(); //if it's not already the current section if(!$parent.hasclass(this.config.currentclass)) { //change the highlighted nav item this.adjustnav(this, $parent); //if there is a scrollchange callback if(this.config.scrollchange) { this.config.scrollchange($parent); } } } }, scrollto: function(target, callback) { var offset = $(target).offset().top; $('html, body').animate({ scrolltop: offset }, this.config.scrollspeed, this.config.easing, callback); }, unbindinterval: function() { clearinterval(this.t); this.$win.unbind('scroll.onepagenav'); } }; onepagenav.defaults = onepagenav.prototype.defaults; $.fn.onepagenav = function(options) { return this.each(function() { new onepagenav(this, options).init(); }); }; })( jquery, window , document );