1
\$\begingroup\$

Starting out with VueJS, making a small fun project. The project is a simple static portfolio website, with a navigation bar at the top and a bootstrap scrollspy, jumping to the different sections (I took this template as inspiration for the looks).

Here is the problem:

The top of the page in my case has a background image covering the whole viewport. I find it looks quite good in my case to add a bit of opacity to the navbar (which is fixed to the top in order to be always visible). However, when scrolling down, text from the content is interfering with the logo and text from the navbar, so I want to switch to a solid background.

Here is my solution:

I wanted to dynamically switch the bootstrap class bg-opacity-75 used in the navbar on and of based on the events fired by the scrollspy. Since I have every section in my scrollable website built as a separate vue component I needed to have a "global" state of the background. For this I read and combined information from the bootstrap and vuejs documentation.

Here is what my App.vue looks like:

<template>
  <NavigationComponent />
  <div
    data-bs-spy="scroll"
    data-bs-target="#navbar-main"
    data-bs-root-margin="0px 0px -40%"
    data-bs-smooth-scroll="true"
    tabindex="0"
  >
    <HomeComponent />
    <UnternehmenComponenet />
    <LeistungenComponent />
    <ReferenzenComponent />
    <KontaktComponent />
  </div>
</template>
  

<script setup>
  import NavigationComponent from './components/NavigationComponent.vue'
  import HomeComponent from './components/HomeComponenet.vue'
  import UnternehmenComponenet from './components/UnternehmenComponenet.vue'
  import LeistungenComponent from './components/LeistungenComponent.vue'
  import ReferenzenComponent from './components/ReferenzenComponent.vue'
  import KontaktComponent from './components/KontaktComponent.vue'


  import { onMounted } from 'vue'
  import {store} from './assets/js/store.js'

  onMounted(() => 
  {
    const firstScrollSpyEl = document.querySelector('[data-bs-spy="scroll"]')
    firstScrollSpyEl.addEventListener('activate.bs.scrollspy', (e) => {
      if(e.relatedTarget.text){
        store.isNavbarBackgroundTransparent = false;
      }
      else {
        store.isNavbarBackgroundTransparent = true;
      }
    })
  });
</script>

Each of the components contains a div which the id for the scroll target.

store.js is just simple file containing my global state:

import { reactive } from 'vue'

export const store = reactive({ isNavbarBackgroundTransparent: true })

The navigation component contains the navbar with the scrollspy:

<template>
  <nav
    id="navbar-main"
    class="navbar navbar-nav-scroll navbar-expand-lg fixed-top border-bottom bg-light"
    :class="{
      'bg-opacity-75': store.isNavbarBackgroundTransparent,
    }"
  >
    <div class="container">
      <a
        class="navbar-brand"
        href="#Home" 
      > 
        <img
          class="main-logo"
          src="/src/assets/img/logo.png"
        >
      </a>
      <button
        class="navbar-toggler"
        type="button"
        data-bs-toggle="collapse"
        data-bs-target="#navbarCollapse"
        aria-controls="navbarCollapse"
        aria-expanded="false"
        aria-label="Toggle navigation"
      >
        <span class="navbar-toggler-icon" />
      </button>
      <div
        id="navbarCollapse"
        class="collapse navbar-collapse"
      >
        <div class="navbar-nav">
          <a
            class="nav-link"
            href="#Unternehmen"
          >Unternehmen</a>
          <a
            class="nav-link"
            href="#Leistungen"
          >Leistungen</a>
          <a
            class="nav-link"
            href="#Referenzen"
          >Referenzen</a>
          <a
            class="nav-link"
            href="#Kontakt"
          >Kontakt</a>
        </div>
      </div>
    </div>
  </nav>
</template>
  
<script setup>
    import {store} from '../assets/js/store.js'
</script> 

Where/why am I unsure about the solution

The solution works quite fine; there are no issues really. I am just not sure, because I have read some docs and forum posts, that using vanilla events outside of the respective frameworks might lead to some issues. On the other hand I have also read some posts, that with bootstrap 5 this is not the case anymore. I initially wanted to use the event system provided by Vuejs directly but after searching the docs for quite a while, I gave up.

\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

There is always a chance that the event name could change in future versions though that should be documented in a log of changes.

While it hasn’t been updated to use bootstrap 5, there is a bootstrap-vue library with a Scrollspy directive v-b-scrollspy. The documentation shows how event listeners can be registered in the Events section

Events

Whenever a target is activated, the event bv:scrollspy::activate is emitted on $root with the target's ID as the argument (i.e. #bar)

const app = new Vue({
  el: '#app',
  created() {
    this.$root.$on('bv::scrollspy::activate', this.onActivate)
  },
  methods: {
    onActivate(target) {
      console.log('Received event: "bv::scrollspy::activate" for target ', target)
    }
  }
})

One could explore using an approach similar to that with this.$root.$on() in the onMounted() callback instead of using addEventListener().

\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.