如何在Vue响应式视图中用Font Awesome切换导航栏bars/xmark图标
实现Bootstrap导航栏图标切换(无需v-if/v-show)
针对你的需求,这里提供两种可行方案,均不依赖Vue的v-if/v-show指令,适配你当前的Vue.js + Bootstrap环境:
方案一:CSS原生实现(推荐)
利用Bootstrap折叠组件的原生状态属性,通过CSS控制图标显示隐藏:
1. 修改导航按钮模板
在按钮内同时放置bars和xmark图标,分别添加类名用于区分:
<button class="navbar-toggler d-flex d-lg-none flex-column justify-content-around" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> <!-- 汉堡菜单图标 --> <i><fa :icon="['fas', 'bars']" class="icon toggle-alt fa-3x burger-icon burger-bars" /></i> <!-- 关闭图标 --> <i><fa :icon="['fas', 'xmark']" class="icon toggle-alt fa-3x burger-icon burger-xmark" /></i> </button>
2. 添加CSS切换规则
Bootstrap会在菜单展开时给按钮添加aria-expanded="true"属性,我们利用这个状态写样式:
/* 默认隐藏关闭图标 */ .burger-xmark { display: none; } /* 菜单展开时,显示关闭图标、隐藏汉堡图标 */ .navbar-toggler[aria-expanded="true"] .burger-xmark { display: block; } .navbar-toggler[aria-expanded="true"] .burger-bars { display: none; }
方案二:JavaScript监听折叠事件
通过监听Bootstrap折叠组件的show/hide事件,动态修改图标属性:
1. 给按钮和图标添加ref引用
<button ref="togglerBtn" class="navbar-toggler d-flex d-lg-none flex-column justify-content-around" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> <i><fa ref="burgerIcon" :icon="['fas', 'bars']" class="icon toggle-alt fa-3x burger-bars" /></i> </button>
2. 监听折叠事件并切换图标
在组件的mounted钩子中添加事件监听:
export default { name: "Navbar", data: () => ({ windowSize: "mobile", }), mounted() { const collapseEl = document.getElementById('navbarSupportedContent'); // 菜单展开时切换为xmark图标 collapseEl.addEventListener('show.bs.collapse', () => { this.$refs.burgerIcon.icon = ['fas', 'xmark']; }); // 菜单收起时切换回bars图标 collapseEl.addEventListener('hide.bs.collapse', () => { this.$refs.burgerIcon.icon = ['fas', 'bars']; }); window.addEventListener("resize", this.onResize.bind(this)); }, beforeUnmount() { // 移除事件监听避免内存泄漏 const collapseEl = document.getElementById('navbarSupportedContent'); collapseEl.removeEventListener('show.bs.collapse'); collapseEl.removeEventListener('hide.bs.collapse'); window.removeEventListener("resize", this.onResize.bind(this)); }, methods: { onResize() { if (window.innerWidth >= 320) this.windowSize = "mobile"; }, }, };
完整修改代码(CSS方案版)
整合上述改动后的完整组件代码:
<template> <nav class="navbar navbar-expand-lg navbar-light sticky-top set-nav"> <div class="container"> <nuxt-link to="/" class="brand"> <img class="img-fluid desktop logo" height="auto" width="auto" src="~/assets/images/logo.png" alt="Logo"/> <img class="img-fluid mobile" height="120" width="120" src="~/assets/images/logo.png" alt=""/> </nuxt-link> <button class="navbar-toggler d-flex d-lg-none flex-column justify-content-around" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> <i><fa :icon="['fas', 'bars']" class="icon toggle-alt fa-3x burger-icon burger-bars" /></i> <i><fa :icon="['fas', 'xmark']" class="icon toggle-alt fa-3x burger-icon burger-xmark" /></i> </button> <div class="collapse navbar-collapse" id="navbarSupportedContent"> <ul class="nav ml-auto"> <li class="nav-item active"> <nuxt-link class="nav-link" :to="{ path: '/', hash: '' }" exact>Home</nuxt-link> </li> <li class="nav-item"> <nuxt-link class="nav-link" :to="{ path: '/', hash: '' }" exact>Hire A Bike</nuxt-link> </li> <li class="nav-item"> <nuxt-link class="nav-link" :to="{ path: '/', hash: '' }" exact>About</nuxt-link> </li> <li class="nav-item"> <nuxt-link class="nav-link" :to="{ path: '/', hash: '' }" exact>Contact</nuxt-link> </li> </ul> </div> </div> <div class="header"> <div class="container"> <ul class="header-links"> <li class="header-link terms"> <fa :icon="['fas', 'hand-point-up']" class="terms-icon fa-2x" style="color:orange" /> <nuxt-link class="header-nuxt terms-link" :to="{ path: '/', hash: '' }" exact>Terms & Conditions</nuxt-link> </li> <li class="header-link phone-number"> <fa :icon="['fas', 'phone']" class="phone-icon fa-2x" style="color:orange" /> <nuxt-link class="header-nuxt phone-link" :to="{ path: '/', hash: '' }" exact>(+90) 533 505 50 50</nuxt-link> </li> </ul> </div> </div> </nav> </template> <script> export default { name: "Navbar", data: () => ({ windowSize: "mobile", }), beforeMount() { window.addEventListener("resize", this.onResize.bind(this)); }, beforeUnmount() { window.removeEventListener("resize", this.onResize.bind(this)); }, methods: { onResize() { if (window.innerWidth >= 320) this.windowSize = "mobile"; }, }, }; </script> <style> .set-nav{ height: 15rem; background-color: var(--color-dark-blue); margin-bottom:10rem; } .container{ width: 80%; } .logo{ height: 25rem; } .nav-link{ font-size: 2.5rem; font-weight: bold; margin: 2rem; list-style-type: none; color: orange !important; } .nav-link:hover{ color: white !important; transition: 0.3s ease-in-out; } .header{ position: absolute; top: 100%; left: 0; right: 0; background-color: #3e80c3; width: 100%; } .header-links{ list-style-type: none; margin-left: auto; margin-top: 1rem; } .header-link{ display: inline-block; } .header-nuxt{ font-size: 2.5rem; color: orange !important; margin: 2rem; } .terms:hover .terms-icon, .terms:hover .terms-link, .phone-number:hover .phone-icon, .phone-number:hover .phone-link{ color: white !important; text-decoration: none; transition: 0.3s ease-in-out; } /* 图标切换样式 */ .burger-xmark { display: none; } .navbar-toggler[aria-expanded="true"] .burger-xmark { display: block; } .navbar-toggler[aria-expanded="true"] .burger-bars { display: none; } @media (max-width: 992px) and (min-width: 320px) { img.desktop { display: none; } #navbarSupportedContent .nav { width: 100%; } .nav { align-items: left; flex-direction: column; position: relative; background-color: var(--color-dark-blue); } .nav-link { height: 6rem; } .nav-item { height: auto; text-align: center; } .navbar-toggler { width: 4rem; align-items: center; margin-top: -1rem; margin-right: 3rem; cursor: pointer; border: none; background-color: transparent !important; } } @media (max-width: 1400px) and (min-width: 992px) { img.mobile { display: none; } } @media (max-width: 320px) and (min-width: 0px) { img.mobile { display: none; } img.desktop { display: none; } } @media (min-width: 1400px) { img.mobile { display: none; } } </style>
内容的提问来源于stack exchange,提问作者Emircan
相关产品推荐
相关产品推荐

