diff --git a/framework/core/js/src/forum/components/AffixedSidebar.js b/framework/core/js/src/forum/components/AffixedSidebar.js
index 3cba6e88ef..dbf0750d43 100644
--- a/framework/core/js/src/forum/components/AffixedSidebar.js
+++ b/framework/core/js/src/forum/components/AffixedSidebar.js
@@ -1,15 +1,23 @@
import Component from '../../common/Component';
/**
- * The `AffixedSidebar` component uses Bootstrap's "affix" plugin to keep a
+ * Calculate the height of element with margin.
+ * @param {HTMLElement} element The element.
+ * @returns {number} The height of element with margin.
+ */
+function heightWithMargin(element) {
+ const style = getComputedStyle(element);
+ return element.getBoundingClientRect().height + parseInt(style.marginBottom, 10) + parseInt(style.marginTop, 10);
+}
+
+/**
+ * The `AffixedSidebar` component uses sticky position to keep a
* sidebar navigation at the top of the viewport when scrolling.
*
* ### Children
*
* The component must wrap an element that itself wraps an
element, which
* will be "affixed".
- *
- * @see https://getbootstrap.com/docs/3.4/javascript/#affix
*/
export default class AffixedSidebar extends Component {
view(vnode) {
@@ -19,35 +27,36 @@ export default class AffixedSidebar extends Component {
oncreate(vnode) {
super.oncreate(vnode);
- // Register the affix plugin to execute on every window resize (and trigger)
+ // Register the affix to execute on every window resize (and trigger)
this.boundOnresize = this.onresize.bind(this);
- $(window).on('resize', this.boundOnresize).resize();
+ window.addEventListener('resize', this.boundOnresize);
+ window.dispatchEvent(new Event('resize'));
}
onremove(vnode) {
super.onremove(vnode);
- $(window).off('resize', this.boundOnresize);
+ window.removeEventListener('resize', this.boundOnresize);
}
onresize() {
- const $sidebar = this.$();
- const $header = $('#header');
- const $footer = $('#footer');
- const $affixElement = $sidebar.find('> ul');
-
- $(window).off('.affix');
- $affixElement.removeClass('affix affix-top affix-bottom').removeData('bs.affix');
+ const header = document.getElementById('header');
+ const affixElement = this.element.querySelector(':scope > ul');
+ const pageSidebar = this.element.closest('.Page-sidebar');
// Don't affix the sidebar if it is taller than the viewport (otherwise
// there would be no way to scroll through its content).
- if ($sidebar.outerHeight(true) > $(window).height() - $header.outerHeight(true)) return;
-
- $affixElement.affix({
- offset: {
- top: () => $sidebar.offset().top - $header.outerHeight(true) - parseInt($sidebar.css('margin-top'), 10),
- bottom: () => (this.bottom = $footer.outerHeight(true)),
- },
- });
+ const enabled = heightWithMargin(this.element) <= window.innerHeight - heightWithMargin(header);
+ affixElement.classList.toggle('affix', enabled);
+ if (enabled) {
+ const top = heightWithMargin(header) + parseInt(getComputedStyle(pageSidebar ?? this.element).marginTop, 10);
+ affixElement.style.position = 'sticky';
+ affixElement.style.top = top + 'px';
+ this.element.style.display = 'initial'; // Workaround for sticky not working
+ } else {
+ affixElement.style.position = '';
+ affixElement.style.top = '';
+ this.element.style.display = '';
+ }
}
}