PageSourceSearch

https://www.fenchihu.com.tw/js/hs.scroll-nav.js

js fenchihu.com.tw collected 2026-09-26 14:19:14 UTC 7,083 bytes, 373 lines download raw bytes

1/**
2 * HSScrollNav Component.
3 *
4 * @author Htmlstream
5 * @version 1.0
6 * @requires jQuery
7 *
8 */
9;(function ($) {
10  'use strict';
11
12  $.HSCore.components.HSScrollNav = {
13
14    /**
15     * Base configuraion of the component.
16     *
17     * @private
18     */
19    _baseConfig: {
20      duration: 400,
21      easing: 'linear',
22      over: $(),
23      activeItemClass: 'active',
24      afterShow: function(){},
25      beforeShow: function(){}
26    },
27
28    /**
29     * All initialized item on the page.
30     *
31     * @private
32     */
33    _pageCollection: $(),
34
35
36    /**
37     * Initialization of the component.
38     *
39     * @param {jQuery} collection
40     * @param {Object} config
41     *
42     * @public
43     * @return {jQuery}
44     */
45    init: function(collection, config) {
46
47      var self = this;
48
49      if( !collection || !collection.length ) return $();
50
51      collection.each(function(i, el) {
52
53        var $this = $(el),
54            itemConfig = config && $.isPlainObject(config) ?
55                         $.extend(true, {}, self._baseConfig, config, $this.data()) :
56                         $.extend(true, {}, self._baseConfig, $this.data());
57
58        if( !$this.data('HSScrollNav') ) {
59
60          $this.data('HSScrollNav', new HSScrollNav($this, itemConfig));
61
62          self._pageCollection = self._pageCollection.add( $this );
63
64        }
65
66      });
67
68      $(window).on('scroll.HSScrollNav', function(){
69
70        self._pageCollection.each(function(i, el) {
71
72          $(el).data('HSScrollNav').highlight();
73
74        });
75
76      }).trigger('scroll.HSScrollNav');
77
78      return collection;
79
80    }
81
82  }
83
84
85  /**
86   * HSScrollNav.
87   *
88   * @param {jQuery} element
89   * @param {Object} config
90   *
91   * @constructor
92   */
93  function HSScrollNav(element, config) {
94
95    /**
96     * Current element.
97     *
98     * @public
99     */
100    this.element = element;
101
102    /**
103     * Configuraion.
104     *
105     * @public
106     */
107    this.config = config;
108
109    /**
110     * Sections.
111     *
112     * @public
113     */
114    this._items = $();
115
116    this._makeItems();
117    this._bindEvents();
118  }
119
120  /**
121   * Return collection of sections.
122   *
123   * @private
124   * @return {jQuery}
125   */
126  HSScrollNav.prototype._makeItems = function() {
127
128    var self = this;
129
130    this.element.find('a[href^="#"]').each(function(i, el) {
131
132      var $this = $(el);
133
134      if( !$this.data('HSScrollNavSection') ) {
135
136        $this.data('HSScrollNavSection', new HSScrollNavSection($this, self.config));
137
138        self._items = self._items.add( $this );
139
140      }
141
142    });
143
144  };
145
146  /**
147   * Binds necessary events.
148   *
149   * @private
150   * @return {undefined}
151   */
152  HSScrollNav.prototype._bindEvents = function() {
153
154    var self = this;
155
156    this.element.on('click.HSScrollNav', 'a[href^="#"]', function(e) {
157
158      var link = this;
159      self._lockHightlight = true;
160      if(self.current) self.current.unhighlight();
161      link.blur();
162      self.current = $(link).data('HSScrollNavSection');
163      self.current.highlight();
164
165      $(this).data('HSScrollNavSection').show( function(){
166        self._lockHightlight = false;
167      } );
168
169      e.preventDefault();
170
171    });
172
173  };
174
175  /**
176   * Activates necessary menu item.
177   *
178   * @public
179   */
180  HSScrollNav.prototype.highlight = function() {
181
182    var self = this, items, currentItem, current, scrollTop;
183
184    if(!this._items.length || this._lockHightlight) return;
185
186    scrollTop = $(window).scrollTop();
187
188    if(scrollTop + $(window).height() === $(document).height()) {
189
190      this.current = this._items.last().data('HSScrollNavSection');
191
192      this.unhighlight();
193      this.current.highlight();
194      this.current.changeHash();
195
196      return;
197    }
198
199    this._items.each(function(i, el){
200
201      var Section = $(el).data('HSScrollNavSection'),
202          $section = Section.section;
203
204      if(scrollTop > Section.offset) {
205        current = Section;
206      }
207
208    });
209
210    if(current && this.current != current) {
211
212      this.unhighlight();
213      current.highlight();
214      if(this.current) current.changeHash();
215
216      this.current = current;
217
218    }
219
220  };
221
222  /**
223   * Deactivates all menu items.
224   *
225   * @public
226   */
227  HSScrollNav.prototype.unhighlight = function() {
228
229    this._items.each(function(i, el){
230      $(el).data('HSScrollNavSection').unhighlight();
231    });
232
233  };
234
235  /**
236   * HSScrollNavSection.
237   *
238   * @param {jQuery} element
239   *
240   * @constructor
241   */
242  function HSScrollNavSection(element, config) {
243
244    var self = this;
245
246    /**
247     * Current section.
248     *
249     * @public
250     */
251    this.element = element;
252
253    /**
254     * Configuration.
255     *
256     * @public
257     */
258    this.config = config;
259
260    /**
261     * Getter for acces to the section element.
262     *
263     * @public
264     */
265    Object.defineProperty(this, 'section', {
266      value: $(self.element.attr('href'))
267    });
268
269    /**
270     * Getter for determinate position of the section relative to document.
271     *
272     * @public
273     */
274
275    Object.defineProperty(this, 'offset', {
276      get: function() {
277
278        var header = $('.u-header'),
279            headerStyles = getComputedStyle(header.get(0)),
280            headerPosition = headerStyles.position,
281            offset = self.section.offset().top;
282
283
284
285        if(header.length && headerPosition == 'fixed' && parseInt(headerStyles.top) == 0) {
286          offset = offset - header.outerHeight() - parseInt(headerStyles.marginTop);
287        }
288
289        if(self.config.over.length) {
290          offset = offset - self.config.over.outerHeight();
291        }
292
293        return offset;
294      }
295    });
296
297
298  }
299
300  /**
301   * Moves to the section.
302   *
303   * @public
304   */
305  HSScrollNavSection.prototype.show = function(callback) {
306
307    var self = this;
308
309    if( !this.section.length ) return;
310
311    self.config.beforeShow.call(self.section);
312
313    this.changeHash();
314
315    $('html, body').stop().animate({
316      scrollTop: self.offset + 3
317    }, {
318      duration: self.config.duration,
319      easing: self.config.easing,
320      complete: function() {
321        $('html, body').stop().animate({
322          scrollTop: self.offset + 3
323        }, {
324          duration: self.config.duration,
325          easing: self.config.easing,
326          complete: function() {
327            self.config.afterShow.call(self.section);
328            if($.isFunction(callback)) callback();
329          }
330        });
331      }
332    });
333
334  };
335
336  /**
337   * Changes location's hash.
338   *
339   * @public
340   */
341  HSScrollNavSection.prototype.changeHash = function() {
342    this.section.attr('id', '');
343    window.location.hash = this.element.attr('href');
344    this.section.attr('id', this.element.attr('href').slice(1));
345  };
346
347  /**
348   * Activates the menu item.
349   *
350   * @public
351   */
352  HSScrollNavSection.prototype.highlight = function() {
353
354    var parent = this.element.parent('li');
355    if(parent.length) parent.addClass(this.config.activeItemClass);
356
357  };
358
359  /**
360   * Deactivates the menu item.
361   *
362   * @public
363   */
364  HSScrollNavSection.prototype.unhighlight = function() {
365
366    var parent = this.element.parent('li');
367    if(parent.length) parent.removeClass(this.config.activeItemClass);
368
369  };
370
371
372
373})(jQuery);

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.