diff --git a/bootstrap-toc.css b/bootstrap-toc.css index e8a054c..118cdd4 100644 --- a/bootstrap-toc.css +++ b/bootstrap-toc.css @@ -8,53 +8,47 @@ /* All levels of nav */ nav[data-toggle='toc'] .nav > li > a { display: block; - padding: 4px 20px; - font-size: 13px; font-weight: 500; color: #767676; + padding: 5px 10px; + border-left: 1px solid rgba(255, 255, 255, 0); } + nav[data-toggle='toc'] .nav > li > a:hover, nav[data-toggle='toc'] .nav > li > a:focus { - padding-left: 19px; color: #563d7c; text-decoration: none; background-color: transparent; border-left: 1px solid #563d7c; } -nav[data-toggle='toc'] .nav > .active > a, -nav[data-toggle='toc'] .nav > .active:hover > a, -nav[data-toggle='toc'] .nav > .active:focus > a { - padding-left: 18px; +nav[data-toggle='toc'] .nav .active > a, +nav[data-toggle='toc'] .nav .active:hover > a, +nav[data-toggle='toc'] .nav .active:focus > a { font-weight: bold; color: #563d7c; background-color: transparent; border-left: 2px solid #563d7c; } +nav[data-toggle='toc'] { + font-size: 18px; +} + +nav[data-toggle='toc'] .nav { + position: relative; + padding-left: 7px; + font-size: 90%; +} + /* Nav: second level (shown on .active) */ nav[data-toggle='toc'] .nav .nav { display: none; /* Hide by default, but at >768px, show it */ - padding-bottom: 10px; -} -nav[data-toggle='toc'] .nav .nav > li > a { - padding-top: 1px; - padding-bottom: 1px; - padding-left: 30px; - font-size: 12px; - font-weight: normal; -} -nav[data-toggle='toc'] .nav .nav > li > a:hover, -nav[data-toggle='toc'] .nav .nav > li > a:focus { - padding-left: 29px; -} -nav[data-toggle='toc'] .nav .nav > .active > a, -nav[data-toggle='toc'] .nav .nav > .active:hover > a, -nav[data-toggle='toc'] .nav .nav > .active:focus > a { - padding-left: 28px; - font-weight: 500; } /* from https://github.com/twbs/bootstrap/blob/e38f066d8c203c3e032da0ff23cd2d6098ee2dd6/docs/assets/css/src/docs.css#L631-L634 */ -nav[data-toggle='toc'] .nav > .active > ul { +nav[data-toggle='toc'] .nav > .active > ul, +nav[data-toggle='toc'] .nav:first-child li:first-child > ul:first-child, +nav[data-toggle='toc'] .nav > .active > ul li:first-child > ul:first-child +{ display: block; } diff --git a/bootstrap-toc.js b/bootstrap-toc.js index 7834877..233438b 100644 --- a/bootstrap-toc.js +++ b/bootstrap-toc.js @@ -65,6 +65,11 @@ return $li; }, + generateEmptyNavEl: function() { + var $li = $('
  • '); + return $li; + }, + generateNavItem: function(headingEl) { var anchor = this.generateAnchor(headingEl); var $heading = $(headingEl); @@ -84,41 +89,54 @@ return 1; }, - // returns the elements for the top level, and the next below it - getHeadings: function($scope, topLevel) { - var topSelector = 'h' + topLevel; - - var secondaryLevel = topLevel + 1; - var secondarySelector = 'h' + secondaryLevel; + getHeadings: function($scope, depth, topLevel) { + var selector = ''; + for (var i = topLevel; i < topLevel + depth; i++) { + selector += 'h' + i; + if (i < topLevel + depth - 1) + selector += ','; + } - return this.findOrFilter($scope, topSelector + ',' + secondarySelector); + return this.findOrFilter($scope, selector); }, getNavLevel: function(el) { return parseInt(el.tagName.charAt(1), 10); }, - populateNav: function($topContext, topLevel, $headings) { - var $context = $topContext; - var $prevNav; + populateNav: function($topContext, depth, topLevel, $headings) { + var $contexts = new Array(depth); + var helpers = this; + + $contexts[0] = $topContext; + $topContext.lastNav = null; - var helpers = this; $headings.each(function(i, el) { var $newNav = helpers.generateNavItem(el); var navLevel = helpers.getNavLevel(el); + var relLevel = navLevel - topLevel; + var j; - // determine the proper $context - if (navLevel === topLevel) { - // use top level - $context = $topContext; - } else if ($prevNav && $context === $topContext) { - // create a new level of the tree and switch to it - $context = helpers.createChildNavList($prevNav); - } // else use the current $context + for (j = relLevel + 1; j < $contexts.length; j++) { + $contexts[j] = null; + } - $context.append($newNav); + if (!$contexts[relLevel]) { + for (j = 0; j < relLevel; j++) { + if (!$contexts[j + 1]) { + if (!$contexts[j].lastNav) { + var $emptyNav = helpers.generateEmptyNavEl(); + $contexts[j].append($emptyNav); + $contexts[j].lastNav = $emptyNav; + } + $contexts[j + 1] = helpers.createChildNavList($contexts[j].lastNav); + $contexts[j + 1].lastNav = null; + } + } + } - $prevNav = $newNav; + $contexts[relLevel].append($newNav); + $contexts[relLevel].lastNav = $newNav; }); }, @@ -132,6 +150,7 @@ opts = arg; } opts.$scope = opts.$scope || $(document.body); + opts.depth = opts.depth || opts.$nav.attr('data-toc-depth') || 2; return opts; } }, @@ -145,8 +164,8 @@ var $topContext = this.helpers.createChildNavList(opts.$nav); var topLevel = this.helpers.getTopLevel(opts.$scope); - var $headings = this.helpers.getHeadings(opts.$scope, topLevel); - this.helpers.populateNav($topContext, topLevel, $headings); + var $headings = this.helpers.getHeadings(opts.$scope, opts.depth, topLevel); + this.helpers.populateNav($topContext, opts.depth, topLevel, $headings); } }; diff --git a/index.md b/index.md index 92c6524..473b907 100644 --- a/index.md +++ b/index.md @@ -83,6 +83,7 @@ option | type | notes --- | --- | --- `$nav` | jQuery Object | (required) The element that the navigation will be created in. `$scope` | jQuery Object | The element where the search for headings will be limited to, or the list of headings that will be used in the navigation. Defaults to `$(document.body)`. +`depth` | Integer | The number of heading classes to consider, starting from the top level. Defaults to `2`. {: .table } ## Customization diff --git a/test/toc-test.js b/test/toc-test.js index c86339f..6da228e 100644 --- a/test/toc-test.js +++ b/test/toc-test.js @@ -89,7 +89,7 @@ describe('Toc', function() { expect(level).to.eql(6); }); }); - + describe('.getNavLevel()', function() { it("returns the value from the tag", function() { var el = document.createElement('h5'); @@ -206,5 +206,112 @@ describe('Toc', function() { '' ); }); + + it("handles up to 6 nested headings", function() { + $fixture.append( + '

    H1

    ' + + '

    H2

    ' + + '

    H3

    ' + + '

    H4

    ' + + '
    H5
    ' + + '
    H6
    ' + ); + + $nav = $( + '' + ); + + Toc.init({ + $nav: $nav, + $scope: $fixture + }); + + expect($nav.html()).to.eql( + '' + ); + }); + + it("handles staggered nesting", function() { + $fixture.append( + '

    H1

    ' + + '

    H2

    ' + + '

    H3

    ' + + '

    H1-1

    ' + // expect drop from 3 to 1 + '

    H3-1

    ' + // intentionally in wrong spot + '

    H2-1

    ' + ); + + $nav = $( + '' + ); + + Toc.init({ + $nav: $nav, + $scope: $fixture + }); + + expect($nav.html()).to.eql( + '' + ); + }); + }); });