You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用visibility与:hover切换导航菜单在触控设备上误触发点击

解决SVG触控设备下拉菜单误触发链接问题

我做了一个SVG下拉导航菜单,鼠标悬停时会显示包含相同路径但不同文本的子菜单组,桌面端用鼠标操作一切正常,但在触控设备上,点击会直接触发原本处于visibility: hidden状态的链接。示例代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 306.12 409.2" preserveAspectRatio="none">
  <defs>
    <style type="text/css">
      @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@500;600');

      .font-md {
        font-family: 'Poppins';
        font-size: 10px;
        font-weight: 500;
      }

      :root {
        --yellow: #f6b034;
      }

      .training {
        fill: var(--yellow);
      }

      .sub-menu {
        opacity: 0;
        visibility: hidden;
        height: 0;
      }

      .sub-menu > text {
        opacity: 0;        
        transition: opacity 0.5s ease-in-out;
      }

      .has-dropdown:hover + .sub-menu,
      .has-dropdown:hover + .sub-menu > text {
        opacity: 1;
        visibility: visible;
        height: auto;
        pointer-events: auto;
        transform: unset;
      }

      .has-dropdown:hover {
        opacity: 0;
        visibility: hidden;
        height: 0;
      }

      .sub-menu:hover {
        opacity: 1;
        visibility: visible;
        height: auto;
        pointer-events: auto;
      }

      .sub-menu:hover > text {
        opacity: 1;
      }
    </style>
  </defs>
  
  <g id="training" class="has-dropdown">
    <path class="training" d="m0,256c0-27.61,22.39-50,50-50,27.61,0,50,22.38,50,49.99,0-.21.02-.42.02-.63v29.39c0,11.06,8.45,20.15,19.25,21.15.02,0,.04.09,0,.09-.04,0-62.38,0-69.27,0,0,0,0,0,0,0s0,0,0,0h-.62c.1,0,.21,0,.31,0C22.22,305.82,0,283.51,0,256Z"/>
    <text style="fill: var(--brown)" class="font-md" transform="translate(31 285)">Training</text>
  </g>
  <g id="training-sub-menu" class="sub-menu">
    <path class="training" d="m0,256c0-27.61,22.39-50,50-50,27.61,0,50,22.38,50,49.99,0-.21.02-.42.02-.63v29.39c0,11.06,8.45,20.15,19.25,21.15.02,0,.04.09,0,.09-.04,0-62.38,0-69.27,0,0,0,0,0,0,0s0,0,0,0h-.62c.1,0,.21,0,.31,0C22.22,305.82,0,283.51,0,256Z"/>
    <text style="fill: var(--brown)" class="font-md sub-menu-item" transform="translate(31 285)">
      <a href="https://google.com" target="_parent">Google</a>  
    </text>
    <text style="fill: var(--brown)" class="font-md sub-menu-item" transform="translate(31 265)">
      <a href="https://google.com" target="_parent">Google</a>  
    </text>
    <text style="fill: var(--brown)" class="font-md sub-menu-item" transform="translate(31 245)">
      <a href="https://google.com" target="_parent">Google</a>  
    </text>
  </g>
</svg>

问题原因

触控设备的点击行为会同时触发:hover和click事件,即便子菜单设置了visibility: hidden,但默认没有禁用指针事件,导致点击穿透直接触发了隐藏的链接。另外visibility: hidden只是隐藏元素,元素仍占据布局空间,触控点击容易命中该区域下的链接。

解决方案

1. 禁用子菜单默认指针事件

给.sub-menu添加pointer-events: none,默认状态下完全禁用子菜单的交互,只有在hover时再开启:

.sub-menu {
  opacity: 0;
  visibility: hidden;
  height: 0;
  pointer-events: none; /* 新增 */
}

2. 适配触控设备的伪类

把:hover相关的选择器补充:focus和:active,确保触控点击父元素时优先触发子菜单显示,而非直接触发子链接:

.has-dropdown:hover + .sub-menu,
.has-dropdown:focus + .sub-menu,
.has-dropdown:active + .sub-menu,
.has-dropdown:hover + .sub-menu > text,
.has-dropdown:focus + .sub-menu > text,
.has-dropdown:active + .sub-menu > text {
  opacity: 1;
  visibility: visible;
  height: auto;
  pointer-events: auto;
  transform: unset;
}

.has-dropdown:hover,
.has-dropdown:focus,
.has-dropdown:active {
  opacity: 0;
  visibility: hidden;
  height: 0;
}

3. 提升可访问性(可选)

给父菜单组添加tabindex="0",让它能响应:focus事件,适配键盘和触控操作:

<g id="training" class="has-dropdown" tabindex="0">
  <path class="training" d="m0,256c0-27.61,22.39-50,50-50,27.61,0,50,22.38,50,49.99,0-.21.02-.42.02-.63v29.39c0,11.06,8.45,20.15,19.25,21.15.02,0,.04.09,0,.09-.04,0-62.38,0-69.27,0,0,0,0,0,0,0s0,0,0,0h-.62c.1,0,.21,0,.31,0C22.22,305.82,0,283.51,0,256Z"/>
  <text style="fill: var(--brown)" class="font-md" transform="translate(31 285)">Training</text>
</g>

修改后的完整代码

<?xml version="1.0" encoding="UTF-8"?>
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 306.12 409.2" preserveAspectRatio="none">
  <defs>
    <style type="text/css">
      @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@500;600');

      .font-md {
        font-family: 'Poppins';
        font-size: 10px;
        font-weight: 500;
      }

      :root {
        --yellow: #f6b034;
      }

      .training {
        fill: var(--yellow);
      }

      .sub-menu {
        opacity: 0;
        visibility: hidden;
        height: 0;
        pointer-events: none;
      }

      .sub-menu > text {
        opacity: 0;        
        transition: opacity 0.5s ease-in-out;
      }

      .has-dropdown:hover + .sub-menu,
      .has-dropdown:focus + .sub-menu,
      .has-dropdown:active + .sub-menu,
      .has-dropdown:hover + .sub-menu > text,
      .has-dropdown:focus + .sub-menu > text,
      .has-dropdown:active + .sub-menu > text {
        opacity: 1;
        visibility: visible;
        height: auto;
        pointer-events: auto;
        transform: unset;
      }

      .has-dropdown:hover,
      .has-dropdown:focus,
      .has-dropdown:active {
        opacity: 0;
        visibility: hidden;
        height: 0;
      }

      .sub-menu:hover {
        opacity: 1;
        visibility: visible;
        height: auto;
        pointer-events: auto;
      }

      .sub-menu:hover > text {
        opacity: 1;
      }
    </style>
  </defs>
  
  <g id="training" class="has-dropdown" tabindex="0">
    <path class="training" d="m0,256c0-27.61,22.39-50,50-50,27.61,0,50,22.38,50,49.99,0-.21.02-.42.02-.63v29.39c0,11.06,8.45,20.15,19.25,21.15.02,0,.04.09,0,.09-.04,0-62.38,0-69.27,0,0,0,0,0,0,0s0,0,0,0h-.62c.1,0,.21,0,.31,0C22.22,305.82,0,283.51,0,256Z"/>
    <text style="fill: var(--brown)" class="font-md" transform="translate(31 285)">Training</text>
  </g>
  <g id="training-sub-menu" class="sub-menu">
    <path class="training" d="m0,256c0-27.61,22.39-50,50-50,27.61,0,50,22.38,50,49.99,0-.21.02-.42.02-.63v29.39c0,11.06,8.45,20.15,19.25,21.15.02,0,.04.09,0,.09-.04,0-62.38,0-69.27,0,0,0,0,0,0,0s0,0,0,0h-.62c.1,0,.21,0,.31,0C22.22,305.82,0,283.51,0,256Z"/>
    <text style="fill: var(--brown)" class="font-md sub-menu-item" transform="translate(31 285)">
      <a href="https://google.com" target="_parent">Google</a>  
    </text>
    <text style="fill: var(--brown)" class="font-md sub-menu-item" transform="translate(31 265)">
      <a href="https://google.com" target="_parent">Google</a>  
    </text>
    <text style="fill: var(--brown)" class="font-md sub-menu-item" transform="translate(31 245)">
      <a href="https://google.com" target="_parent">Google</a>  
    </text>
  </g>
</svg>

内容的提问来源于stack exchange,提问作者jylesy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 01:19:51