@@ -22,58 +22,71 @@ function throttle(fn, interval) {
2222 } ;
2323}
2424
25+ function scrollNavbar ( ) {
26+ const scrollPosition = document . documentElement . scrollTop ;
27+
28+ //Navbar Clone
29+ const navbarClone = document . getElementById ( "navbar-clone" ) ;
30+
31+ // Make navbar sticky, by activating a second, duplicate navbar
32+ // that is fixed to the top of the screen.
33+ navbarClone . classList . toggle ( "is-active" , scrollPosition > 50 ) ;
34+ }
35+
2536// Highlight currently scrolled to header in shortcuts
2637// Based on https://stackoverflow.com/a/32396543/214686
2738// and
2839// https://stackoverflow.com/a/57494988/214686
2940// which fixes some issues with the first, particularly
3041// around scrolling upward.
3142function scrollHeadersAndNavbar ( ) {
32- var scrollPosition = $ ( window ) . scrollTop ( ) ;
33- var headers = $ ( ":header[id]" ) ;
34- var allShortcuts = $ ( "#shortcuts > div" ) ;
43+ scrollNavbar ( ) ;
3544
36- //Navbar Clone
37- if ( scrollPosition > 50 ) {
38- $ ( "#navbar-clone" ) . addClass ( "is-active" ) ;
39- } else {
40- $ ( "#navbar-clone" ) . removeClass ( "is-active" ) ;
41- }
45+ const scrollPosition = document . documentElement . scrollTop ;
46+ const headers = Array . from (
47+ document . querySelectorAll ( ":is(h1, h2, h3, h4, h5, h6)[id]" ) ,
48+ ) ;
49+ const allShortcuts = Array . from (
50+ document . querySelectorAll ( "#shortcuts > div" ) ,
51+ ) ;
4252
43- headers . each ( function ( ) {
44- var currentSection = $ ( this ) ;
53+ headers . map ( ( currentSection ) => {
4554 // get the position of the section
46- var sectionTop = currentSection . position ( ) . top ;
47- var sectionHeight = currentSection . height ( ) ;
55+ // emulates JQuery's .position().top
56+ const marginTop = parseInt ( getComputedStyle ( currentSection ) . marginTop , 10 ) ;
57+ var sectionTop = currentSection . offsetTop - marginTop ;
58+ var sectionHeight = currentSection . getBoundingClientRect ( ) . height ;
4859 var overall = scrollPosition + sectionHeight ;
4960 var headerOffset = remToPx ( 4 ) ;
5061
5162 if ( scrollPosition < headerOffset ) {
52- allShortcuts . removeClass ( "active" ) ;
53- return false ;
63+ allShortcuts . map ( ( shortcut ) => shortcut . classList . remove ( "active" ) ) ;
64+ return ;
5465 }
5566
5667 // user has scrolled over the top of the section
5768 if (
5869 scrollPosition + headerOffset >= sectionTop &&
5970 scrollPosition < overall
6071 ) {
61- var id = currentSection . attr ( "id" ) ;
62- var shortcut = $ ( `# ${ id } -shortcut`) ;
63- if ( shortcut . length && ! shortcut . hasClass ( "active" ) ) {
64- allShortcuts . removeClass ( "active" ) ;
65- shortcut . addClass ( "active" ) ;
72+ const id = currentSection . id ;
73+ const shortcut = document . getElementById ( ` ${ id } -shortcut`) ;
74+ if ( shortcut ) {
75+ allShortcuts . map ( ( shortcut ) => shortcut . classList . remove ( "active" ) ) ;
76+ shortcut . classList . add ( "active" ) ;
6677 }
6778 }
6879 } ) ;
6980}
7081
82+ const throttledScrollHeadersAndNavbar = throttle ( scrollHeadersAndNavbar , 100 ) ;
83+
7184function bindScroll ( ) {
72- $ ( window ) . scroll ( throttle ( scrollHeadersAndNavbar , 100 ) ) ;
85+ window . addEventListener ( "scroll" , throttledScrollHeadersAndNavbar ) ;
7386}
7487
7588function unbindScroll ( ) {
76- $ ( window ) . unbind ( "scroll" ) ;
89+ window . removeEventListener ( "scroll" , throttledScrollHeadersAndNavbar ) ;
7790}
7891
7992function remToPx ( rem ) {
@@ -93,53 +106,77 @@ function setupShortcuts(shortcutDepth = 2) {
93106 }
94107
95108 // Content Page Shortcuts
96- const shortcutsTarget = $ ( "#shortcuts" ) ;
97- if ( shortcutsTarget . length > 0 ) {
98- $ ( classes ) . map ( function ( idx , el ) {
109+ const shortcutsTarget = document . getElementById ( "shortcuts" ) ;
110+ if ( shortcutsTarget ) {
111+ const classElements = Array . from ( document . querySelectorAll ( classes ) ) ;
112+ classElements . map ( ( el ) => {
99113 const title = el . textContent ;
100114 const elId = el . id ;
101115 // Gets the element type (e.g. h2, h3)
102- const elType = $ ( el ) . get ( 0 ) . tagName ;
116+ const elType = el . tagName ;
103117 // Adds snake-case title as an id attribute to target element
104- shortcutsTarget . append (
118+ shortcutsTarget . insertAdjacentHTML (
119+ "beforeend" ,
105120 `<div id="${ elId } -shortcut" class="shortcuts-${ elType } " href="#${ elId } ">${ title } </div>` ,
106121 ) ;
107122
108- const shortcut = $ ( `#${ elId } -shortcut` ) ;
109- shortcut . click ( function ( ) {
123+ const shortcut = document . getElementById ( `${ elId } -shortcut` ) ;
124+ shortcut . addEventListener ( "click" , ( ) => {
125+ event . preventDefault ( ) ;
126+
110127 // We don't want the shortcuts to flash through highlights while
111128 // we scroll to the desired header
112129 unbindScroll ( ) ;
113130
114131 // Replace what's in the location bar, without changing browser history
115132 // and without triggering a page scroll
116133 history . replaceState ( null , null , `#${ elId } ` ) ;
117-
118- let distance = $ ( `#${ elId } ` ) . offset ( ) . top - 60 ;
119- $ ( [ document . documentElement , document . body ] ) . animate (
120- {
121- scrollTop : distance ,
122- } ,
123- 300 ,
124- null ,
125- function ( ) {
126- $ ( "#shortcuts > div" ) . removeClass ( "active" ) ;
127- shortcut . addClass ( "active" ) ;
128-
129- // Done moving to clicked header; re-enable
130- // scroll highlighting of shortcuts
131- bindScroll ( ) ;
132- } ,
134+ const shortcutDivs = Array . from (
135+ document . querySelectorAll ( "#shortcuts > div" ) ,
133136 ) ;
137+ shortcutDivs . forEach ( ( e ) => e . classList . remove ( "active" ) ) ;
138+ shortcut . classList . add ( "active" ) ;
139+
140+ let headerOffset = el . offsetTop - 60 ;
141+ scrollToThen ( headerOffset , ( ) => {
142+ // Done moving to clicked header; re-enable
143+ // scroll highlighting of shortcuts
144+ bindScroll ( ) ;
145+
146+ // After scroll, display the navbar, if necessary
147+ scrollNavbar ( ) ;
148+ } ) ;
134149 } ) ;
135150 } ) ;
136151 }
137152
138153 // Removes the shortcuts container if no shortcuts exist.
139154 // Also removes the 'Get Help' link.
140- if ( $ ( "#shortcuts div" ) . length < 1 ) {
141- $ ( ".shortcuts-container" ) . css ( "display" , "none" ) ;
155+ const shortcuts = Array . from ( document . querySelectorAll ( "#shortcuts div" ) ) ;
156+ if ( shortcuts . length == 0 ) {
157+ const shortcutsContainer = document . getElementById ( "shortcuts-container" ) ;
158+ shortcutsContainer . style . display = "none" ;
142159 }
143160
144161 bindScroll ( ) ;
145162}
163+
164+ /**
165+ * Modified from https://stackoverflow.com/a/55686711/214686
166+ */
167+ function scrollToThen ( offset , callback ) {
168+ const onScroll = throttle ( ( ) => {
169+ const fixedOffset = offset . toFixed ( ) ;
170+ if ( window . pageYOffset . toFixed ( ) === fixedOffset ) {
171+ window . removeEventListener ( "scroll" , onScroll ) ;
172+ callback ( ) ;
173+ }
174+ } , 100 ) ;
175+
176+ window . addEventListener ( "scroll" , onScroll ) ;
177+ onScroll ( ) ;
178+ window . scrollTo ( {
179+ top : offset ,
180+ /* behavior: 'smooth' */ /* too slow? */
181+ } ) ;
182+ }
0 commit comments