在 Bulma 中导航到不同的 link 时,如何关闭悬停下拉菜单?

How do I make the hoverable dropdown close when navigating to a differerent link in Bulma?

HERE is a SANDBOX with the issue on it

这是我的 default.vue 文件

<template>
  <div class="ch-container">
    <header class="ch-header">
      <nav
        class="navbar is-fixed-top"
        role="navigation"
        aria-label="main navigation"
      >
        <div class="navbar-brand">
          <nuxt-link class="navbar-item" to="/">
            <img
              alt="CH Logo"
              src="https://i.imgur.com/v35Kfc9.png"
              width="28"
              height="28"
            />
          </nuxt-link>

          <a
            role="button"
            class="navbar-burger burger"
            aria-label="menu"
            aria-expanded="false"
            data-target="navbarBasicExample"
          >
            <span aria-hidden="true"></span>
            <span aria-hidden="true"></span>
            <span aria-hidden="true"></span>
          </a>
        </div>

        <div id="navbarBasicExample" class="navbar-menu">
          <div class="navbar-start">
            <nuxt-link class="navbar-item" to="/news">
              News
            </nuxt-link>

            <nuxt-link class="navbar-item" to="/resources">
              Resources
            </nuxt-link>

            <nuxt-link class="navbar-item" to="/tickers">
              Tickers
            </nuxt-link>

            <div class="navbar-item has-dropdown is-hoverable">
              <a class="navbar-link">
                More
              </a>
              <div class="navbar-dropdown">
                <a class="navbar-item">
                  FAQ
                </a>
                <nuxt-link class="navbar-item" to="/contact">
                  Contact
                </nuxt-link>
                <hr class="navbar-divider" />
                <a class="navbar-item">
                  Feature Request
                </a>
              </div>
            </div>
          </div>

          <div class="navbar-end">
            <div class="navbar-item">
              <a href="#">
                <fa :icon="faMoon" />
              </a>
            </div>
            <div class="navbar-item has-dropdown is-hoverable">
              <a class="navbar-link is-arrowless">
                <fa :icon="faExclamationCircle" />
              </a>
              <div class="navbar-dropdown">
                <a class="navbar-item">
                  No new notifications
                </a>
              </div>
            </div>
            <div class="navbar-item">
              <div class="buttons">
                <nuxt-link class="button is-primary" to="/signup">
                  <strong>Sign up</strong>
                </nuxt-link>
                <nuxt-link id="login" class="button is-light" to="/login">
                  Log in
                </nuxt-link>
              </div>
            </div>
          </div>
        </div>
      </nav>
    </header>
    <main class="ch-main">
      <nuxt />
    </main>
    <footer class="ch-footer is-hidden-mobile">
      <div class="level">
        <div class="level-left">
          <div class="level-item">
            <a href="#">
              <span class="icon">
                <fa :icon="faFacebookSquare" />
              </span>
            </a>
            <a href="#">
              <span class="icon">
                <fa :icon="faTwitterSquare" />
              </span>
            </a>
            <a href="#">
              <span class="icon">
                <fa :icon="faRedditSquare" />
              </span>
            </a>
          </div>
        </div>
        <div class="level-right">
          <div class="level-item">
            &copy;ch, All Rights Reserved
          </div>
          <div class="level-item">
            <nuxt-link to="/contact">Contact</nuxt-link>
          </div>
          <div class="level-item">
            <nuxt-link to="/terms-of-service">Terms</nuxt-link>
          </div>
          <div class="level-item">
            <nuxt-link to="/privacy-policy">Privacy</nuxt-link>
          </div>
        </div>
      </div>
    </footer>
  </div>
</template>

<script>
import {
  faFacebookSquare,
  faTwitterSquare,
  faRedditSquare,
} from '@fortawesome/free-brands-svg-icons'

import { faMoon, faExclamationCircle } from '@fortawesome/free-solid-svg-icons'

export default {
  computed: {
    faFacebookSquare() {
      return faFacebookSquare
    },
    faTwitterSquare() {
      return faTwitterSquare
    },
    faRedditSquare() {
      return faRedditSquare
    },
    faMoon() {
      return faMoon
    },
    faExclamationCircle() {
      return faExclamationCircle
    },
  },

  mounted() {
    // Get all "navbar-burger" elements
    const $navbarBurgers = Array.prototype.slice.call(
      document.querySelectorAll('.navbar-burger'),
      0
    )

    // Check if there are any navbar burgers
    if ($navbarBurgers.length > 0) {
      // Add a click event on each of them
      $navbarBurgers.forEach((el) => {
        el.addEventListener('click', () => {
          // Get the target from the "data-target" attribute
          const target = el.dataset.target
          const $target = document.getElementById(target)

          // Toggle the "is-active" class on both the "navbar-burger" and the "navbar-menu"
          el.classList.toggle('is-active')
          $target.classList.toggle('is-active')
        })
      })
    }
  },
}
</script>

<style></style>

这是说明问题的 GIF

移动到其他页面后如何关闭下拉菜单?

好吧,整个事情变得复杂了,因为悬停是由 css 触发的,因此当鼠标悬停在下拉菜单上时总是可以看到它。你必须覆盖这个状态并用 vue 事件解决它。我们还必须在路线上放置一个观察者来重置状态。

CodeSandbox - Example

<template>
  <div class="container">
    <nav class="navbar" role="navigation" aria-label="main navigation">
      <div class="navbar-brand">
        <!-- <a class="navbar-item" href="https://bulma.io"> -->
        <img src="https://bulma.io/images/bulma-logo.png" width="112" height="28">

        <a
          role="button"
          class="navbar-burger burger"
          aria-label="menu"
          aria-expanded="false"
          data-target="navbarBasicExample"
        >
          <span aria-hidden="true"></span>
          <span aria-hidden="true"></span>
          <span aria-hidden="true"></span>
        </a>
      </div>

      <div id="navbarBasicExample" class="navbar-menu">
        <div class="navbar-start">
          <a class="navbar-item">Home</a>

          <a class="navbar-item">Documentation</a>

          <div
            @mouseover="toggleDropdown(true)"
            @mouseleave="toggleDropdown(false)"
            class="navbar-item has-dropdown is-hoverable"
          >
            <a class="navbar-link">More</a>

            <div class="navbar-dropdown" :style="{display: showDropdown ? 'block' : 'none' }">
              <nuxt-link class="navbar-item" to="/about">About</nuxt-link>
              <nuxt-link class="navbar-item" to="/jobs">Jobs</nuxt-link>
            </div>
          </div>
        </div>

        <div class="navbar-end">
          <div class="navbar-item">
            <div class="buttons">
              <a class="button is-primary">
                <strong>Sign up</strong>
              </a>
              <a class="button is-light">Log in</a>
            </div>
          </div>
        </div>
      </div>
    </nav>
    <Nuxt/>
  </div>
</template>

<script>
export default {
  data() {
    return {
      routeChange: false,
      showDropdown: false
    };
  },

  watch: {
    $route() {
      this.routeChange = true;
      this.showDropdown = false;
    }
  },

  methods: {
    toggleDropdown(payload) {
      if (this.showDropdown !== payload) this.routeChange = false;
      if (!this.routeChange) {
        this.showDropdown = payload;
      }
    }
  }
};
</script>

这对我有用Bootstrap 5,在触摸设备上它使用点击,在有鼠标的设备上它使用悬停

<template>
  <span
    v-if="item"
    class="primary-navigation-list-dropdown"
    @mouseover="isTouchscreenDevice ? null : openDropdownMenu()"
    @mouseleave="isTouchscreenDevice ? null : closeDropdownMenu()"
  >
    <nuxt-link
      to="#"
      @click.prevent.native="openDropdownMenu"
      v-click-outside="closeDropdownMenu"
      :title="item.title"
      :class="[
        item.cssClasses,
        { show: isDropdownMenuVisible }
      ]"
      :id="`navbarDropdownMenuLink-${item.id}`"
      :aria-expanded="[isDropdownMenuVisible ? true : false]"
      class="
        primary-navigation-list-dropdown__toggle
        nav-link
        dropdown-toggle"
      aria-current="page"
      role="button"
      data-toggle="dropdown"
    >
      {{ item.label }}
    </nuxt-link>
    <ul
      :class="{ show: isDropdownMenuVisible }"
      :aria-labelledby="`navbarDropdownMenuLink-${item.id}`"
      class="
        primary-navigation-list-dropdown__menu
        dropdown-menu-list
        dropdown-menu"
    >
      <li
        v-for="item in item.children" :key="item.id"
        class="dropdown-menu-list__item"
      >
        <NavLink
          :attributes="item"
          class="dropdown-menu-list__link dropdown-item"
        />
      </li>
    </ul>
  </span>
</template>

<script>
import NavLink from '@/components/Navigation/NavLink';

export default {
  name: "DropdownMenu",
  props: {
    item: {
      type: Object,
      required: true,
    },
  },
  data() {
    return {
      isDropdownMenuVisible: false,
      isTouchscreenDevice: false
    };
  },
  mounted() {
    this.detectTouchscreenDevice();
  },
  methods: {
    openDropdownMenu() {
      if (this.isTouchscreenDevice) {
        this.isDropdownMenuVisible = !this.isDropdownMenuVisible;
      } else {
        this.isDropdownMenuVisible = true;
      }
    },

    closeDropdownMenu() {
      if (this.isTouchscreenDevice) {
        this.isDropdownMenuVisible = false;
      } else {
        this.isDropdownMenuVisible = false;
      }
    },

    detectTouchscreenDevice() {
      if (window.PointerEvent && ('maxTouchPoints' in navigator)) {
        if (navigator.maxTouchPoints > 0) {
          this.isTouchscreenDevice = true;
        }
      } else {
        if (window.matchMedia && window.matchMedia("(any-pointer:coarse)").matches) {
          this.isTouchscreenDevice = true;
        } else if (window.TouchEvent || ('ontouchstart' in window)) {
          this.isTouchscreenDevice = true;
        }
      }
      return this.isTouchscreenDevice;
    }
  },
  components: {
    NavLink
  }
};
</script>

<style scoped lang="scss">
.primary-navigation-list-dropdown {
  &__toggle {
    color: $white;

    &:hover {
      color: $blue;
    }
  }

  &__menu {
    margin-top: 0;
  }

  &__dropdown {

  }
}

.dropdown-menu-list {
  &__item {

  }

  &__link {
    &.active,
    &.nuxt-link-exact-active {
      border-bottom: 1px solid $blue;
    }
  }
}
</style>

NavLink.vue

<template>
  <component
    :is="attributes"
    v-bind="linkAttributes(attributes.path)"
    :title="attributes.title"
    :class="[ attributes.cssClasses ]"
    class="nav-link active_"
    aria-current="page"
    prefetch
  >
  {{ attributes.label }}
  </component>
</template>

<script>
export default {
  name: 'NavLink',
  props: {
    attributes: {
      type: Object,
      required: true
    }
  },
  methods: {
    linkAttributes(path) {
      if (path.match(/^(http(s)?|ftp):\/\//) || path.target === '_blank') {
        return {
          is: 'a',
          href: path,
          target: '_blank',
          rel: 'noopener'
        }
      }
      return {
        is: 'nuxt-link',
        to: path
      }
    }
  }
}
</script>

点击-outside.js

import Vue from 'vue'

Vue.directive('click-outside', {
  bind: function (el, binding, vnode) {
    el.clickOutsideEvent = function (event) {
      if (!(el == event.target || el.contains(event.target))) {
        vnode.context[binding.expression](event);
      }
    };
    document.body.addEventListener('click', el.clickOutsideEvent)
  },
  unbind: function (el) {
    document.body.removeEventListener('click', el.clickOutsideEvent)
  },
});

我没有使用 Vue,而是使用 Meteor(其中类似的路由调用导致 Bulma 下拉删除)。

修复它的方法有点笨拙,但是下拉菜单是可见的,因为 .has-dropdown 元素具有 .is-hoverable class。因此,为了解决这个问题,在任何点击下拉菜单项时,我 运行 这个:

// Remove the hover effect = dropdown disappears    
$(".has-dropdown").removeClass("is-hoverable");
// Tiny time later, put it back, so hover-drop works anew
setTimeout(function() {
    $(".has-dropdown").addClass("is-hoverable");
}, 100);

如果你有一个以上的下拉菜单,它会点击所有的下拉菜单并不重要,因为在路由到新页面时将它们全部重置是无害的。但如果你很挑剔,你可以只定位最近的下拉菜单。

由于 Bulma 的移动视图无论如何都不会激活悬停效果,因此这不会破坏我的导航栏的移动“汉堡菜单”版本。

我的项目在 Chrome、Safari、Firefox 上工作正常。