├── .gitignore ├── .travis.yml ├── README.md ├── index.html └── table-of-contents.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Node 2 | node_modules 3 | test/results 4 | test/coverage 5 | 6 | ## OS X 7 | .DS_Store 8 | ._* 9 | .Spotlight-V100 10 | .Trashes -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "7" 4 | before_script: 5 | - npm install -g gulp 6 | script: gulp -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Table of Contents 2 | Automatically generate a table of contents from the headings on the page 3 | 4 | **[Demo](https://cferdinandi.github.io/table-of-contents/)** 5 | 6 | [Getting Started](#getting-started) | [Options & Settings](#options-and-settings) | [Browser Compatibility](#browser-compatibility) | [License](#license) 7 | 8 | 9 | ## Getting Started 10 | 11 | ### 1. Include Table of Contents on your site. 12 | 13 | ```html 14 | 15 | ``` 16 | 17 | ### 2. Add the markup to your HTML. 18 | 19 | Include an empty element on the page that will hold your table of contents, and give it a unique selector. You also need to wrap the content that you want to generate your table of contents from in an element with a unique selector. 20 | 21 | You can use any selectors you want. The example below uses data attributes. 22 | 23 | ```html 24 |
25 | 26 |
27 |

Cat O'Nine Tails

28 |

...

29 | 30 |

The Brig

31 |

...

32 | 33 |

Privateer

34 |

...

35 | 36 |

Ahoy

37 |

...

38 |
39 | ``` 40 | 41 | *__Note:__ headings without an ID will have one automatically generated based on the heading content.* 42 | 43 | ### 3. Initialize Table of Contents 44 | 45 | In the footer of your page, after the content, initialize Table of Contents by passing in two arguments: the table of contents element selector, and content selector. 46 | 47 | ```html 48 | 51 | ``` 52 | 53 | 54 | 55 | ## Options and Settings 56 | 57 | You can pass options into `tableOfContents()` as an optional third argument when initializing it to customize how the script works. 58 | 59 | ```javascript 60 | tableOfContents('[data-content]', '[data-toc]', { 61 | levels: 'h2, h3, h4, h5, h6', // The heading levels to generate a table of contents from 62 | heading: 'Table of Contents', // The heading text for the table of contents list 63 | headingLevel: 'h2', // The level to use for the heading for the table of contents list 64 | listType: 'ul' // The list type to use for the table of contents 65 | }); 66 | ``` 67 | 68 | 69 | 70 | ## Browser Compatibility 71 | 72 | Table of Contents works in all modern browsers, and IE 9 and above. 73 | 74 | 75 | 76 | ## License 77 | 78 | The code is available under the [MIT License](LICENSE.md). -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Table of Contents 5 | 6 | 14 | 15 | 16 | 17 |

Table of Contents

18 | 19 |
20 | 21 |
22 |

Cat O'Nine Tails

23 | 24 |

Cat o'nine tails Pieces of Eight swab carouser tackle. Pink hornswaggle gabion Sea Legs Davy Jones' Locker.

25 | 26 |

Hang the jib Nelsons folly trysail ahoy prow. Transom strike colors scallywag aft league.

27 | 28 |

The Brig

29 | 30 |

Dead men tell no tales topmast Sail ho Davy Jones' Locker chantey. Wherry fluke pillage rope's end brig.

31 | 32 |

Privateer

33 | 34 |

Tack topgallant draft line flogging. Maroon overhaul grog blossom Privateer main sheet.

35 | 36 |

Provost me cackle fruit Corsair Cat o'nine tails. Hempen halter Davy Jones' Locker clipper bring a spring upon her cable run a shot across the bow.

37 | 38 |

Ahoy

39 | 40 |

Booty squiffy wench overhaul ahoy. Parrel Pirate Round long clothes long boat come about.

41 | 42 |

Squiffy jack crow's nest bilged on her anchor barkadeer. Snow bucko mizzen six pounders tack.

43 | 44 |

Man-of-War

45 | 46 |

Lee lad nipperkin avast pressgang. Man-of-war prow ho Sail ho landlubber or just lubber.

47 | 48 |

Ho no prey, no pay fire ship salmagundi capstan. Hail-shot doubloon wherry loaded to the gunwalls cutlass.

49 | 50 |

Corsair

51 | 52 |

Corsair chantey hardtack ahoy snow. Maroon cog galleon topmast tender.

53 | 54 |

Shiver Me Timbers

55 | 56 |

Galleon nipper Shiver me timbers lugger Nelsons folly. Wench ballast scurvy man-of-war hearties.

57 | 58 |

Poop deck clap of thunder Corsair grog hornswaggle. Dead men tell no tales brigantine long clothes black spot sutler.

59 | 60 |

Scurvy Dog

61 | 62 |

Jury mast Letter of Marque boatswain scurvy sheet. Jolly boat plunder jack starboard Pirate Round.

63 | 64 |

Holystone bring a spring upon her cable grog blossom deadlights league. Lanyard gabion reef sails booty gaff.

65 | 66 |

Sea Legs

67 | 68 |

Sea Legs to go on account skysail Yellow Jack heave down. Spanker heave down yawl starboard barque.

69 | 70 |

To go on account hulk swing the lead heave to tack. Fore fire in the hole prow run a rig Jack Ketch.

71 | 72 |

On the Quarterdeck

73 | 74 |

Tack chase red ensign league pinnace. Holystone quarterdeck me boatswain rope's end.

75 | 76 |

Sink me lanyard Pieces of Eight starboard black spot. Blimey heave down crimp mutiny matey.

77 | 78 |

Jolly Roger

79 | 80 |

Belay piracy come about jolly boat transom. Heave to gally snow Arr wherry.

81 | 82 |

Sutler Davy Jones' Locker ahoy walk the plank lugger. Jolly Roger matey hornswaggle Privateer marooned.

83 | 84 |

Davy Jones' Locker

85 | 86 |

Davy Jones' Locker jib trysail bowsprit heave down. Transom square-rigged clipper Jack Ketch chandler.

87 | 88 |

Square-rigged yawl execution dock sloop American Main. Six pounders red ensign lugger heave to dead men tell no tales.

89 | 90 |

Sloop

91 | 92 |

Spanker rutters Arr sloop pinnace. List crimp swab Blimey grog.

93 | 94 |

Shiver me timbers hulk black jack Pirate Round clap of thunder. Scuppers six pounders carouser spirits jib.

95 | 96 |

Swab

97 | 98 |

Spanker rutters Arr sloop pinnace. List crimp swab Blimey grog.

99 | 100 |

Shiver me timbers hulk black jack Pirate Round clap of thunder. Scuppers six pounders carouser spirits jib.

101 | 102 |
Grog
103 | 104 |

Spanker rutters Arr sloop pinnace. List crimp swab Blimey grog.

105 | 106 |

Shiver me timbers hulk black jack Pirate Round clap of thunder. Scuppers six pounders carouser spirits jib.

107 |
108 | 109 | 112 | 113 | 114 | 121 | 122 | -------------------------------------------------------------------------------- /table-of-contents.js: -------------------------------------------------------------------------------- 1 | /*! tableOfContents.js v1.0.0 | (c) 2020 Chris Ferdinandi | MIT License | http://github.com/cferdinandi/table-of-contents */ 2 | 3 | /* 4 | * Automatically generate a table of contents from the headings on the page 5 | * @param {String} content A selector for the element that the content is in 6 | * @param {String} target The selector for the container to render the table of contents into 7 | * @param {Object} options An object of user options [optional] 8 | */ 9 | var tableOfContents = function (content, target, options) { 10 | 11 | // 12 | // Variables 13 | // 14 | 15 | // Get content 16 | var contentWrap = document.querySelector(content); 17 | var toc = document.querySelector(target); 18 | if (!contentWrap || !toc) return; 19 | 20 | // Settings & Defaults 21 | var defaults = { 22 | levels: 'h2, h3, h4, h5, h6', 23 | heading: 'Table of Contents', 24 | headingLevel: 'h2', 25 | listType: 'ul' 26 | }; 27 | var settings = {}; 28 | 29 | // Placeholder for headings 30 | var headings; 31 | 32 | 33 | // 34 | // Methods 35 | // 36 | 37 | /** 38 | * Merge user options into defaults 39 | * @param {Object} obj The user options 40 | */ 41 | var merge = function (obj) { 42 | for (var key in defaults) { 43 | if (Object.prototype.hasOwnProperty.call(defaults, key)) { 44 | settings[key] = Object.prototype.hasOwnProperty.call(obj, key) ? obj[key] : defaults[key]; 45 | } 46 | } 47 | }; 48 | 49 | /** 50 | * Create an ID for a heading if one does not exist 51 | * @param {Node} heading The heading element 52 | */ 53 | var createID = function (heading) { 54 | if (heading.id.length) return; 55 | heading.id = 'toc_' + heading.textContent.replace(/[^A-Za-z0-9]/g, '-'); 56 | }; 57 | 58 | /** 59 | * Get the HTML to indent a list a specific number of levels 60 | * @param {Integer} count The number of times to indent the list 61 | * @return {String} The HTML 62 | */ 63 | var getIndent = function (count) { 64 | var html = ''; 65 | for (var i = 0; i < count; i++) { 66 | html += '<' + settings.listType + '>'; 67 | } 68 | return html; 69 | }; 70 | 71 | /** 72 | * Get the HTML to close an indented list a specific number of levels 73 | * @param {Integer} count The number of times to "outdent" the list 74 | * @return {String} The HTML 75 | */ 76 | var getOutdent = function (count) { 77 | var html = ''; 78 | for (var i = 0; i < count; i++) { 79 | html += ''; 80 | } 81 | return html; 82 | }; 83 | 84 | /** 85 | * Get the HTML string to start a new list of headings 86 | * @param {Integer} diff The number of levels in or out from the current level the list is 87 | * @param {Integer} index The index of the heading in the "headings" NodeList 88 | * @return {String} The HTML 89 | */ 90 | var getStartingHTML = function (diff, index) { 91 | 92 | // If indenting 93 | if (diff > 0) { 94 | return getIndent(diff); 95 | } 96 | 97 | // If outdenting 98 | if (diff < 0) { 99 | return getOutdent(Math.abs(diff)); 100 | } 101 | 102 | // If it's not the first item and there's no difference 103 | if (index && !diff) { 104 | return ''; 105 | } 106 | 107 | return ''; 108 | 109 | }; 110 | 111 | /** 112 | * Inject the table of contents into the DOM 113 | */ 114 | var injectTOC = function () { 115 | 116 | // Track the current heading level 117 | var level = headings[0].tagName.slice(1); 118 | var startingLevel = level; 119 | 120 | // Cache the number of headings 121 | var len = headings.length - 1; 122 | 123 | // Inject the HTML into the DOM 124 | toc.innerHTML = 125 | '<' + settings.headingLevel + '>' + settings.heading + '' + 126 | '<' + settings.listType + '>' + 127 | Array.prototype.map.call(headings, function (heading, index) { 128 | 129 | // Add an ID if one is missing 130 | createID(heading); 131 | 132 | // Check the heading level vs. the current list 133 | var currentLevel = heading.tagName.slice(1); 134 | var levelDifference = currentLevel - level; 135 | level = currentLevel; 136 | var html = getStartingHTML(levelDifference, index); 137 | 138 | // Generate the HTML 139 | html += 140 | '
  • ' + 141 | '' + 142 | heading.innerHTML.trim() + 143 | ''; 144 | 145 | // If the last item, close it all out 146 | if (index === len) { 147 | html += getOutdent(Math.abs(startingLevel - currentLevel)); 148 | } 149 | 150 | return html; 151 | 152 | }).join('') + 153 | ''; 154 | }; 155 | 156 | /** 157 | * Initialize the script 158 | */ 159 | var init = function () { 160 | 161 | // Merge any user settings into the defaults 162 | merge(options || {}); 163 | 164 | // Get the headings 165 | // If none are found, don't render a list 166 | headings = contentWrap.querySelectorAll(settings.levels); 167 | if (!headings.length) return; 168 | 169 | // Inject the table of contents 170 | injectTOC(); 171 | 172 | }; 173 | 174 | 175 | // 176 | // Initialize the script 177 | // 178 | 179 | init(); 180 | 181 | }; --------------------------------------------------------------------------------