Make position: fixed behavior like sticky (for Vue2)
Asked Answered
N

2

8

Position: sticky doesn't support by the most mobile browsers. But position: fixed is not that thing I need (because of fixed block overlaps content in the bottom of document).

I guess for jquery it will be easy to set static position for fixed block if we get bottom of document onscroll.

But for Vue2 I haven't any idea how to do the same. Give some advice please. Or maybe better solution exists.

Neolamarckism answered 20/2, 2018 at 11:27 Comment(4)
Maybe npmjs.com/package/position-stickyEssieessinger
Or github.com/wilddeer/stickyfillEssieessinger
I don't think it will work because following libs required jquery, not VuejsNeolamarckism
I don't think they require jQuery. For stickyfill, you just need to call Stickyfill.add(elements); after your elements are created (in the mounted hook, most likely).Essieessinger
E
15

As I mentioned in the comments, I'd recommend using a polyfill if at all possible. They will have put a lot of effort into getting it right. However, here is a simple take on how you might do it in Vue.

I have the application handle scroll events by putting the scrollY value into a data item. My sticky-top component calculates what its fixed top position would be, and if it's > 0, it uses it. The widget is position: relative.

new Vue({
  el: '#app',
  data: {
    scrollY: null
  },
  mounted() {
    window.addEventListener('scroll', (event) => {
      this.scrollY = Math.round(window.scrollY);
    });
  },
  components: {
    stickyTop: {
      template: '<div class="a-box" :style="myStyle"></div>',
      props: ['top', 'scrollY'],
      data() {
        return {
          myStyle: {},
          originalTop: 0
        }
      },
      mounted() {
        this.originalTop = this.$el.getBoundingClientRect().top;
      },
      watch: {
        scrollY(newValue) {
          const rect = this.$el.getBoundingClientRect();
          const newTop = this.scrollY + +this.top - this.originalTop;

          if (newTop > 0) {
            this.$set(this.myStyle, 'top', `${newTop}px`);
          } else {
            this.$delete(this.myStyle, 'top');
          }
        }
      }
    }
  }
});
#app {
  height: 1200px;
}

.spacer {
  height: 80px;
}

.a-box {
  display: inline-block;
  height: 5rem;
  width: 5rem;
  border: 2px solid blue;
  position: relative;
}
<script src="//unpkg.com/vue@latest/dist/vue.js"></script>
<div id="app">
  <div class="spacer"></div>
  <div class="a-box"></div>
  <sticky-top top="20" :scroll-y="scrollY"></sticky-top>
  <div class="a-box"></div>
</div>
Essieessinger answered 21/2, 2018 at 2:52 Comment(2)
@Boern Why did you revert the answer to include variables that are not used?Essieessinger
haha :) cause there was more and I did neither want to publish a incomplete revision nor did I have the confidence to revise the majority of your code (JS noob here) without having your OK. Also, i did not want to submit my own solution and steal your rep.Coyne
T
0

This seem to work for me

...

  <header
    ref="header"
    class="header-container"
    :class="{ 'header-container--sticky': isHeaderSticky }"
  >

...

...

data() {
    return{
      scrollY: null,
      headerTop: 0,
      isHeaderSticky: false,
    }
  },
  mounted() {
    window.addEventListener('load', () => {
      window.addEventListener('scroll', () => {
        this.scrollY = Math.round(window.scrollY);
      });
      this.headerTop = this.$refs.header.getBoundingClientRect().top;
    });
  },
  watch: {
    scrollY(newValue) {
      if (newValue > this.headerTop) {
        this.isHeaderSticky = true;
      } else {
        this.isHeaderSticky = false;
      }
    }
  }

...

...

.header-container {
      &--sticky {
        position: sticky;
        top: 0;
        z-index: 9999;
      }
    }

...

Tenner answered 25/2, 2021 at 6:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.