eddiemf / vue-affix

A Vue.js plugin that affixes an element on the window while scrolling based on a relative element
MIT License
405 stars 38 forks source link

scroll-affix breaks when the affix dynamically grows #44

Open 719media opened 5 years ago

719media commented 5 years ago

if you scroll-affix something that is smaller height than viewport (no scroll needed), scroll down slightly, and then dynamically grow it such that it becomes taller than the viewport, you won't be able to get the affix to scroll until you reach the top or bottom of the main content.

To see issue, use this: https://jsfiddle.net/dhab1z3e/1/

Scroll down a little, then click the "SHOW MORE BUTTON", then continue scrolling. Note that the affix does not scroll.

It works correctly if you SHOW MORE before scrolling, or return back to the top so that the affix position resets to normal and then begin scrolling again.

eddiemf commented 5 years ago

Such a great description, thanks a lot for that, I'll take a look into the issue asap :)

719media commented 5 years ago

I don't think it should be the job of vue-affix to listen for height changes due to internal DOM changes. I mean, if you want to go down that route, great. However, alternatively, you could just have a method available to "refresh" the calculations.

I'm comparing your library to https://github.com/somewebmedia/hc-sticky They have a method called refresh which I can use when this happens.

Your library I tried calling onScroll, after my DOM change, which seems like the closest thing, but it did not work when the DOM changed (and the affix was partially scrolled).

I just wanted to propose a potential solution (basically to have a refresh method that is meant to be called anytime you want to recalibrate)

719media commented 5 years ago

Calling initScrollAffix followed by onScroll sort of fixes the problem... however, some jenk still occurs when you re-collapse, or open the content when it will have overspilled the bottom (e.g. scroll to the bottom of the reference, then open the content). example

https://jsfiddle.net/d178ogjh/1/

Artem-Schander commented 3 years ago

Hi everyone, I know it's an old issue, but I stumbled upon a similar one and I think I have a decent solution.

A small addition to the data object

data() {
  return {
    ro: null,
    // ...
  };
},

Two additional methods:

// kind of a workaround
stick() {
  this.lastState = null;
  this.initScrollAffix();
  this.onScroll();
},
unstick() {
  this.removeClasses();
  this.$el.style = {};
}

Add (and remove) a ResizeObserver in the lifecycle hooks.

mounted() {
  // ...

  this.ro = new ResizeObserver(entries => {
    if (! this.scrollContainer || ! this.relativeElement) return;

    for (let entry of entries) {
      const elHeight = entry.target.clientHeight;
      const fullHeight = this.relativeElement.clientHeight;
      const scrollHeight = this.scrollContainer.innerHeight || this.scrollContainer.clientHeight

      if (elHeight < fullHeight && fullHeight > scrollHeight) {
        this.stick();
      } else {
        this.unstick();
      }
    }
  });

  this.ro.observe(this.$el);
},

beforeDestroy() {
  this.ro.unobserve(this.$el);

  this.unstick();
  this.scrollContainer.removeEventListener('scroll', this.handleScroll);
}

Let me know, if there is any interest in a PR.