1/* 2 * jQuery One Page Nav Plugin 3 * http://github.com/davist11/jQuery-One-Page-Nav 4 * 5 * Copyright (c) 2010 Trevor Davis (http://trevordavis.net) 6 * Dual licensed under the MIT and GPL licenses. 7 * Uses the same license as jQuery, see: 8 * http://jquery.org/license 9 * 10 * @version 3.0.0 11 * 12 * Example usage: 13 * $('#nav').onePageNav({ 14 * currentClass: 'current', 15 * changeHash: false, 16 * scrollSpeed: 750 17 * }); 18 */ 19;(function($, window, document, undefined){ 20 21 // our plugin constructor 22 var OnePageNav = function(elem, options){ 23 this.elem = elem; 24 this.$elem = $(elem); 25 this.options = options; 26 this.metadata = this.$elem.data('plugin-options'); 27 this.$win = $(window); 28 this.sections = {}; 29 this.didScroll = false; 30 this.$doc = $(document); 31 this.docHeight = this.$doc.height(); 32 }; 33 34 // the plugin prototype 35 OnePageNav.prototype = { 36 defaults: { 37 navItems: 'a', 38 currentClass: 'current', 39 changeHash: false, 40 easing: 'swing', 41 filter: '', 42 scrollSpeed: 750, 43 scrollThreshold: 0.5, 44 begin: false, 45 end: false, 46 scrollChange: false 47 }, 48 49 init: function() { 50 // Introduce defaults that can be extended either 51 // globally or using an object literal. 52 this.config = $.extend({}, this.defaults, this.options, this.metadata); 53 54 this.$nav = this.$elem.find(this.config.navItems); 55 56 //Filter any links out of the nav 57 if(this.config.filter !== '') { 58 this.$nav = this.$nav.filter(this.config.filter); 59 } 60 61 //Handle clicks on the nav 62 this.$nav.on('click.onePageNav', $.proxy(this.handleClick, this)); 63 64 //Get the section positions 65 this.getPositions(); 66 67 //Handle scroll changes 68 this.bindInterval(); 69 70 //Update the positions on resize too 71 this.$win.on('resize.onePageNav', $.proxy(this.getPositions, this)); 72 73 return this; 74 }, 75 76 adjustNav: function(self, $parent) { 77 self.$elem.find('.' + self.config.currentClass).removeClass(self.config.currentClass); 78 $parent.addClass(self.config.currentClass); 79 }, 80 81 bindInterval: function() { 82 var self = this; 83 var docHeight; 84 85 self.$win.on('scroll.onePageNav', function() { 86 self.didScroll = true; 87 }); 88 89 self.t = setInterval(function() { 90 docHeight = self.$doc.height(); 91 92 //If it was scrolled 93 if(self.didScroll) { 94 self.didScroll = false; 95 self.scrollChange(); 96 } 97 98 //If the document height changes 99 if(docHeight !== self.docHeight) { 100 self.docHeight = docHeight; 101 self.getPositions(); 102 } 103 }, 250); 104 }, 105 106 getHash: function($link) { 107 return $link.attr('href').split('#')[1]; 108 }, 109 110 getPositions: function() { 111 var self = this; 112 var linkHref; 113 var topPos; 114 var $target; 115 116 self.$nav.each(function() { 117 linkHref = self.getHash($(this)); 118 $target = $('#' + linkHref); 119 120 if($target.length) { 121 topPos = $target.offset().top; 122 self.sections[linkHref] = Math.round(topPos); 123 } 124 }); 125 }, 126 127 getSection: function(windowPos) { 128 var returnValue = null; 129 var windowHeight = Math.round(this.$win.height() * this.config.scrollThreshold); 130 131 for(var section in this.sections) { 132 if((this.sections[section] - windowHeight) < windowPos) { 133 returnValue = section; 134 } 135 } 136 137 return returnValue; 138 }, 139 140 handleClick: function(e) { 141 var self = this; 142 var $link = $(e.currentTarget); 143 var $parent = $link.parent(); 144 var newLoc = '#' + self.getHash($link); 145 146 if(!$parent.hasClass(self.config.currentClass)) { 147 //Start callback 148 if(self.config.begin) { 149 self.config.begin(); 150 } 151 152 //Change the highlighted nav item 153 self.adjustNav(self, $parent); 154 155 //Removing the auto-adjust on scroll 156 self.unbindInterval(); 157 158 //Scroll to the correct position 159 self.scrollTo(newLoc, function() { 160 //Do we need to change the hash? 161 if(self.config.changeHash) { 162 window.location.hash = newLoc; 163 } 164 165 //Add the auto-adjust on scroll back in 166 self.bindInterval(); 167 168 //End callback 169 if(self.config.end) { 170 self.config.end(); 171 } 172 }); 173 } 174 175 e.preventDefault(); 176 }, 177 178 scrollChange: function() { 179 var windowTop = this.$win.scrollTop();
180 var position = this.getSection(windowTop); 181 var $parent; 182 183 //If the position is set 184 if(position !== null) { 185 $parent = this.$elem.find('a[href$="#' + position + '"]').parent(); 186 187 //If it's not already the current section 188 if(!$parent.hasClass(this.config.currentClass)) { 189 //Change the highlighted nav item 190 this.adjustNav(this, $parent); 191 192 //If there is a scrollChange callback 193 if(this.config.scrollChange) { 194 this.config.scrollChange($parent); 195 } 196 } 197 } 198 }, 199 200 scrollTo: function(target, callback) { 201 var offset = $(target).offset().top; 202 203 $('html, body').animate({ 204 scrollTop: offset 205 }, this.config.scrollSpeed, this.config.easing, callback); 206 }, 207 208 unbindInterval: function() { 209 clearInterval(this.t); 210 this.$win.unbind('scroll.onePageNav'); 211 } 212 }; 213 214 OnePageNav.defaults = OnePageNav.prototype.defaults; 215 216 $.fn.onePageNav = function(options) { 217 return this.each(function() { 218 new OnePageNav(this, options).init(); 219 }); 220 }; 221 222})( jQuery, window , document );
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.