custom.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. // Handle page scroll and adjust sidebar accordingly.
  2. // Each page has two scrolls: the main scroll, which is moving the content of the page;
  3. // and the sidebar scroll, which is moving the navigation in the sidebar.
  4. // We want the logo to gradually disappear as the main content is scrolled, giving
  5. // more room to the navigation on the left. This means adjusting the height
  6. // available to the navigation on the fly. There is also a banner below the navigation
  7. // that must be dealt with simultaneously.
  8. const registerOnScrollEvent = (function(){
  9. // Configuration.
  10. // The number of pixels the user must scroll by before the logo is completely hidden.
  11. const scrollTopPixels = 234;
  12. // The target margin to be applied to the navigation bar when the logo is hidden.
  13. const menuTopMargin = 90;
  14. // The max-height offset when the logo is completely visible.
  15. const menuHeightOffset_default = 338;
  16. // The max-height offset when the logo is completely hidden.
  17. const menuHeightOffset_fixed = 102;
  18. // The distance between the two max-height offset values above; used for intermediate values.
  19. const menuHeightOffset_diff = (menuHeightOffset_default - menuHeightOffset_fixed);
  20. // Media query handler.
  21. return function(mediaQuery) {
  22. // We only apply this logic to the "desktop" resolution (defined by a media query at the bottom).
  23. // This handler is executed when the result of the query evaluation changes, which means that
  24. // the page has moved between "desktop" and "mobile" states.
  25. // When entering the "desktop" state, we register scroll events and adjust elements on the page.
  26. // When entering the "mobile" state, we clean up any registered events and restore elements on the page
  27. // to their initial state.
  28. const $window = $(window);
  29. const $menu = $('.wy-menu-vertical');
  30. const $search = $('.wy-side-nav-search');
  31. const $ethical = $('.ethical-rtd');
  32. if (mediaQuery.matches) {
  33. // Entering the "desktop" state.
  34. // The scroll event handler.
  35. // Executed as the page is scrolled and once immediatelly as the page enters this state.
  36. const handleScroll = (currentScroll) => {
  37. if (currentScroll >= scrollTopPixels) {
  38. // After the page is scrolled below the threshold, we fix everything in place.
  39. $search.css('margin-top', `-${scrollTopPixels}px`);
  40. $menu.css('margin-top', `${menuTopMargin}px`);
  41. $menu.css('max-height', `calc(100% - ${menuHeightOffset_fixed}px)`);
  42. }
  43. else {
  44. // Between the top of the page and the threshold we calculate intermediate values
  45. // to guarantee a smooth transition.
  46. $search.css('margin-top', `-${currentScroll}px`);
  47. $menu.css('margin-top', `${menuTopMargin + (scrollTopPixels - currentScroll)}px`);
  48. if (currentScroll > 0) {
  49. const scrolledPercent = (scrollTopPixels - currentScroll) / scrollTopPixels;
  50. const offsetValue = menuHeightOffset_fixed + menuHeightOffset_diff * scrolledPercent;
  51. $menu.css('max-height', `calc(100% - ${offsetValue}px)`);
  52. } else {
  53. $menu.css('max-height', `calc(100% - ${menuHeightOffset_default}px)`);
  54. }
  55. }
  56. };
  57. $search.addClass('fixed');
  58. $ethical.addClass('fixed');
  59. // Adjust the inner height of navigation so that the banner can be overlaid there later.
  60. const ethicalOffsetBottom = $ethical.height() || 0;
  61. if (ethicalOffsetBottom) {
  62. $menu.css('padding-bottom', `${ethicalOffsetBottom}px`);
  63. } else {
  64. $menu.css('padding-bottom', `0px`);
  65. }
  66. $window.scroll(function() {
  67. handleScroll(window.scrollY);
  68. });
  69. $menu.scroll(function() {
  70. const menuScrollTop = $(this).scrollTop();
  71. const menuScrollBottom = this.scrollHeight - (menuScrollTop + this.offsetHeight);
  72. // As the navigation is scrolled we add a shadow to the top bar hanging over it.
  73. if (menuScrollTop > 0) {
  74. $search.addClass('fixed-and-scrolled');
  75. } else {
  76. $search.removeClass('fixed-and-scrolled');
  77. }
  78. // Near the bottom we start moving the sidebar banner into view.
  79. if (menuScrollBottom < ethicalOffsetBottom) {
  80. $ethical.css('margin-top', `-${ethicalOffsetBottom - menuScrollBottom}px`);
  81. } else {
  82. $ethical.css('margin-top', '0px');
  83. }
  84. })
  85. handleScroll(window.scrollY);
  86. } else {
  87. // Entering the "mobile" state.
  88. $window.unbind('scroll');
  89. $menu.unbind('scroll');
  90. $search.removeClass('fixed');
  91. $ethical.removeClass('fixed');
  92. $search.css('margin-top', `0px`);
  93. $menu.css('margin-top', `0px`);
  94. $menu.css('padding-bottom', `0px`);
  95. $menu.css('max-height', 'initial');
  96. $ethical.css('margin-top', '0px');
  97. }
  98. }
  99. })();
  100. $(document).ready(() => {
  101. const mediaQuery = window.matchMedia('only screen and (min-width: 769px)');
  102. registerOnScrollEvent(mediaQuery);
  103. mediaQuery.addListener(registerOnScrollEvent);
  104. });