Svelte中W3C响应式菜单窄屏时无法显示垂直菜单项
我太懂你这种挫败感了——把W3C官网好好的响应式菜单搬到Svelte里,结果窄屏模式下点汉堡按钮,菜单就是不出来,甚至在Playground里点一下还会刷新页面,折腾半天找不到问题在哪!别着急,咱们一步步来解决:
第一个问题:汉堡按钮点击导致页面刷新
你代码里的汉堡按钮用的是<a href="#">,默认情况下点击a标签会触发页面跳转(哪怕是#也会触发滚动或刷新),这就是Playground里页面刷新的原因。解决办法很简单,要么阻止默认行为,要么干脆把a标签换成更合适的button:
方案1:阻止a标签默认行为
修改汉堡按钮的onclick事件,加上event.preventDefault():
<a href="#" class="icon" onclick={(event) => { event.preventDefault(); myFunction(); }}>☰</a>
方案2:换成button标签(更推荐,因为这是按钮行为)
把a标签改成button,然后调整样式去掉默认按钮的边框、背景:
<button class="icon" onclick={myFunction}>☰</button>
然后在style里给button.icon加样式:
.topnav .icon { display: none; border: none; background: transparent; cursor: pointer; }
第二个问题:DOM操作的可靠性问题
你用document.getElementById("myTopnav")来获取元素,在Svelte里有时候会遇到DOM还没渲染完成就执行代码的情况,导致获取不到元素。咱们用Svelte自带的元素绑定来替代,更可靠:
- 在topnav元素上绑定变量:
<div class="topnav" bind:this={topnav}>
- 在script里声明变量,然后修改myFunction:
<script> let topnav; // 绑定的DOM元素 function myFunction() { if (!topnav) return; // 防止元素未加载 topnav.classList.toggle("responsive"); // 直接用toggle简化代码,不用判断加/减 console.log(topnav.className); } </script>
这里用classList.toggle()还能简化你的判断逻辑,一行代码搞定添加/移除类。
第三个问题:确认CSS样式是否正确生效
检查你的媒体查询样式,确保.topnav.responsive相关的样式能正确覆盖窄屏时的display:none。你现有的CSS看起来是对的,但如果还是有问题,可以把样式放到Svelte的全局样式文件里(比如app.css),或者在组件样式里用:global()包裹(不过一般组件内的样式只要元素在组件里就没问题)。
修改后的完整代码示例
<div class="topnav" bind:this={topnav}> <a href="#home" class="active">Home</a> <a href="#news">News</a> <a href="#contact">Contact</a> <div class="dropdown"> <button class="dropbtn">Dropdown <i class="fa fa-caret-down"></i> </button> <div class="dropdown-content"> <a href="#">Link 1</a> <a href="#">Link 2</a> <a href="#">Link 3</a> </div> </div> <a href="#about">About</a> <!-- 用阻止默认行为的a标签 --> <a href="#" class="icon" onclick={(event) => { event.preventDefault(); myFunction(); }}>☰</a> </div> <script> let topnav; function myFunction() { if (!topnav) return; topnav.classList.toggle("responsive"); console.log(topnav.className); } </script> <style> .topnav { overflow: hidden; background-color: #333; } .topnav a { font-size: 17px; float: left; display: block; color: #f2f2f2; text-align: center; padding: 14px 16px; text-decoration: none; } .active { background-color: #04AA6D; color: white; } .topnav .icon { display: none; } .dropdown { float: left; overflow: hidden; } .dropdown .dropbtn { font-size: 17px; border: none; outline: none; color: white; padding: 14px 16px; background-color: inherit; font-family: inherit; margin: 0; } .dropdown-content { display: none; position: absolute; background-color: #f9f9f9; min-width: 160px; box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); z-index: 1; } .dropdown-content a { float: none; color: black; padding: 14px 16px; text-decoration: none; display: block; text-align: left; } .topnav a:hover, .dropdown:hover .dropbtn { background-color: #555; color: white; } .dropdown-content a:hover { background-color: #ddd; color: black; } .dropdown:hover .dropdown-content { display: block; } @media screen and (max-width: 600px) { .topnav a:not(:first-child), .dropdown .dropbtn { display: none; } .topnav a.icon { float: right; display: block; } } @media screen and (max-width: 600px) { .topnav.responsive {position: relative;} .topnav.responsive .icon { position: absolute; right: 0; top: 0; } .topnav.responsive a { float: none; display: block; text-align: left; } .topnav.responsive .dropdown {float: none;} .topnav.responsive .dropdown-content {position: relative;} .topnav.responsive .dropdown .dropbtn { display: block; width: 100%; text-align: left; } } </style>
这样修改后,窄屏模式下点击汉堡按钮应该就能正常展开垂直菜单了,也不会出现页面刷新的问题。
备注:内容来源于stack exchange,提问作者Chris

