Following this answer to a similar question Make position: fixed behavior like sticky (for Vue2), I have tried to implement it in my application.
The solution was a little bit buggy (in some cases it behaved oddly, especially when opening other tabs and coming back), so I decided to implement it using jQuery and it's working as expected.
Here is the working example:
<template>
<div>
<div class="recap">
<div class="inner" :style="recapStyle">
</div>
</div>
</div>
</template>
<script>
export default {
name: 'ProductRecap',
data() {
return {
scrollY: null,
top: null,
bottom: null,
marginTop: 40,
recapStyle: {},
};
},
methods: {
updatePosition(scroll) {
// using jQuery to calculate amount
const offset = $(this.$el).offset().top;
const scrollAmount = offset - scroll;
const rectHeight = $(this.$el).find('.inner').outerHeight();
if (scrollAmount < this.top) {
let updatedTop = scroll - offset + this.top;
if ((scroll + rectHeight) < this.bottom) {
this.prevScroll = updatedTop;
} else {
updatedTop = this.prevScroll;
}
this.$set(this.recapStyle, 'top', `${updatedTop}px`);
} else {
this.$delete(this.recapStyle, 'top');
}
},
},
watch: {
scrollY(scrollUpdate) {
// call `updatePosition` on scroll
this.updatePosition(scrollUpdate);
},
},
mounted() {
// calculate header size (position: fixed) and add a fixed offset
this.top = $('#main-header').outerHeight() + this.marginTop;
// calculate height of the document (without the footer)
this.bottom = document.querySelector('.global-container').offsetHeight;
// update scrollY position
window.addEventListener('scroll', _.throttle(() => {
this.scrollY = Math.round(window.scrollY);
}, 20, { leading: true }));
},
};
</script>
However I'd like to find a solution that doesn't use jQuery to calculate the offset, so I headed to You Might Not Need jQuery, but if I just replace the offset
part with the one that's suggested it's still a bit buggy.
$(el).offset();
Should become:
var rect = el.getBoundingClientRect();
{
top: rect.top + document.body.scrollTop,
left: rect.left + document.body.scrollLeft
}
So I replaced the line:
const offset = $(this.$el).offset().top;
with:
const rect = this.$el.getBoundingClientRect();
const offset = rect.top + document.body.scrollTop;
But the distance of the sidebar from the fixed header increases with the scroll: can anyone explain how to fix it?
Here is a working fiddle (slightly simplified): Fiddle