@@ -11,9 +11,10 @@
< / div >
< div class = "header-nav-items" >
{if condition="!empty($header_categorys)"}
< div class = "header-nav-item" >
< div class = "header-nav-item" >
< div class = "header-nav-title" > {:lang_i18n('产品列表')}< / div >
< div class = "header-dropdown" >
<!-- style=" opacity: 1;" -- >
< div class = "header-dropdown" style = " opacity: 1;" >
< div class = "header-dropdown-tabs" >
{volist name="header_categorys" id="vo" key="idx"}
< a href = "{:url('product/category', ['id' => $vo.id])}" class = "header-tab-item {if condition=" $ idx = = 1 " } active { / if } " data-tab = "tag-{$vo.id}" > {$vo.name}< / a >
@@ -190,40 +191,41 @@
< / div >
< div style = "width: 100vw;height: 64px;" > < / div >
< div id = "mhk" > < / div >
< script >
// // 获取所有包含商品卡片的元素
< script >
// 获取所有包含商品卡片的元素
const productCards = document . querySelectorAll ( '.header-buy-product-card' ) ;
productCards . forEach ( card => {
// 获取卡片内的图片容器和两张图片
const imgContainer = card . querySelector ( '.header-buy-product-img' ) ;
const defaultImg = imgContainer ? . querySelector ( '.header-default-img' ) ;
const hoverImg = imgContainer ? . querySelector ( '.header-hover-img' ) ;
// 获取卡片内的图片容器和两张图片
const imgContainer = card . querySelector ( '.header-buy-product-img' ) ;
const defaultImg = imgContainer ? . querySelector ( '.header-default-img' ) ;
const hoverImg = imgContainer ? . querySelector ( '.header-hover-img' ) ;
// 检查悬浮图是否存在( 因为HTML中用了if判断, 所以可能有也可能没有这个元素)
const hasHoverImg = hoverImg !== null ;
// 检查悬浮图是否存在( 因为HTML中用了if判断, 所以可能有也可能没有这个元素)
const hasHoverImg = hoverImg !== null ;
// 如果没有悬浮图,不需要任何交互,直接返回
if ( ! hasHoverImg ) return ;
// 如果没有悬浮图,不需要任何交互,直接返回
if ( ! hasHoverImg ) return ;
// 初始状态确保普通图显示,悬浮图隐藏
// 初始状态确保普通图显示,悬浮图隐藏
defaultImg . style . opacity = '1' ;
hoverImg . style . opacity = '0' ;
// 鼠标移入事件:显示悬浮图,隐藏普通图
card . addEventListener ( 'mouseenter' , ( ) => {
defaultImg . style . opacity = '0' ;
hoverImg . style . opacity = '1' ;
} ) ;
// 鼠标移出事件:恢复普通图,隐藏悬浮图
card . addEventListener ( 'mouseleave' , ( ) => {
defaultImg . style . opacity = '1' ;
hoverImg . style . opacity = '0' ;
// 鼠标移入事件:显示悬浮图,隐藏普通图
card . addEventListener ( 'mouseenter' , ( ) => {
defaultImg . style . opacity = '0' ;
hoverImg . style . opacity = '1' ;
} ) ;
// 鼠标移出事件:恢复普通图,隐藏悬浮图
card . addEventListener ( 'mouseleave' , ( ) => {
defaultImg . style . opacity = '1' ;
hoverImg . style . opacity = '0' ;
} ) ;
} ) ;
} ) ;
//产品列表在hover复位tabs选择
// 产品列表在hover复位tabs选择
const productNavContainer = document . querySelector ( '.header-nav-item:first-child' ) ;
if ( productNavContainer ) {
@@ -247,20 +249,15 @@
} ) ;
}
const tabItems = document . querySelectorAll ( '.header-tab-item' ) ;
const tabItems1 = document . querySelectorAll ( '.header-tab-item' ) ;
const tabContents = document . querySelectorAll ( '.header-tab-content' ) ;
tabItems . forEach ( tab =>
{
tab . addEventListener ( 'mouseenter' , ( ) =>
{
tabItems . forEach ( item => item . classList . remove ( 'active' ) ) ;
tabItems1 . forEach ( tab => {
tab . addEventListener ( 'mouseenter' , ( ) => {
tabItems1 . forEach ( item => item . classList . remove ( 'active' ) ) ;
tab . classList . add ( 'active' ) ;
const tabId = tab . getAttribute ( 'data-tab' ) ;
tabContents . forEach ( content =>
{
tabContents . forEach ( content => {
content . classList . remove ( 'active' ) ;
if ( content . id === tabId ) {
content . classList . add ( 'active' ) ;
@@ -271,33 +268,31 @@
let searchHistory = JSON . parse ( localStorage . getItem ( 'searchHistory' ) || '[]' ) ;
function renderHistory ( )
{
function renderHistory ( ) {
const historyList = document . getElementById ( 'historyList' ) ;
const historySection = document . getElementById ( 'searchHistory' ) ;
if ( searchHistory . length === 0 ) {
historySection . style . display = 'none' ;
if ( historySection ) historySection . style . display = 'none' ;
return ;
}
historySection . style . display = 'block' ;
historyList . innerHTML = searchHistory . map ( ( item , index ) => `
<div class="header-history-item" data-keyword=" ${ item } ">
<span> ${ item } </span >
<span class="header-delete-icon" data-index=" ${ index } ">✕ </span>
</div >
` ) . join ( '' ) ;
document . querySelectorAll ( '.header-history-item' ) . forEach ( item =>
{
if ( historySection ) historySection . style . display = 'block' ;
if ( historyList ) {
historyList . innerHTML = searchHistory . map ( ( item , index ) => `
<div class="header-history-item" data-keyword=" ${ item } " >
<span> ${ item } </span>
<span class="header-delete-icon" data-index=" ${ index } ">✕</span >
</div>
` ) . join ( '' ) ;
}
document . querySelectorAll ( '.header-history-item' ) . forEach ( item => {
const keyword = item . getAttribute ( 'data-keyword' ) ;
const deleteBtn = item . querySelector ( '.header-delete-icon' ) ;
item . addEventListener ( 'click' , ( e ) =>
{
item . addEventListener ( 'click' , ( e ) => {
if ( e . target === deleteBtn ) return ;
performSearch ( keyword ) ;
} ) ;
if ( deleteBtn ) {
deleteBtn . addEventListener ( 'click' , ( e ) =>
{
deleteBtn . addEventListener ( 'click' , ( e ) => {
e . stopPropagation ( ) ;
const index = parseInt ( deleteBtn . getAttribute ( 'data-index' ) ) ;
searchHistory . splice ( index , 1 ) ;
@@ -308,8 +303,7 @@
} ) ;
}
function addToHistory ( keyword )
{
function addToHistory ( keyword ) {
if ( ! keyword . trim ( ) ) return ;
searchHistory = searchHistory . filter ( item => item !== keyword ) ;
searchHistory . unshift ( keyword ) ;
@@ -320,16 +314,14 @@
const clearHistoryBtn = document . getElementById ( 'clearHistory' ) ;
if ( clearHistoryBtn ) {
clearHistoryBtn . addEventListener ( 'click' , ( ) =>
{
clearHistoryBtn . addEventListener ( 'click' , ( ) => {
searchHistory = [ ] ;
localStorage . setItem ( 'searchHistory' , JSON . stringify ( searchHistory ) ) ;
renderHistory ( ) ;
} ) ;
}
function performSearch ( keyword )
{
function performSearch ( keyword ) {
if ( keyword . trim ( ) ) {
addToHistory ( keyword ) ;
window . location . href = "{:url('product/search')}?keywords=" + encodeURIComponent ( keyword ) ;
@@ -344,67 +336,119 @@
const searchSubmit = document . querySelector ( '.header-search-submit' ) ;
const langDropdown = document . getElementById ( 'langDropdown' ) ;
const buyDropdown = document . getElementById ( 'buyDropdown' ) ;
const mhk = document . getElementById ( 'mhk' ) ;
const body = document . body ;
const langCloseBtn = document . querySelector ( '.header-lang-dropdown-delete-icon' ) ;
const searchCloseBtn = document . querySelector ( '.header-search-dropdown-delete-icon' ) ;
// 保存滚动位置
let scrollTopPosition = 0 ;
// 打开蒙版层(禁止滚动,不抖动)
function openOverlay ( ) {
if ( mhk && mhk . style . display !== 'block' ) {
// 保存当前滚动位置
scrollTopPosition = window . pageYOffset || document . documentElement . scrollTop ;
mhk . style . display = 'block' ;
// 禁止滚动,保持位置
body . style . position = 'fixed' ;
body . style . top = ` - ${ scrollTopPosition } px ` ;
body . style . left = '0' ;
body . style . right = '0' ;
body . style . width = '100%' ;
}
}
// 关闭蒙版层(恢复滚动)
function closeOverlay ( ) {
if ( mhk && mhk . style . display === 'block' ) {
const isBuyOpen = buyDropdown && buyDropdown . classList . contains ( 'show' ) ;
const isSearchOpen = searchDropdown && searchDropdown . classList . contains ( 'show' ) ;
const isLangOpen = langDropdown && langDropdown . classList . contains ( 'show' ) ;
// 检查是否有任何导航下拉菜单打开
let hasNavOpen = false ;
document . querySelectorAll ( '.header-dropdown' ) . forEach ( dropdown => {
const style = window . getComputedStyle ( dropdown ) ;
if ( style . opacity === '1' ) {
hasNavOpen = true ;
}
} ) ;
mhk . style . display = 'none' ;
// 恢复滚动
body . style . position = '' ;
body . style . top = '' ;
body . style . left = '' ;
body . style . right = '' ;
body . style . width = '' ;
// 恢复滚动位置
window . scrollTo ( 0 , scrollTopPosition ) ;
}
}
// 关闭所有导航下拉菜单
function closeAllNavDropdowns ( ) {
document . querySelectorAll ( '.header-dropdown' ) . forEach ( dropdown => {
dropdown . style . opacity = '' ;
dropdown . style . transform = '' ;
dropdown . style . pointerEvents = '' ;
} ) ;
}
// 语言关闭按钮
if ( langCloseBtn && langCloseBtn ) {
langCloseBtn . addEventListener ( 'click' , ( e ) =>
{
if ( langCloseBtn ) {
langCloseBtn . addEventListener ( 'click' , ( e ) => {
e . stopPropagation ( ) ;
langDropdown . classList . remove ( 'show' ) ;
if ( langDropdown ) langDropdown . classList . remove ( 'show' ) ;
closeOverlay ( ) ;
} ) ;
}
//搜索关闭
if ( searchCloseBtn && searchCloseBtn ) {
searchCloseBtn . addEventListener ( 'click' , ( e ) =>
{
// 搜索关闭按钮
if ( searchCloseBtn ) {
searchCloseBtn . addEventListener ( 'click' , ( e ) => {
e . stopPropagation ( ) ;
searchDropdown . classList . remove ( 'show' ) ;
if ( searchDropdown ) searchDropdown . classList . remove ( 'show' ) ;
closeOverlay ( ) ;
} ) ;
}
// 鼠标移入导航项时,关闭购买下拉框
// 鼠标移入导航项时,关闭其他弹窗
const navItems = document . querySelectorAll ( '.header-nav-item' ) ;
navItems . forEach ( item =>
{
item . addEventListener ( 'mouseenter' , ( ) =>
{
buyDropdown . classList . remove ( 'show' ) ;
langDropdown . classList . remove ( 'show' ) ;
searchDropdown . classList . remove ( 'show' ) ;
navItems . forEach ( item => {
item . addEventListener ( 'mouseenter' , ( ) => {
if ( buyDropdown ) buyDropdown . classList . remove ( 'show' ) ;
if ( langDropdown ) langDropdown . classList . remove ( 'show' ) ;
if ( searchDropdown ) searchDropdown . classList . remove ( 'show' ) ;
} ) ;
} ) ;
// 搜索按钮点击
if ( searchBtn && searchDropdown ) {
searchBtn . addEventListener ( 'click' , ( e ) =>
{
searchBtn . addEventListener ( 'click' , ( e ) => {
e . stopPropagation ( ) ;
if ( langDropdown ) langDropdown . classList . remove ( 'show' ) ;
if ( buyDropdown ) buyDropdown . classList . remove ( 'show' ) ;
searchDropdown . classList . toggle ( 'show' ) ;
if ( searchDropdown . classList . contains ( 'show' ) ) {
renderHistory ( ) ;
openOverlay ( ) ;
} else {
closeOverlay ( ) ;
}
} ) ;
}
if ( searchSubmit ) {
searchSubmit . addEventListener ( 'click' , ( ) =>
{
searchSubmit . addEventListener ( 'click' , ( ) => {
performSearch ( searchInput . value ) ;
} ) ;
}
if ( searchInput ) {
searchInput . addEventListener ( 'keypress' , ( e ) =>
{
searchInput . addEventListener ( 'keypress' , ( e ) => {
if ( e . key === 'Enter' ) {
performSearch ( searchInput . value ) ;
}
@@ -412,37 +456,44 @@
}
const hotProducts = document . querySelectorAll ( '.header-hot-product-item' ) ;
hotProducts . forEach ( product =>
{
product . addEventListener ( 'click' , ( ) =>
{
hotProducts . forEach ( product => {
product . addEventListener ( 'click' , ( ) => {
const keyword = product . getAttribute ( 'data-keyword' ) ;
if ( keyword ) performSearch ( keyword ) ;
} ) ;
} ) ;
// 语言按钮点击
const langBtn = document . querySelector ( '.header-lang-wrapper .header-nav-btn' ) ;
if ( langBtn && langDropdown ) {
langBtn . addEventListener ( 'click' , ( e ) =>
{
langBtn . addEventListener ( 'click' , ( e ) => {
e . stopPropagation ( ) ;
if ( searchDropdown ) searchDropdown . classList . remove ( 'show' ) ;
if ( buyDropdown ) buyDropdown . classList . remove ( 'show' ) ;
langDropdown . classList . toggle ( 'show' ) ;
if ( langDropdown . classList . contains ( 'show' ) ) {
openOverlay ( ) ;
} else {
closeOverlay ( ) ;
}
} ) ;
}
// 购买按钮点击
const buyWrapper = document . querySelector ( '.header-buy-wrapper' ) ;
if ( buyWrapper && buyDropdown ) {
buyWrapper . addEventListener ( 'click' , ( ) =>
{
// buyDropdown.classList.add('show');
buyWrapper . addEventListener ( 'click' , ( ) => {
buyDropdown . classList . toggle ( 'show' ) ;
if ( buyDropdown . classList . contains ( 'show' ) ) {
openOverlay ( ) ;
} else {
closeOverlay ( ) ;
}
} ) ;
}
document . addEventListener ( 'click' , ( e ) =>
{
// 全局点击关闭
document . addEventListener ( 'click' , ( e ) => {
if ( ! e . target . closest ( '.header-search-wrapper' ) ) {
if ( searchDropdown ) searchDropdown . classList . remove ( 'show' ) ;
}
@@ -452,438 +503,195 @@
if ( ! e . target . closest ( '.header-buy-wrapper' ) ) {
if ( buyDropdown ) buyDropdown . classList . remove ( 'show' ) ;
}
// 关闭所有导航下拉菜单
if ( ! e . target . closest ( '.header-nav-item' ) ) {
closeAllNavDropdowns ( ) ;
closeOverlay ( ) ;
}
} ) ;
// 阻止弹窗冒泡
if ( searchDropdown ) searchDropdown . addEventListener ( 'click' , ( e ) => e . stopPropagation ( ) ) ;
if ( langDropdown ) langDropdown . addEventListener ( 'click' , ( e ) => e . stopPropagation ( ) ) ;
if ( buyDropdown ) buyDropdown . addEventListener ( 'click' , ( e ) => e . stopPropagation ( ) ) ;
renderHistory ( ) ;
// ========== 蒙版层功能(支持所有弹窗,包括导航悬停下拉) ==========
const mhk = document . getElementById ( 'mhk ' ) ;
const body = document . body ;
// 获取滚动条宽度(防止打开弹窗时页面抖动)
function getScrollbarWidth ( ) {
const scrollDiv = document . createElement ( 'div' ) ;
scrollDiv . style . cssText = `
width: 100px;
height: 100px;
overflow: scroll;
position: absolute;
top: -9999px;
` ;
document . body . appendChild ( scrollDiv ) ;
const scrollbarWidth = scrollDiv . offsetWidth - scrollDiv . clientWidth ;
console . log ( scrollbarWidth , '=scrollbarWidth=' )
document . body . removeChild ( scrollDiv ) ;
return scrollbarWidth ;
}
// 打开蒙版层的函数
function openOverlay ( ) {
if ( mhk && mhk . style . display !== 'block' ) {
mhk . style . display = 'block' ;
body . style . overflow = 'hidden' ;
body . style . paddingRight = getScrollbarWidth ( ) + 'px' ;
}
}
// 关闭蒙版层的函数
function closeOverlay ( ) {
if ( mhk && mhk . style . display === 'block' ) {
// 检查是否还有其他弹窗打开(包括导航下拉菜单)
const isBuyOpen = buyDropdown && buyDropdown . classList . contains ( 'show' ) ;
const isSearchOpen = searchDropdown && searchDropdown . classList . contains ( 'show' ) ;
const isLangOpen = langDropdown && langDropdown . classList . contains ( 'show' ) ;
// 检查导航下拉菜单是否打开(通过检查是否有 active 或显示状态)
const productDropdown = document . querySelector ( '.header-nav-item:first-child .header-dropdown' ) ;
const isProductDropdownOpen = productDropdown && window . getComputedStyle ( productDropdown ) . display !== 'none' ;
// 检查其他导航下拉菜单
let isOtherNavOpen = false ;
const otherNavItems = document . querySelectorAll ( '.header-nav-item:not(:first-child)' ) ;
otherNavItems . forEach ( item => {
const dropdown = item . querySelector ( '.header-dropdown' ) ;
if ( dropdown && window . getComputedStyle ( dropdown ) . display !== 'none' ) {
isOtherNavOpen = true ;
}
} ) ;
// 只有当所有弹窗和导航下拉菜单都关闭时,才关闭蒙版层
if ( ! isBuyOpen && ! isSearchOpen && ! isLangOpen && ! isProductDropdownOpen && ! isOtherNavOpen ) {
mhk . style . display = 'none' ;
body . style . overflow = '' ;
body . style . paddingRight = '' ;
}
}
}
// 强制关闭所有弹窗和蒙版
function closeAllDropdownsAndOverlay ( ) {
let anyOpen = false ;
if ( buyDropdown && buyDropdown . classList . contains ( 'show' ) ) {
buyDropdown . classList . remove ( 'show' ) ;
anyOpen = true ;
}
if ( searchDropdown && searchDropdown . classList . contains ( 'show' ) ) {
searchDropdown . classList . remove ( 'show' ) ;
anyOpen = true ;
}
if ( langDropdown && langDropdown . classList . contains ( 'show' ) ) {
langDropdown . classList . remove ( 'show' ) ;
anyOpen = true ;
}
// 关闭产品列表导航的下拉菜单( 通过移除hover触发的显示)
const productNavItem = document . querySelector ( '.header-nav-item:first-child' ) ;
if ( productNavItem ) {
const productDropdown = productNavItem . querySelector ( '.header-dropdown' ) ;
if ( productDropdown && window . getComputedStyle ( productDropdown ) . display !== 'none' ) {
productDropdown . style . display = '' ;
anyOpen = true ;
}
}
// 关闭其他导航的下拉菜单
const otherNavItems = document . querySelectorAll ( '.header-nav-item:not(:first-child)' ) ;
otherNavItems . forEach ( item => {
const dropdown = item . querySelector ( '.header-dropdown' ) ;
if ( dropdown && window . getComputedStyle ( dropdown ) . display !== 'none' ) {
dropdown . style . display = '' ;
anyOpen = true ;
}
} ) ;
if ( anyOpen ) {
closeOverlay ( ) ;
} else {
closeOverlay ( ) ;
}
}
// ========== 监听导航下拉菜单的显示/隐藏 ==========
// 1. 产品列表导航(第一个导航项)
const productNavItem = document . querySelector ( '.header-nav-item:first-child' ) ;
if ( productNavItem ) {
const productDropdown = productNavItem . querySelector ( '.header-dropdown' ) ;
// 监听鼠标进入产品列表导航
productNavItem . addEventListener ( 'mouseenter' , ( ) => {
// 当导航下拉菜单显示时,关闭其他点击弹窗
if ( searchDropdown && searchDropdown . classList . contains ( 'show' ) ) {
searchDropdown . classList . remove ( 'show' ) ;
}
if ( langDropdown && langDropdown . classList . contains ( 'show' ) ) {
langDropdown . classList . remove ( 'show' ) ;
}
if ( buyDropdown && buyDropdown . classList . contains ( 'show' ) ) {
buyDropdown . classList . remove ( 'show' ) ;
}
// 打开蒙版层
openOverlay ( ) ;
} ) ;
// 监听鼠标离开产品列表导航(延迟关闭,给用户移动到下拉菜单的时间)
productNavItem . addEventListener ( 'mouseleave' , ( ) => {
setTimeout ( ( ) => {
// 检查鼠标是否在下拉菜单上
if ( productDropdown ) {
const isHoveringDropdown = productDropdown . matches ( ':hover' ) ;
const isHoveringNav = productNavItem . matches ( ':hover' ) ;
if ( ! isHoveringDropdown && ! isHoveringNav ) {
closeOverlay ( ) ;
}
} else {
closeOverlay ( ) ;
}
} , 100 ) ;
} ) ;
// 监听下拉菜单的鼠标离开事件
if ( productDropdown ) {
productDropdown . addEventListener ( 'mouseleave' , ( ) => {
setTimeout ( ( ) => {
const isHoveringNav = productNavItem . matches ( ':hover' ) ;
if ( ! isHoveringNav ) {
closeOverlay ( ) ;
}
} , 100 ) ;
// 蒙版层点击关闭
if ( mhk ) {
mhk . addEventListener ( 'click' , ( ) => {
if ( searchDropdown ) searchDropdown . classList . remove ( 'show ' ) ;
if ( langDropdown ) langDropdown . classList . remove ( 'show' ) ;
if ( buyDropdown ) buyDropdown . classList . remove ( 'show' ) ;
closeAllNavDropdowns ( ) ;
closeOverlay ( ) ;
} ) ;
}
}
// 2. 其他导航项(有下拉菜单的)
const otherNavItems = document . querySelectorAll ( '.header-nav-item:not(:first-child)' ) ;
otherNavItems . forEach ( item => {
const hasDropdown = item . querySelector ( '.header-dropdown' ) ;
if ( hasDropdown ) {
const dropdown = hasDropdown ;
// 监听鼠标进入导航项
item . addEventListener ( 'mouseenter' , ( ) => {
// 关闭其他点击弹窗
if ( searchDropdown && searchDropdown . classList . contains ( 'show' ) ) {
searchDropdown . classList . remove ( 'show' ) ;
}
if ( langDropdown && langDropdown . classList . contains ( 'show' ) ) {
langDropdown . classList . remove ( 'show' ) ;
}
if ( buyDropdown && buyDropdown . classList . contains ( 'show' ) ) {
buyDropdown . classList . remove ( 'show' ) ;
}
// 打开蒙版层
openOverlay ( ) ;
} ) ;
// 监听鼠标离开导航项
item . addEventListener ( 'mouseleave' , ( ) => {
setTimeout ( ( ) => {
if ( dropdown ) {
const isHoveringDropdown = dropdown . matches ( ':hover' ) ;
const isHoveringNav = item . matches ( ':hover' ) ;
if ( ! isHoveringDropdown && ! isHoveringNav ) {
// 监听弹窗状态变化关闭蒙版
function setupDropdownObserver ( dropdown ) {
if ( ! dropdown ) return ;
const observer = new MutationObserver ( ( mutations ) => {
mutations . forEach ( ( mutation ) => {
if ( mutation . attributeName === 'class' ) {
if ( ! dropdown . classList . contains ( 'show' ) ) {
closeOverlay ( ) ;
}
} else {
closeOverlay ( ) ;
}
} , 100 );
} ) ;
} ) ;
observer . observe ( dropdown , { attributes : true } ) ;
}
// 监听下拉菜单的鼠标离开
if ( d ropdown ) {
dropdown . addEventListener ( 'mouseleave' , ( ) => {
setTimeout ( ( ) => {
const isHoveringNav = item . matches ( ':hover' ) ;
if ( ! isHoveringNav ) {
setupDropdownObserver ( buyDropdown ) ;
setupDropdownObserver ( searchD ropdown) ;
setupDropdownObserver ( langDropdown ) ;
// ========== 监听下拉菜单打开状态,自动控制蒙版 ==========
function initHeaderDropdown ( ) {
// 获取所有下拉菜单
const allDropdowns = document . querySelectorAll ( '.header-dropdown' ) ;
// 设置初始样式
allDropdowns . forEach ( dropdown => {
dropdown . style . opacity = '0' ;
dropdown . style . transform = 'translateY(-1.25em)' ;
dropdown . style . pointerEvents = 'none' ;
dropdown . style . transition = 'opacity 0.3s ease, transform 0.3s ease' ;
} ) ;
// 监听单个下拉菜单的样式变化
function observeDropdown ( dropdown ) {
const observer = new MutationObserver ( ( ) => {
const isOpen = dropdown . style . opacity === '1' ;
if ( isOpen ) {
openOverlay ( ) ;
} else {
let anyOpen = false ;
allDropdowns . forEach ( d => {
if ( d . style . opacity === '1' ) anyOpen = true ;
} ) ;
if ( ! anyOpen ) {
closeOverlay ( ) ;
}
}
} ) ;
observer . observe ( dropdown , {
attributes : true ,
attributeFilter : [ 'style' ]
} ) ;
}
// 为每个下拉菜单添加监听
allDropdowns . forEach ( dropdown => {
observeDropdown ( dropdown ) ;
} ) ;
// 鼠标悬停控制下拉菜单显示/隐藏
const navItems = document . querySelectorAll ( '.header-nav-item' ) ;
navItems . forEach ( item => {
const dropdown = item . querySelector ( '.header-dropdown' ) ;
if ( ! dropdown ) return ;
let timer = null ;
let isHovering = false ; // 增加悬停状态标记
// 显示下拉菜单的函数
const showDropdown = ( ) => {
if ( timer ) clearTimeout ( timer ) ;
if ( dropdown . style . opacity === '1' ) return ;
dropdown . style . opacity = '1' ;
dropdown . style . transform = 'translateY(0)' ;
dropdown . style . pointerEvents = 'auto' ;
} ;
// 隐藏下拉菜单的函数
const hideDropdown = ( ) => {
if ( timer ) clearTimeout ( timer ) ;
if ( dropdown . style . opacity === '0' ) return ;
dropdown . style . opacity = '0' ;
dropdown . style . transform = 'translateY(-1.25em)' ;
dropdown . style . pointerEvents = 'none' ;
} ;
item . addEventListener ( 'mouseenter' , ( ) => {
isHovering = true ;
clearTimeout ( timer ) ;
showDropdown ( ) ;
} ) ;
item . addEventListener ( 'mouseleave' , ( e ) => {
isHovering = false ;
// 检查鼠标是否移到了下拉菜单上
const relatedTarget = e . relatedTarget ;
const isMovingToDropdown = dropdown . contains ( relatedTarget ) ;
if ( isMovingToDropdown ) {
// 如果移向下拉菜单,不清除,等待下拉菜单的 mouseenter
return ;
}
timer = setTimeout ( ( ) => {
if ( ! isHovering && ! dropdown . matches ( ':hover' ) ) {
hideDropdown ( ) ;
}
} , 100 ) ;
} ) ;
}
}
} ) ;
// ========== 重新绑定所有点击弹窗事件 ==========
// 1. 购买弹窗
const buyWrapperNew = document . querySelector ( '.header-buy-wrapper' ) ;
if ( buyWrapperNew && buyDropdown ) {
const newBuyWrapper = buyWrapperNew . cloneNode ( true ) ;
buyWrapperNew . parentNod e . rep laceChild ( newBuyWrapper , buyWrapperNew ) ;
newBuyWrapper . addEventListener ( 'click' , ( e ) => {
e . stopPropagation ( ) ;
const isOpen = buyDropdown . classList . contains ( 'show' ) ;
if ( isOpen ) {
buyDropdown . classList . remove ( 'show' ) ;
closeOverlay ( ) ;
} else {
// 关闭其他弹窗和导航下拉菜单
if ( searchDropdown && searchDropdown . classList . contains ( 'show' ) ) {
searchDropdown . classList . remove ( 'show' ) ;
}
if ( langDropdown && langDropdown . classList . contains ( 'show' ) ) {
langDropdown . classList . remove ( 'show' ) ;
}
// 关闭导航下拉菜单
closeAllNavDropdowns ( ) ;
buyDropdown . classList . add ( 'show' ) ;
openOverlay ( ) ;
}
} ) ;
}
// 2. 搜索弹窗
const searchBtnNew = document . querySelector ( '.header-search-wrapper .header-nav-btn' ) ;
if ( searchBtnNew && searchDropdown ) {
const newSearchBtn = searchBtnNew . cloneNode ( true ) ;
searchBtnNew . parentNode . replaceChild ( newSearchBtn , searchBtnNew ) ;
newSearchBtn . addEventListener ( 'click' , ( e ) => {
e . stopPropagation ( ) ;
const isOpen = searchDropdown . classList . contains ( 'show' ) ;
if ( isOpen ) {
searchDropdown . classList . remove ( 'show' ) ;
closeOverlay ( ) ;
} else {
// 关闭其他弹窗和导航下拉菜单
if ( buyDropdown && buyDropdown . classList . contains ( 'show' ) ) {
buyDropdown . classList . remove ( 'show' ) ;
}
if ( langDropdown && langDropdown . classList . contains ( 'show' ) ) {
langDropdown . classList . remove ( 'show' ) ;
}
// 关闭导航下拉菜单
closeAllNavDropdowns ( ) ;
searchDropdown . classList . add ( 'show' ) ;
openOverlay ( ) ;
if ( typeof renderHistory === 'function' ) {
renderHistory ( ) ;
}
}
} ) ;
}
// 3. 语言弹窗
const langBtnNew = document . querySelector ( '.header-lang-wrapper .header-nav-btn' ) ;
if ( langBtnNew && langDropdown ) {
const newLangBtn = langBtnNew . cloneNode ( true ) ;
langBtnNew . parentNode . replaceChild ( newLangBtn , langBtnNew ) ;
newLangBtn . addEventListener ( 'click' , ( e ) => {
e . stopPropagation ( ) ;
const isOpen = langDropdown . classList . contains ( 'show' ) ;
if ( isOpen ) {
langDropdown . classList . remove ( 'show' ) ;
closeOverlay ( ) ;
} else {
// 关闭其他弹窗和导航下拉菜单
if ( buyDropdown && buyDropdown . classList . contains ( 'show' ) ) {
buyDropdown . classList . remove ( 'show' ) ;
}
if ( searchDropdown && searchDropdown . classList . contains ( 'show' ) ) {
searchDropdown . classList . remove ( 'show' ) ;
}
// 关闭导航下拉菜单
closeAllNavDropdowns ( ) ;
langDropdown . classList . add ( 'show' ) ;
openOverlay ( ) ;
}
} ) ;
}
// 辅助函数:关闭所有导航下拉菜单
function closeAllNavDropdowns ( ) {
// 关闭产品列表导航下拉菜单
const productNavItem = document . querySelector ( '.header-nav-item:first-child' ) ;
if ( productNavItem ) {
const productDropdown = productNavItem . querySelector ( '.header-dropdown' ) ;
if ( productDropdown ) {
productDropdown . style . display = '' ;
}
}
// 关闭其他导航下拉菜单
const otherNavItems = document . querySelectorAll ( '.header-nav-item:not(:first-child)' ) ;
otherNavItems . forEach ( item => {
const dropdown = item . querySelector ( '.header-dropdown' ) ;
if ( dropdown ) {
dropdown . style . display = '' ;
}
} ) ;
}
// ========== 关闭按钮事件重新绑定 ==========
// 语言关闭按钮
const langCloseBtnNew = document . querySelector ( '.header-lang-dropdown-delete-icon' ) ;
if ( langCloseBtnNew && langDropdown ) {
const newLangCloseBtn = langCloseBtnNew . cloneNode ( true ) ;
langCloseBtnNew . parentNode . replaceChild ( newLangCloseBtn , langCloseBtnNew ) ;
newLangCloseBtn . addEventListener ( 'click' , ( e ) => {
e . stopPropagation ( ) ;
langDropdown . classList . remove ( 'show' ) ;
closeOverlay ( ) ;
} ) ;
}
// 搜索关闭按钮
const searchCloseBtnNew = document . querySelector ( '.header-search-dropdown-delete-icon' ) ;
if ( searchCloseBtnNew && searchDropdown ) {
const newSearchCloseBtn = searchCloseBtnNew . cloneNode ( true ) ;
searchCloseBtnNew . parentNode . replaceChild ( newSearchCloseBtn , searchCloseBtnNew ) ;
newSearchCloseBtn . addEventListener ( 'click' , ( e ) => {
e . stopPropagation ( ) ;
searchDropdown . classList . remove ( 'show' ) ;
closeOverlay ( ) ;
} ) ;
}
// ========== 全局点击事件 ==========
document . addEventListener ( 'click' , ( e ) => {
// 检查是否点击在购买区域外
if ( ! e . target . closest ( '.header-buy-wrapper' ) && ! e . target . closest ( '#buyDropdown' ) ) {
if ( buyDropdown && buyDropdown . classList . contains ( 'show' ) ) {
buyDropdown . classList . remove ( 'show' ) ;
closeOverlay ( ) ;
}
}
// 检查是否点击在搜索区域外
if ( ! e . target . closest ( '.header-search-wrapper' ) && ! e . target . closest ( '#searchDropdown' ) ) {
if ( searchDropdown && searchDropdown . classList . contains ( 'show' ) ) {
searchDropdown . classList . remove ( 'show' ) ;
closeOverlay ( ) ;
}
}
// 检查是否点击在语言区域外
if ( ! e . target . closest ( '.header-lang-wrapper' ) && ! e . target . closest ( '#langDropdown' ) ) {
if ( langDropdown && langDropdown . classList . contains ( 'show' ) ) {
langDropdown . classList . remove ( 'show' ) ;
closeOverlay ( ) ;
}
}
// 检查是否点击在导航区域外(如果点击在导航外部,关闭导航下拉菜单)
if ( ! e . target . closest ( '.header-nav-item' ) ) {
closeAllNavDropdowns ( ) ;
closeOverlay ( ) ;
}
} ) ;
// ========== 蒙版层点击事件 ==========
if ( mhk ) {
mhk . addEventListener ( 'click' , ( ) => {
closeAllDropdownsAndOverlay ( ) ;
closeAllNavDropdowns ( ) ;
closeOverlay ( ) ;
} ) ;
}
// ========== 监听弹窗状态变化 ==========
function setupDropdownObserver ( dropdown , name ) {
if ( ! dropdown ) return ;
const observer = new MutationObserver ( ( mutations ) => {
mutations . forEach ( ( mutation ) => {
if ( mutation . attributeName === 'class' ) {
if ( ! dropdown . classList . contains ( 'show' ) ) {
closeOverlay ( ) ;
dropdown . addEventListener ( 'mouseenter' , ( ) => {
isHovering = true ;
clearTimeout ( timer ) ;
showDropdown ( ) ;
} ) ;
dropdown . addEventListener ( 'mouseleave' , ( e ) => {
isHovering = false ;
// 检查鼠标是否移回了导航项
const relatedTarget = e. relatedTarget ;
const isMovingToNavItem = item . contains ( relatedTarget ) ;
if ( isMovingToNavItem ) {
return ;
}
}
timer = setTimeout ( ( ) => {
if ( ! isHovering && ! item . matches ( ':hover' ) ) {
hideDropdown ( ) ;
}
} , 100 ) ;
} ) ;
} ) ;
} ) ;
observer . observe ( dropdown , { attributes : true } ) ;
}
setupDropdownObserver ( buyDropdown , 'buy' ) ;
setupDropdownObserver ( searchDropdown , 'search' ) ;
setupDropdownObserver ( langDropdown , 'lang' ) ;
// ========== 页面卸载时恢复滚动 ==========
window . addEventListener ( 'beforeunload' , ( ) => {
if ( body ) {
body . style . overflow = '' ;
body . style . paddingRight = '' ;
}
} ) ;
// 初始化(在 DOM 加载完成后执行)
if ( document . readyState === 'loading' ) {
document . addEventListener ( 'DOMContentLoaded' , initHeaderDropdown ) ;
} else {
initHeaderDropdown ( ) ;
}
// 确保蒙版层初始状态正确
closeOverlay ( ) ;
< / script >
// 页面卸载时恢复滚动
window . addEventListener ( 'beforeunload' , ( ) => {
if ( body ) {
body . style . position = '' ;
body . style . top = '' ;
body . style . left = '' ;
body . style . right = '' ;
body . style . width = '' ;
}
} ) ;
// // 初始化导航下拉菜单蒙版层
// initNavDropdownOverlay();
// 初始化渲染
renderHistory ( ) ;
closeOverlay ( ) ;
< / script >