1
0
Fork 0
mirror of https://github.com/twbs/bootstrap.git synced 2022-11-09 12:25:43 -05:00

scrollspy es6

This commit is contained in:
fat 2015-05-11 12:05:35 -07:00
parent ca9c850ebb
commit 2d91494d96
7 changed files with 562 additions and 363 deletions

View file

@ -63,13 +63,14 @@ module.exports = function (grunt) {
},
dist: {
files: {
'js/dist/util.js' : 'js/src/util.js',
'js/dist/alert.js' : 'js/src/alert.js',
'js/dist/button.js' : 'js/src/button.js',
'js/dist/carousel.js' : 'js/src/carousel.js',
'js/dist/collapse.js' : 'js/src/collapse.js',
'js/dist/dropdown.js' : 'js/src/dropdown.js',
'js/dist/modal.js' : 'js/src/modal.js'
'js/dist/util.js' : 'js/src/util.js',
'js/dist/alert.js' : 'js/src/alert.js',
'js/dist/button.js' : 'js/src/button.js',
'js/dist/carousel.js' : 'js/src/carousel.js',
'js/dist/collapse.js' : 'js/src/collapse.js',
'js/dist/dropdown.js' : 'js/src/dropdown.js',
'js/dist/modal.js' : 'js/src/modal.js',
'js/dist/scrollspy.js' : 'js/src/scrollspy.js'
}
}
},

268
js/dist/scrollspy.js vendored Normal file
View file

@ -0,0 +1,268 @@
'use strict';
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.0.0): scrollspy.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* --------------------------------------------------------------------------
*/
var ScrollSpy = (function ($) {
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
var NAME = 'scrollspy';
var VERSION = '4.0.0';
var DATA_KEY = 'bs.scrollspy';
var JQUERY_NO_CONFLICT = $.fn[NAME];
var TRANSITION_DURATION = 150;
var Defaults = {
offset: 10
};
var Event = {
ACTIVATE: 'activate.bs.scrollspy',
SCROLL: 'scroll.bs.scrollspy',
LOAD: 'load.bs.scrollspy.data-api'
};
var ClassName = {
DROPDOWN_MENU: 'dropdown-menu',
ACTIVE: 'active'
};
var Selector = {
DATA_SPY: '[data-spy="scroll"]',
ACTIVE: '.active',
LI_DROPDOWN: 'li.dropdown',
LI: 'li'
};
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
var ScrollSpy = (function () {
function ScrollSpy(element, config) {
_classCallCheck(this, ScrollSpy);
this._scrollElement = element.tagName === 'BODY' ? window : element;
this._config = $.extend({}, Defaults, config);
this._selector = '' + (this._config.target || '') + ' .nav li > a';
this._offsets = [];
this._targets = [];
this._activeTarget = null;
this._scrollHeight = 0;
$(this._scrollElement).on(Event.SCROLL, this._process.bind(this));
this.refresh();
this._process();
}
_createClass(ScrollSpy, [{
key: 'refresh',
// public
value: function refresh() {
var _this = this;
var offsetMethod = 'offset';
var offsetBase = 0;
if (this._scrollElement !== this._scrollElement.window) {
offsetMethod = 'position';
offsetBase = this._getScrollTop();
}
this._offsets = [];
this._targets = [];
this._scrollHeight = this._getScrollHeight();
var targets = $.makeArray($(this._selector));
targets.map(function (element) {
var target = undefined;
var targetSelector = Util.getSelectorFromElement(element);
if (targetSelector) {
target = $(targetSelector)[0];
}
if (target && (target.offsetWidth || target.offsetHeight)) {
// todo (fat): remove sketch reliance on jQuery position/offset
return [$(target)[offsetMethod]().top + offsetBase, targetSelector];
}
}).filter(function (item) {
return item;
}).sort(function (a, b) {
return a[0] - b[0];
}).forEach(function (item) {
_this._offsets.push(item[0]);
_this._targets.push(item[1]);
});
}
}, {
key: '_getScrollTop',
// private
value: function _getScrollTop() {
return this._scrollElement === window ? this._scrollElement.scrollY : this._scrollElement.scrollTop;
}
}, {
key: '_getScrollHeight',
value: function _getScrollHeight() {
return this._scrollElement.scrollHeight || Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
}
}, {
key: '_process',
value: function _process() {
var scrollTop = this._getScrollTop() + this._config.offset;
var scrollHeight = this._getScrollHeight();
var maxScroll = this._config.offset + scrollHeight - this._scrollElement.offsetHeight;
if (this._scrollHeight !== scrollHeight) {
this.refresh();
}
if (scrollTop >= maxScroll) {
var target = this._targets[this._targets.length - 1];
if (this._activeTarget !== target) {
this._activate(target);
}
}
if (this._activeTarget && scrollTop < this._offsets[0]) {
this._activeTarget = null;
this._clear();
return;
}
for (var i = this._offsets.length; i--;) {
var isActiveTarget = this._activeTarget !== this._targets[i] && scrollTop >= this._offsets[i] && (this._offsets[i + 1] === undefined || scrollTop < this._offsets[i + 1]);
if (isActiveTarget) {
this._activate(this._targets[i]);
}
}
}
}, {
key: '_activate',
value: function _activate(target) {
this._activeTarget = target;
this._clear();
var selector = '' + this._selector + '[data-target="' + target + '"],' + ('' + this._selector + '[href="' + target + '"]');
// todo (fat): getting all the raw li's up the tree is not great.
var parentListItems = $(selector).parents(Selector.LI);
for (var i = parentListItems.length; i--;) {
$(parentListItems[i]).addClass(ClassName.ACTIVE);
var itemParent = parentListItems[i].parentNode;
if (itemParent && $(itemParent).hasClass(ClassName.DROPDOWN_MENU)) {
var closestDropdown = $(itemParent).closest(Selector.LI_DROPDOWN)[0];
$(closestDropdown).addClass(ClassName.ACTIVE);
}
}
$(this._scrollElement).trigger(Event.ACTIVATE, {
relatedTarget: target
});
}
}, {
key: '_clear',
value: function _clear() {
var activeParents = $(this._selector).parentsUntil(this._config.target, Selector.ACTIVE);
for (var i = activeParents.length; i--;) {
$(activeParents[i]).removeClass(ClassName.ACTIVE);
}
}
}], [{
key: 'VERSION',
// getters
get: function () {
return VERSION;
}
}, {
key: 'Default',
get: function () {
return Default;
}
}, {
key: '_jQueryInterface',
// static
value: function _jQueryInterface(config) {
return this.each(function () {
var data = $(this).data(DATA_KEY);
var _config = typeof config === 'object' && config || null;
if (!data) {
data = new ScrollSpy(this, _config);
$(this).data(DATA_KEY, data);
}
if (typeof config === 'string') {
data[config]();
}
});
}
}]);
return ScrollSpy;
})();
/**
* ------------------------------------------------------------------------
* Data Api implementation
* ------------------------------------------------------------------------
*/
$(window).on(Event.LOAD, function () {
var scrollSpys = $.makeArray($(Selector.DATA_SPY));
for (var i = scrollSpys.length; i--;) {
var $spy = $(scrollSpys[i]);
ScrollSpy._jQueryInterface.call($spy, $spy.data());
}
});
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = ScrollSpy._jQueryInterface;
$.fn[NAME].Constructor = ScrollSpy;
$.fn[NAME].noConflict = function () {
$.fn[NAME] = JQUERY_NO_CONFLICT;
return ScrollSpy._jQueryInterface;
};
return ScrollSpy;
})(jQuery);
//# sourceMappingURL=scrollspy.js.map

1
js/dist/scrollspy.js.map vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -1,337 +0,0 @@
/* ========================================================================
* Bootstrap: modal.js v3.3.4
* http://getbootstrap.com/javascript/#modals
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
+function ($) {
'use strict';
// MODAL CLASS DEFINITION
// ======================
var Modal = function (element, options) {
this.options = options
this.$body = $(document.body)
this.$element = $(element)
this.$dialog = this.$element.find('.modal-dialog')
this.$backdrop = null
this.isShown = null
this.originalBodyPad = null
this.scrollbarWidth = 0
this.ignoreBackdropClick = false
if (this.options.remote) {
this.$element
.find('.modal-content')
.load(this.options.remote, $.proxy(function () {
this.$element.trigger('loaded.bs.modal')
}, this))
}
}
Modal.VERSION = '3.3.4'
Modal.TRANSITION_DURATION = 300
Modal.BACKDROP_TRANSITION_DURATION = 150
Modal.DEFAULTS = {
backdrop: true,
keyboard: true,
show: true
}
Modal.prototype.toggle = function (_relatedTarget) {
return this.isShown ? this.hide() : this.show(_relatedTarget)
}
Modal.prototype.show = function (_relatedTarget) {
var that = this
var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })
this.$element.trigger(e)
if (this.isShown || e.isDefaultPrevented()) return
this.isShown = true
this.checkScrollbar()
this.setScrollbar()
this.$body.addClass('modal-open')
this.escape()
this.resize()
this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))
this.$dialog.on('mousedown.dismiss.bs.modal', function () {
that.$element.one('mouseup.dismiss.bs.modal', function (e) {
if ($(e.target).is(that.$element)) that.ignoreBackdropClick = true
})
})
this.backdrop(function () {
var transition = $.support.transition && that.$element.hasClass('fade')
if (!that.$element.parent().length) {
that.$element.appendTo(that.$body) // don't move modals dom position
}
that.$element
.show()
.scrollTop(0)
that.adjustDialog()
if (transition) {
that.$element[0].offsetWidth // force reflow
}
that.$element.addClass('in')
that.enforceFocus()
var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })
transition ?
that.$dialog // wait for modal to slide in
.one('bsTransitionEnd', function () {
that.$element.trigger('focus').trigger(e)
})
.emulateTransitionEnd(Modal.TRANSITION_DURATION) :
that.$element.trigger('focus').trigger(e)
})
}
Modal.prototype.hide = function (e) {
if (e) e.preventDefault()
e = $.Event('hide.bs.modal')
this.$element.trigger(e)
if (!this.isShown || e.isDefaultPrevented()) return
this.isShown = false
this.escape()
this.resize()
$(document).off('focusin.bs.modal')
this.$element
.removeClass('in')
.off('click.dismiss.bs.modal')
.off('mouseup.dismiss.bs.modal')
this.$dialog.off('mousedown.dismiss.bs.modal')
$.support.transition && this.$element.hasClass('fade') ?
this.$element
.one('bsTransitionEnd', $.proxy(this.hideModal, this))
.emulateTransitionEnd(Modal.TRANSITION_DURATION) :
this.hideModal()
}
Modal.prototype.enforceFocus = function () {
$(document)
.off('focusin.bs.modal') // guard against infinite focus loop
.on('focusin.bs.modal', $.proxy(function (e) {
if (this.$element[0] !== e.target && !this.$element.has(e.target).length) {
this.$element.trigger('focus')
}
}, this))
}
Modal.prototype.escape = function () {
if (this.isShown && this.options.keyboard) {
this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) {
e.which == 27 && this.hide()
}, this))
} else if (!this.isShown) {
this.$element.off('keydown.dismiss.bs.modal')
}
}
Modal.prototype.resize = function () {
if (this.isShown) {
$(window).on('resize.bs.modal', $.proxy(this.handleUpdate, this))
} else {
$(window).off('resize.bs.modal')
}
}
Modal.prototype.hideModal = function () {
var that = this
this.$element.hide()
this.backdrop(function () {
that.$body.removeClass('modal-open')
that.resetAdjustments()
that.resetScrollbar()
that.$element.trigger('hidden.bs.modal')
})
}
Modal.prototype.removeBackdrop = function () {
this.$backdrop && this.$backdrop.remove()
this.$backdrop = null
}
Modal.prototype.backdrop = function (callback) {
var that = this
var animate = this.$element.hasClass('fade') ? 'fade' : ''
if (this.isShown && this.options.backdrop) {
var doAnimate = $.support.transition && animate
this.$backdrop = $(document.createElement('div'))
.addClass('modal-backdrop ' + animate)
.appendTo(this.$body)
this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) {
if (this.ignoreBackdropClick) {
this.ignoreBackdropClick = false
return
}
if (e.target !== e.currentTarget) return
this.options.backdrop == 'static'
? this.$element[0].focus()
: this.hide()
}, this))
if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
this.$backdrop.addClass('in')
if (!callback) return
doAnimate ?
this.$backdrop
.one('bsTransitionEnd', callback)
.emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
callback()
} else if (!this.isShown && this.$backdrop) {
this.$backdrop.removeClass('in')
var callbackRemove = function () {
that.removeBackdrop()
callback && callback()
}
$.support.transition && this.$element.hasClass('fade') ?
this.$backdrop
.one('bsTransitionEnd', callbackRemove)
.emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
callbackRemove()
} else if (callback) {
callback()
}
}
// these following methods are used to handle overflowing modals
Modal.prototype.handleUpdate = function () {
this.adjustDialog()
}
Modal.prototype.adjustDialog = function () {
var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight
this.$element.css({
paddingLeft: !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '',
paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : ''
})
}
Modal.prototype.resetAdjustments = function () {
this.$element.css({
paddingLeft: '',
paddingRight: ''
})
}
Modal.prototype.checkScrollbar = function () {
var fullWindowWidth = window.innerWidth
if (!fullWindowWidth) { // workaround for missing window.innerWidth in IE8
var documentElementRect = document.documentElement.getBoundingClientRect()
fullWindowWidth = documentElementRect.right - Math.abs(documentElementRect.left)
}
this.bodyIsOverflowing = document.body.clientWidth < fullWindowWidth
this.scrollbarWidth = this.measureScrollbar()
}
Modal.prototype.setScrollbar = function () {
var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10)
this.originalBodyPad = document.body.style.paddingRight || ''
if (this.bodyIsOverflowing) this.$body.css('padding-right', bodyPad + this.scrollbarWidth)
}
Modal.prototype.resetScrollbar = function () {
this.$body.css('padding-right', this.originalBodyPad)
}
Modal.prototype.measureScrollbar = function () { // thx walsh
var scrollDiv = document.createElement('div')
scrollDiv.className = 'modal-scrollbar-measure'
this.$body.append(scrollDiv)
var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth
this.$body[0].removeChild(scrollDiv)
return scrollbarWidth
}
// MODAL PLUGIN DEFINITION
// =======================
function Plugin(option, _relatedTarget) {
return this.each(function () {
var $this = $(this)
var data = $this.data('bs.modal')
var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data) $this.data('bs.modal', (data = new Modal(this, options)))
if (typeof option == 'string') data[option](_relatedTarget)
else if (options.show) data.show(_relatedTarget)
})
}
var old = $.fn.modal
$.fn.modal = Plugin
$.fn.modal.Constructor = Modal
// MODAL NO CONFLICT
// =================
$.fn.modal.noConflict = function () {
$.fn.modal = old
return this
}
// MODAL DATA-API
// ==============
$(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) {
var $this = $(this)
var href = $this.attr('href')
var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) // strip for ie7
var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())
if ($this.is('a')) e.preventDefault()
$target.one('show.bs.modal', function (showEvent) {
if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown
$target.one('hidden.bs.modal', function () {
$this.is(':visible') && $this.trigger('focus')
})
})
Plugin.call($target, option, this)
})
}(jQuery);

275
js/src/scrollspy.js Normal file
View file

@ -0,0 +1,275 @@
import Util from './util'
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.0.0): scrollspy.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* --------------------------------------------------------------------------
*/
const ScrollSpy = (($) => {
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
const NAME = 'scrollspy'
const VERSION = '4.0.0'
const DATA_KEY = 'bs.scrollspy'
const JQUERY_NO_CONFLICT = $.fn[NAME]
const TRANSITION_DURATION = 150
const Defaults = {
offset : 10
}
const Event = {
ACTIVATE : 'activate.bs.scrollspy',
SCROLL : 'scroll.bs.scrollspy',
LOAD : 'load.bs.scrollspy.data-api'
}
const ClassName = {
DROPDOWN_MENU : 'dropdown-menu',
ACTIVE : 'active'
}
const Selector = {
DATA_SPY : '[data-spy="scroll"]',
ACTIVE : '.active',
LI_DROPDOWN : 'li.dropdown',
LI : 'li'
}
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
class ScrollSpy {
constructor(element, config) {
this._scrollElement = element.tagName === 'BODY' ? window : element
this._config = $.extend({}, Defaults, config)
this._selector = `${this._config.target || ''} .nav li > a`
this._offsets = []
this._targets = []
this._activeTarget = null
this._scrollHeight = 0
$(this._scrollElement).on(Event.SCROLL, this._process.bind(this))
this.refresh()
this._process()
}
// getters
static get VERSION() {
return VERSION
}
static get Default() {
return Default
}
// public
refresh() {
let offsetMethod = 'offset'
let offsetBase = 0
if (this._scrollElement !== this._scrollElement.window) {
offsetMethod = 'position'
offsetBase = this._getScrollTop()
}
this._offsets = []
this._targets = []
this._scrollHeight = this._getScrollHeight()
let targets = $.makeArray($(this._selector))
targets
.map((element) => {
let target
let targetSelector = Util.getSelectorFromElement(element)
if (targetSelector) {
target = $(targetSelector)[0]
}
if (target && (target.offsetWidth || target.offsetHeight)) {
// todo (fat): remove sketch reliance on jQuery position/offset
return [
$(target)[offsetMethod]().top + offsetBase,
targetSelector
]
}
})
.filter((item) => item)
.sort((a, b) => a[0] - b[0])
.forEach((item) => {
this._offsets.push(item[0])
this._targets.push(item[1])
})
}
// private
_getScrollTop() {
return this._scrollElement === window ?
this._scrollElement.scrollY : this._scrollElement.scrollTop
}
_getScrollHeight() {
return this._scrollElement.scrollHeight || Math.max(
document.body.scrollHeight,
document.documentElement.scrollHeight
)
}
_process() {
let scrollTop = this._getScrollTop() + this._config.offset
let scrollHeight = this._getScrollHeight()
let maxScroll = this._config.offset
+ scrollHeight
- this._scrollElement.offsetHeight
if (this._scrollHeight !== scrollHeight) {
this.refresh()
}
if (scrollTop >= maxScroll) {
let target = this._targets[this._targets.length - 1]
if (this._activeTarget !== target) {
this._activate(target)
}
}
if (this._activeTarget && scrollTop < this._offsets[0]) {
this._activeTarget = null
this._clear()
return
}
for (let i = this._offsets.length; i--;) {
let isActiveTarget = this._activeTarget !== this._targets[i]
&& scrollTop >= this._offsets[i]
&& (this._offsets[i + 1] === undefined ||
scrollTop < this._offsets[i + 1])
if (isActiveTarget) {
this._activate(this._targets[i])
}
}
}
_activate(target) {
this._activeTarget = target
this._clear()
let selector =
`${this._selector}[data-target="${target}"],` +
`${this._selector}[href="${target}"]`
// todo (fat): getting all the raw li's up the tree is not great.
let parentListItems = $(selector).parents(Selector.LI)
for (let i = parentListItems.length; i--;) {
$(parentListItems[i]).addClass(ClassName.ACTIVE)
let itemParent = parentListItems[i].parentNode
if (itemParent && $(itemParent).hasClass(ClassName.DROPDOWN_MENU)) {
let closestDropdown = $(itemParent)
.closest(Selector.LI_DROPDOWN)[0]
$(closestDropdown).addClass(ClassName.ACTIVE)
}
}
$(this._scrollElement).trigger(Event.ACTIVATE, {
relatedTarget: target
})
}
_clear() {
let activeParents = $(this._selector).parentsUntil(
this._config.target,
Selector.ACTIVE
)
for (let i = activeParents.length; i--;) {
$(activeParents[i]).removeClass(ClassName.ACTIVE)
}
}
// static
static _jQueryInterface(config) {
return this.each(function () {
let data = $(this).data(DATA_KEY)
let _config = typeof config === 'object' && config || null
if (!data) {
data = new ScrollSpy(this, _config)
$(this).data(DATA_KEY, data)
}
if (typeof config === 'string') {
data[config]()
}
})
}
}
/**
* ------------------------------------------------------------------------
* Data Api implementation
* ------------------------------------------------------------------------
*/
$(window).on(Event.LOAD, function () {
let scrollSpys = $.makeArray($(Selector.DATA_SPY))
for (let i = scrollSpys.length; i--;) {
let $spy = $(scrollSpys[i])
ScrollSpy._jQueryInterface.call($spy, $spy.data())
}
})
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = ScrollSpy._jQueryInterface
$.fn[NAME].Constructor = ScrollSpy
$.fn[NAME].noConflict = function () {
$.fn[NAME] = JQUERY_NO_CONFLICT
return ScrollSpy._jQueryInterface
}
return ScrollSpy
})(jQuery)
export default ScrollSpy

View file

@ -137,9 +137,9 @@
<script src="../../js/dist/collapse.js"></script>
<script src="../../js/dist/dropdown.js"></script>
<script src="../../js/dist/modal.js"></script>
<script src="../../js/dist/scrollspy.js"></script>
<!-- Old Plugin sources -->
<script src="../../js/scrollspy.js"></script>
<script src="../../js/tab.js"></script>
<script src="../../js/tooltip.js"></script>
<script src="../../js/popover.js"></script>

View file

@ -24,21 +24,12 @@
<nav class="navbar navbar-default navbar-fixed-top" role="navigation">
<div class="container-fluid">
<div class="navbar-header">
<button class="navbar-toggle collapsed" type="button" data-toggle="collapse" data-target=".js-navbar-scrollspy">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Project Name</a>
</div>
<div class="collapse navbar-collapse js-navbar-scrollspy">
<div class="js-navbar-scrollspy">
<ul class="nav navbar-nav">
<li class=""><a href="#fat">@fat</a></li>
<li class=""><a href="#mdo">@mdo</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown <b class="caret"></b></a>
<li class="nav-item active"><a class="nav-link" href="#fat">@fat</a></li>
<li class="nav-item"><a class="nav-link" href="#mdo">@mdo</a></li>
<li class="dropdown nav-item">
<a href="#" class="dropdown-toggle nav-link" data-toggle="dropdown">Dropdown <b class="caret"></b></a>
<ul class="dropdown-menu" role="menu">
<li class=""><a href="#one" tabindex="-1">one</a></li>
<li><a href="#two" tabindex="-1">two</a></li>
@ -100,10 +91,10 @@
<!-- JavaScript Includes -->
<script src="../vendor/jquery.min.js"></script>
<script src="../../transition.js"></script>
<script src="../../scrollspy.js"></script>
<script src="../../dropdown.js"></script>
<script src="../../collapse.js"></script>
<script src="../../dist/util.js"></script>
<script src="../../dist/scrollspy.js"></script>
<script src="../../dist/dropdown.js"></script>
<script src="../../dist/collapse.js"></script>
</body>
</html>