更新日志
重构 nav.js:移除 jQuery 依赖和内联样式操作,改用 CSS class(nav-show-title)控制状态切换;正确清理 PJAX 切换时的旧 scroll 监听器;修复默认状态下标题按钮仍可被 hover 的问题(pointer-events: none);修复 rmf.scrollToTop() 未定义的引用
前言
美化之前
美化的时候需要修改网站的源文件、添加样式、js之类的,需要一点基础,可以参考
🏠 站内链接,放心访问
Hexo博客添加自定义css和js文件
June's Blog
🏠 站内链接,放心访问
Butterfly自用全局变量
June's Blog
效果
修改效果:导航栏居中,二级菜单横向展示,导航栏显示网站、文章的标题等

参考链接
🔗 站外链接,请注意甄别
关于Butterfly的导航栏的一些教程
Ariasakaの小窝
🔗 站外链接,请注意甄别
butterfly导航栏修改方案(自用方案)
安知鱼
修改方案
1. 修改nav.pug文件
修改[blogRoot]\themes\Butterfly\layout\includes\header\nav.pug
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| nav#nav span#blog_name a#site-name(href=url_for('/')) #[=config.title]
#menus !=partial('includes/header/menu_item', {}, {cache: true}) center(id="name-container") a(id="page-name" href="javascript:rmf.scrollToTop()") PAGE_NAME
#toggleButtons if (theme.algolia_search.enable || theme.local_search.enable) #search-button a.site-page.social-icon.search i.fas.fa-search.fa-fw //span=' '+_p('search.title') #toggle-menu a.site-page i.fas.fa-bars.fa-fw
|
如果你的themes下没有Butterfly文件夹(可能是主题安装方式有问题),可以去Butterfly官网下载jerryc127/hexo-theme-butterfly: 🦋 A Hexo Theme: Butterfly (github.com),我下载的版本是4.5.1
如果你没有定义rmf.scrollToTop()函数,可以将其改为btf.scrollToDest(0, 500)
2. 添加nav.js
记得将信息改成自己的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| document.addEventListener('pjax:complete', tonav); document.addEventListener('DOMContentLoaded', tonav);
function tonav(){ document.getElementById("name-container").setAttribute("style", "display:none");
var position = $(window).scrollTop();
$(window).scroll(function () {
var scroll = $(window).scrollTop();
if (scroll > position) {
document.getElementById("name-container").setAttribute("style", ""); document.getElementsByClassName("menus_items")[1].setAttribute("style", "display:none!important");
} else {
document.getElementsByClassName("menus_items")[1].setAttribute("style", ""); document.getElementById("name-container").setAttribute("style", "display:none");
}
position = scroll;
}); function scrollToTop(){ document.getElementsByClassName("menus_items")[1].setAttribute("style",""); document.getElementById("name-container").setAttribute("style","display:none"); btf.scrollToDest(0, 500); }
document.getElementById("page-name").innerText = document.title.split(" | June's Blog")[0];}
|
3. 添加nav.css
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
| #nav *::after { background-color: var(--june-theme) !important; }
.menus_item_child li:not(#sidebar-menus li){ float: left; border-radius: 6px!important; -webkit-border-radius: 6px!important; -moz-border-radius: 6px!important; -ms-border-radius: 6px!important; -o-border-radius: 6px!important; } .menus_item_child:not(#sidebar-menus ul){
left:50%; translate:-50%; }
.menus_item_child:not(#sidebar-menus>.menus_items>.menus_item>.menus_item_child):hover{ border: 1px solid var(--june-theme)!important; color: white; }
#nav .menus_items .menus_item .menus_item_child li a:hover { color: white !important; }
#site-name::before { opacity: 0; background-color: var(--june-theme) !important; border-radius: 8px; -webkit-border-radius: 8px; -moz-border-radius: 8px; -ms-border-radius: 8px; -o-border-radius: 8px; transition: .3s; -webkit-transition: .3s; -moz-transition: .3s; -ms-transition: .3s; -o-transition: .3s; position: absolute; top: 0 !important; right: 0 !important; width: 105%; height: 120%; content: "\f015"; box-shadow: 0 0 5px var(--june-theme); font-family: "Font Awesome 6 Free"; text-align: center; color: white; line-height: 34px; font-size: 18px; }
#site-name:hover::before { opacity: 1; scale: 1.03; }
#site-name { position: relative; font-size: 24px; }
.nav-fixed #nav { transform: translateY(58px) !important; -webkit-transform: translateY(58px) !important; -moz-transform: translateY(58px) !important; -ms-transform: translateY(58px) !important; -o-transform: translateY(58px) !important; }
#nav { transition: none !important; -webkit-transition: none !important; -moz-transition: none !important; -ms-transition: none !important; -o-transition: none !important; }
#page-name::before { font-size: 18px; position: absolute; width: 100%; height: 100%; border-radius: 8px; color: white !important; top: 0; left: 0; content: '回到顶部'; background-color: var(--june-theme); transition: all .3s; -webkit-transition: all .3s; -moz-transition: all .3s; -ms-transition: all .3s; -o-transition: all .3s; opacity: 0; box-shadow: 0 0 3px var(--june-theme); line-height: 45px; }
#page-name:hover:before { opacity: 1; }
@media screen and (max-width: 900px) { #page-name, #menus { display: none !important; } }
#name-container { transition: all .3s; -webkit-transition: all .3s; -moz-transition: all .3s; -ms-transition: all .3s; -o-transition: all .3s; }
#name-container:hover { scale: 1.03 }
#page-name { position: relative; padding: 10px 30px }
#nav { padding: 0 20px; }
#toggleButtons { display: flex; flex:1 1 auto; justify-content: flex-end; flex-basis: 20px; }
#toggleButtons .site-page{ margin:3px; padding: 5px 10px; white-space: nowrap; }
|
过渡动画
效果
直接滚动鼠标,看导航栏切换即可
参考链接
🔗 站外链接,请注意甄别
1. 修改nav.pug文件
修改[blogRoot]\themes\Butterfly\layout\includes\header\nav.pug,在name-container前面加个divmask-name-container
1 2 3 4 5 6 7 8 9 10 11
| nav#nav span#blog_name a#site-name(href=url_for('/')) #[=config.title]
#menus !=partial('includes/header/menu_item', {}, {cache: true}) #mask-name-container(style="z-index: -1") center(id="name-container") a(id="page-name" href="javascript:rmf.scrollToTop()") PAGE_NAME ...
|
2. 修改nav.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| document.addEventListener('pjax:complete', tonav); document.addEventListener('DOMContentLoaded', tonav);
function tonav() { document.getElementById("name-container").setAttribute("style", "display:none");
var position = $(window).scrollTop();
$(window).scroll(function () {
var scroll = $(window).scrollTop();
if (scroll > position) { document.getElementById("mask-name-container").setAttribute("style", ""); document.getElementById("name-container").setAttribute("style", "top: 10px !important;"); document.getElementsByClassName("menus_items")[1].setAttribute("style", "top: -60px !important;");
} else { document.getElementById("mask-name-container").setAttribute("style", "z-index: -1 !important"); document.getElementById("name-container").setAttribute("style", "top: 70px !important;"); document.getElementsByClassName("menus_items")[1].setAttribute("style", ""); }
position = scroll; });
function scrollToTop() { document.getElementsByClassName("menus_items")[1].setAttribute("style", ""); document.getElementById("name-container").setAttribute("style", "display:none"); btf.scrollToDest(0, 500); }
document.getElementById("page-name").innerText = document.title.split(" | June's Blog")[0]; }
|
3. 修改nav.css
主要就是修改了菜单栏menus_items、文章名的容器name-container和遮罩mask-name-container
1 2 3 4 5 6 7 8 9
| #nav .menus_items { display: inline-block !important; transition: all .3s; position: absolute !important; width: fit-content !important; top: 50%; left: 50% !important; transform: translate(-50%, -50%) !important; }
|
1 2 3 4 5 6 7 8 9 10 11 12
| #name-container { transition: all .3s; -webkit-transition: all .3s; -moz-transition: all .3s; -ms-transition: all .3s; -o-transition: all .3s; position: absolute; top: 60px; left: 50%; transform: translateX(-50%); height: 100%; }
|
1 2 3 4 5 6 7 8
| #mask-name-container { width: 100%; height: 100%; position: absolute; overflow: hidden; left: 0; top: 0; }
|
如果你的站点名(左上角)hover没效果的话,再加上
1 2 3
| #site-name::before { z-index: 1 !important; }
|
完整的导航栏样式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
| #nav .menus_items { display: inline-block !important; transition: all .3s; position: absolute !important; width: fit-content !important; top: 50%; left: 50% !important; transform: translate(-50%, -50%) !important; }
.menus_item_child li:not(#sidebar-menus li){ float: left; border-radius: 6px!important; -webkit-border-radius: 6px!important; -moz-border-radius: 6px!important; -ms-border-radius: 6px!important; -o-border-radius: 6px!important; } .menus_item_child:not(#sidebar-menus ul){
left:50%; translate:-50%; }
#nav .menus_items .menus_item .menus_item_child li a:hover { color: white !important; }
#site-name::before { opacity: 0; background-color: var(--june-theme) !important; border-radius: 8px; -webkit-border-radius: 8px; -moz-border-radius: 8px; -ms-border-radius: 8px; -o-border-radius: 8px; transition: .3s; -webkit-transition: .3s; -moz-transition: .3s; -ms-transition: .3s; -o-transition: .3s; position: absolute; top: 0 !important; right: 0 !important; z-index: 1 !important; width: 105%; height: 120%; content: "\f015"; box-shadow: 0 0 5px var(--june-theme); font-family: "Font Awesome 6 Free"; text-align: center; color: white; line-height: 34px; font-size: 18px; }
#site-name:hover::before { opacity: 1; scale: 1.03; }
#blog_name { pointer-events: none; }
#site-name { position: relative; font-size: 24px; pointer-events: auto; z-index: 2; }
.nav-fixed #nav { transform: translateY(58px) !important; -webkit-transform: translateY(58px) !important; -moz-transform: translateY(58px) !important; -ms-transform: translateY(58px) !important; -o-transform: translateY(58px) !important; }
#nav { transition: none !important; -webkit-transition: none !important; -moz-transition: none !important; -ms-transition: none !important; -o-transition: none !important; }
#page-name::before { font-size: 18px; position: absolute; width: 100%; height: 100%; border-radius: 8px; color: white !important; top: 0; left: 0; content: '回到顶部'; background-color: var(--june-theme); transition: all .3s; -webkit-transition: all .3s; -moz-transition: all .3s; -ms-transition: all .3s; -o-transition: all .3s; opacity: 0; box-shadow: 0 0 3px var(--june-theme); line-height: 45px; }
#mask-name-container { width: 100%; height: 100%; position: absolute; overflow: hidden; left: 0; top: 0; }
#page-name:hover:before { opacity: 1; }
@media screen and (max-width: 900px) { #page-name, #menus { display: none !important; } }
#name-container { transition: all .3s; -webkit-transition: all .3s; -moz-transition: all .3s; -ms-transition: all .3s; -o-transition: all .3s; position: absolute; top: 60px; left: 50%; transform: translateX(-50%); height: 100%; }
#name-container:hover { scale: 1.03 }
#page-name { position: relative; padding: 10px 30px }
#nav { padding: 0 20px; }
#toggleButtons { display: flex; flex:1 1 auto; justify-content: flex-end; flex-basis: 20px; }
#toggleButtons .site-page{ margin:3px; padding: 5px 10px; white-space: nowrap; }
|
重构:CSS Class 驱动
上面的方案虽然能用,但实现比较粗糙:依赖 jQuery、用内联 style.cssText 覆盖样式、PJAX 切换时重复绑定 scroll 事件不清理。这里记录重构后的方案,核心思路是 JS 只管切换 class,CSS 负责所有视觉表现。
1. 修改 nav.pug
移除内联 style,href 改为全局暴露的 scrollToTop() 方法
1 2 3 4 5 6 7 8 9 10 11 12
| nav#nav span#blog_name a#site-name(href=url_for('/')) #[=config.title]
#menus !=partial('includes/header/menu_item', {}, {cache: true}) #mask-name-container center(id="name-container") a(id="page-name" href="javascript:scrollToTop()") PAGE_NAME
#toggleButtons ...
|
2. 重写 nav.js
不再操作 style.cssText,只在 #nav 上切换 nav-show-title class;正确清理旧监听器避免 PJAX 重复绑定
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
|
;(() => { 'use strict'
const NAV_STATE_CLASS = 'nav-show-title' const SCROLL_THRESHOLD = 50
let lastScrollTop = 0 let ticking = false let scrollHandler = null
function updateNav () { const scrollTop = window.scrollY || document.documentElement.scrollTop const nav = document.getElementById('nav') if (!nav) return
if (scrollTop > lastScrollTop && scrollTop > SCROLL_THRESHOLD) { nav.classList.add(NAV_STATE_CLASS) } else { nav.classList.remove(NAV_STATE_CLASS) }
lastScrollTop = scrollTop ticking = false }
function onScroll () { if (!ticking) { window.requestAnimationFrame(updateNav) ticking = true } }
function init () { const pageName = document.getElementById('page-name') if (pageName) { const titleParts = document.title.split(" | June's Blog") pageName.innerText = titleParts[0] || document.title }
if (scrollHandler) { window.removeEventListener('scroll', scrollHandler) }
lastScrollTop = window.scrollY || document.documentElement.scrollTop const nav = document.getElementById('nav') if (nav) nav.classList.remove(NAV_STATE_CLASS)
scrollHandler = onScroll window.addEventListener('scroll', scrollHandler, { passive: true }) }
window.scrollToTop = () => { const nav = document.getElementById('nav') if (nav) nav.classList.remove(NAV_STATE_CLASS) if (typeof btf !== 'undefined') { btf.scrollToDest(0, 500) } else { window.scrollTo({ top: 0, behavior: 'smooth' }) } }
document.addEventListener('DOMContentLoaded', init) document.addEventListener('pjax:complete', init) })()
|
3. 修改 nav.css(状态切换部分)
用 .nav-show-title class 控制菜单/标题的显隐,pointer-events: none 防止隐藏的标题按钮被 hover 到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| #nav .menus_items { display: inline-block !important; position: absolute !important; width: fit-content !important; top: 50%; left: 50% !important; transform: translate(-50%, -50%) !important; transition: opacity .3s, visibility .3s; }
#nav.nav-show-title .menus_items { opacity: 0; visibility: hidden; pointer-events: none; }
#nav.nav-show-title #name-container { top: 10px; }
#nav #mask-name-container { z-index: -1; pointer-events: none; }
#nav #name-container { top: 60px; display: block; }
#nav.nav-show-title #mask-name-container { z-index: 1; pointer-events: auto; }
#mask-name-container { width: 100%; height: 100%; position: absolute; overflow: hidden; left: 0; top: 0; }
#name-container { transition: top .3s; position: absolute; top: 60px; left: 50%; transform: translateX(-50%); height: 100%; }
#name-container:hover { scale: 1.03; }
|