Search code examples
cssvue.jsbootstrap-vue

How specify same href link for active class in navbar in bootstrap-vue?


I use this navbar. And it's ok when href links are different. But when two pages have the same link, active class appears for both li with that link in the way I write.

Vue.component('navbar', {
  template: `<svg xmlns="http://www.w3.org/2000/svg" width="15.352" height="15.355" viewBox="0 0 15.352 15.355">
        <path id="Union_19" data-name="Union 19" d="M-19.5-15958.5l-7.5,7.5,7.5-7.5-7.5-7.5,7.5,7.5,7.5-7.5-7.5,7.5,7.5,7.5Z" transform="translate(27.176 15966.178)" fill="none" stroke="#bbb" stroke-width="0.5"/>
    </svg>`
})

new Vue({
  el: "#app",
  data() {
      return {
        menu: [
        { to: { name: 'link1' }, name: 'page1' },
        { to: { name: 'sameLink' }, name: 'page2' },
        { to: { name: 'sameLink' }, name: 'page3' },
        { to: { name: 'link2' }, name: 'page4' }
        ]
      }
    }
});
.nav {
  display: flex;
  flex-wrap: nowrap;
  margin-bottom: 0;
  list-style: none;
  font-size: .875rem;
  overflow: auto;
  background-color: $navBar-bg;
}

.navLink {
  display: block;
  color: $navBar-color;
  text-decoration: none;
  position: relative;
  white-space: nowrap;

  @include hover-focus() {
    text-decoration: none;
    color: $navBar-hover-color;
  }

  &.disabled {
    color: $navBar-disabled-color;
    pointer-events: none;
    cursor: default;
  }

  :global(.show) > &,
  :global(.active) > &,
  .nav-link:global(.show),
  .nav-link:global(.active) {
    color: red;

    &:after {
      content: '';
      display: block;
      position: absolute;
      left: 0;
      right: 0;
      bottom: 0;
      height: .1875rem;
      background-color: currentColor;
    }
  }
}
<link type="text/css" rel="stylesheet" href="https://unpkg.com/bootstrap@4.5.3/dist/css/bootstrap.min.css" />
<link type="text/css" rel="stylesheet" href="https://unpkg.com/bootstrap-vue@2.21.2/dist/bootstrap-vue.css" />

<script src="https://unpkg.com/vue@2.6.12/dist/vue.min.js"></script>
<script src="https://unpkg.com/bootstrap-vue@2.21.2/dist/bootstrap-vue.min.js"></script>


<div id="app">
  <ul class="nav">
   <router-link
      v-for="{ to, name } in menu"
      v-slot="{ href: linkHref, navigate, isActive, isExactActive }"
      :key="to.name || name"
      :to="to"
    >
      <li v-else :class="['nav-item', isActive && 'active', isExactActive && 'exact-active']">
        <a
          v-t="name"
          href="linkHref"
          class="navLink"
        />
      </li>
    </router-link>
  </ul>
</div>

How can I have sameLink for pages but be active on only the page is shown and really active? These pages are different in content because of some filters but both have the same link.


Solution

  • The properties isActive and isExactActive will be the same for router-links that point to the same route, which is the case for menu items page2and page3 in your example. You could define these as different routes pointing to the same router-view component

    const routes = [
      { name: 'page2', path: '/foo', component: Foo },
      { name: 'page3', path: '/foo', component: Foo }
    ]
    

    and then compare the current route name to the route name of each link to determine the exact active class:

    <div id="app">
      <ul class="nav">
       <router-link
          v-for="{ to, name } in menu"
          v-slot="{ href: linkHref, navigate, isActive, isExactActive }"
          :key="to.name || name"
          :to="to"
        >
          <li v-else :class="['nav-item', isExactActive && $route.name === name && 'exact-active']">
            <a
              v-t="name"
              href="linkHref"
              class="navLink"
            />
          </li>
        </router-link>
      </ul>
    </div>