@@ -31,16 +31,25 @@ const registerOnScrollEvent = (function(){
31
31
// to their initial state.
32
32
33
33
const $window = $ ( window ) ;
34
- const $menu = $ ( '.wy-menu-vertical' ) ;
35
- const $search = $ ( '.wy-side-nav-search' ) ;
36
- const $ethical = $ ( '.ethical-rtd' ) ;
34
+ const $sidebar = $ ( '.wy-side-scroll' ) ;
35
+ const $search = $sidebar . children ( '.wy-side-nav-search' ) ;
36
+ const $menu = $sidebar . children ( '.wy-menu-vertical' ) ;
37
+ const $ethical = $sidebar . children ( '.ethical-rtd' ) ;
38
+
39
+ // This padding is needed to correctly adjust the height of the scrollable area in the sidebar.
40
+ // It has to have the same height as the ethical block, if there is one.
41
+ let $menuPadding = $menu . children ( '.wy-menu-ethical-padding' ) ;
42
+ if ( $menuPadding . length == 0 ) {
43
+ $menuPadding = $ ( '<div class="wy-menu-ethical-padding"></div>' ) ;
44
+ $menu . append ( $menuPadding ) ;
45
+ }
37
46
38
47
if ( mediaQuery . matches ) {
39
48
// Entering the "desktop" state.
40
49
41
- // The scroll event handler.
50
+ // The main scroll event handler.
42
51
// Executed as the page is scrolled and once immediatelly as the page enters this state.
43
- const handleScroll = ( currentScroll ) => {
52
+ const handleMainScroll = ( currentScroll ) => {
44
53
if ( currentScroll >= scrollTopPixels ) {
45
54
// After the page is scrolled below the threshold, we fix everything in place.
46
55
$search . css ( 'margin-top' , `-${ scrollTopPixels } px` ) ;
@@ -63,24 +72,13 @@ const registerOnScrollEvent = (function(){
63
72
}
64
73
} ;
65
74
66
- $search . addClass ( 'fixed' ) ;
67
- $ethical . addClass ( 'fixed' ) ;
68
-
69
- // Adjust the inner height of navigation so that the banner can be overlaid there later.
70
- const ethicalOffsetBottom = $ethical . height ( ) || 0 ;
71
- if ( ethicalOffsetBottom ) {
72
- $menu . css ( 'padding-bottom' , `${ ethicalOffsetBottom } px` ) ;
73
- } else {
74
- $menu . css ( 'padding-bottom' , `0px` ) ;
75
- }
76
-
77
- $window . scroll ( function ( ) {
78
- handleScroll ( window . scrollY ) ;
79
- } ) ;
80
-
81
- $menu . scroll ( function ( ) {
82
- const menuScrollTop = $ ( this ) . scrollTop ( ) ;
83
- const menuScrollBottom = this . scrollHeight - ( menuScrollTop + this . offsetHeight ) ;
75
+ // The sidebar scroll event handler.
76
+ // Executed as the sidebar is scrolled as well as after the main scroll. This is needed
77
+ // because the main scroll can affect the scrollable area of the sidebar.
78
+ const handleSidebarScroll = ( ) => {
79
+ const menuElement = $menu . get ( 0 ) ;
80
+ const menuScrollTop = $menu . scrollTop ( ) ;
81
+ const menuScrollBottom = menuElement . scrollHeight - ( menuScrollTop + menuElement . offsetHeight ) ;
84
82
85
83
// As the navigation is scrolled we add a shadow to the top bar hanging over it.
86
84
if ( menuScrollTop > 0 ) {
@@ -95,9 +93,30 @@ const registerOnScrollEvent = (function(){
95
93
} else {
96
94
$ethical . css ( 'margin-top' , '0px' ) ;
97
95
}
96
+ } ;
97
+
98
+ $search . addClass ( 'fixed' ) ;
99
+ $ethical . addClass ( 'fixed' ) ;
100
+
101
+ // Adjust the inner height of navigation so that the banner can be overlaid there later.
102
+ const ethicalOffsetBottom = $ethical . height ( ) || 0 ;
103
+ if ( ethicalOffsetBottom ) {
104
+ $menuPadding . css ( 'height' , `${ ethicalOffsetBottom } px` ) ;
105
+ } else {
106
+ $menuPadding . css ( 'height' , `0px` ) ;
107
+ }
108
+
109
+ $window . scroll ( function ( ) {
110
+ handleMainScroll ( window . scrollY ) ;
111
+ handleSidebarScroll ( ) ;
112
+ } ) ;
113
+
114
+ $menu . scroll ( function ( ) {
115
+ handleSidebarScroll ( ) ;
98
116
} )
99
117
100
- handleScroll ( window . scrollY ) ;
118
+ handleMainScroll ( window . scrollY ) ;
119
+ handleSidebarScroll ( ) ;
101
120
} else {
102
121
// Entering the "mobile" state.
103
122
@@ -109,15 +128,32 @@ const registerOnScrollEvent = (function(){
109
128
110
129
$search . css ( 'margin-top' , `0px` ) ;
111
130
$menu . css ( 'margin-top' , `0px` ) ;
112
- $menu . css ( 'padding-bottom' , `0px` ) ;
113
131
$menu . css ( 'max-height' , 'initial' ) ;
132
+ $menuPadding . css ( 'height' , `0px` ) ;
114
133
$ethical . css ( 'margin-top' , '0px' ) ;
115
134
}
116
135
}
117
136
} ) ( ) ;
118
137
119
138
$ ( document ) . ready ( ( ) => {
139
+ const sidebarContainer = document . querySelector ( '.wy-side-scroll' ) ;
120
140
const mediaQuery = window . matchMedia ( 'only screen and (min-width: 769px)' ) ;
141
+
142
+ // Subscribe to DOM changes in the sidebar container, because there is a
143
+ // banner that gets added at a later point, that we might not catch otherwise.
144
+ const observerConfig = { childList : true } ;
145
+ const observerCallback = ( mutationsList , observer ) => {
146
+ for ( let mutation of mutationsList ) {
147
+ if ( mutation . type !== 'childList' ) {
148
+ continue ;
149
+ }
150
+
151
+ registerOnScrollEvent ( mediaQuery ) ;
152
+ }
153
+ } ;
154
+ const observer = new MutationObserver ( observerCallback ) ;
155
+ observer . observe ( sidebarContainer , observerConfig ) ;
156
+
121
157
registerOnScrollEvent ( mediaQuery ) ;
122
158
mediaQuery . addListener ( registerOnScrollEvent ) ;
123
159
} ) ;
0 commit comments