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

如何实现点击页面任意位置关闭下拉菜单?

实现点击页面任意位置关闭语言切换下拉菜单

现有基于jQuery的语言切换下拉菜单,仅点击下拉选项时能关闭菜单,需求是点击页面任意位置都可关闭该菜单。

实现步骤

  1. 封装关闭逻辑:将关闭菜单的代码抽成独立函数,避免重复编写
  2. 阻止菜单区域事件冒泡:点击下拉菜单自身时,阻止事件传递到document,防止菜单刚打开就关闭
  3. 绑定document点击事件:判断点击目标是否属于下拉菜单区域,若不属于则触发关闭逻辑

修改后的完整代码

JavaScript 部分

let el = $('.switch');
let cur = el.find('.current');
let options = el.find('.options li');
let content = $('#content');

// 封装关闭下拉菜单的函数
function closeDropdown() {
  if (!el.hasClass('show-options')) return;
  el.removeClass('anim-options');
  el.removeClass('show-shadow');
  
  setTimeout(function() {
    el.removeClass('show-options');
  }, 600);
}

// 切换下拉菜单显示/隐藏状态
el.on('click', function(e) {
  // 阻止事件冒泡,避免触发document的点击事件
  e.stopPropagation();
  if (el.hasClass('show-options')) {
    closeDropdown();
  } else {
    el.addClass('show-options');
    
    setTimeout(function() {
      el.addClass('anim-options');
    }, 50);
    
    setTimeout(function() {
      el.addClass('show-shadow');
    }, 200);
  }
});

// 点击选项切换语言并关闭菜单
options.on('click', function(e) {
  e.stopPropagation();
  
  let newLang = $(this).data('lang');
  
  cur.find('span').text(newLang);
  content.attr('class', newLang);
  
  setLang(newLang);
  
  options.removeClass('selected');
  $(this).addClass('selected');
  
  closeDropdown();
});

// 点击页面其他区域关闭下拉菜单
$(document).on('click', function(e) {
  if (!$(e.target).closest('.switch').length) {
    closeDropdown();
  }
});

// 从本地存储读取当前语言
function getLang() {
  let lang;
  if (localStorage.getItem('currentLang') === null) {
    lang = cur.find('span').text();
  } else {
    lang = JSON.parse(localStorage.getItem('currentLang')).toLowerCase();
  }
  
  cur.find('span').text(lang);
  options.parent().find(`li[data-lang="${lang}"]`).addClass('selected');
  
  content.attr('class', lang);
}

getLang();

// 将选中语言保存到本地存储
function setLang(newLang) {
  localStorage.setItem('currentLang', JSON.stringify(newLang).toLowerCase());
  
  content.attr('class', newLang);
}

CSS 部分

@import url("https://fonts.googleapis.com/css?family=Playfair+Display:400,900|Noto+Sans:400,400i,700&subset=latin-ext");
:root {
  font-size: 100%;
  --base-font-size: calc(15px + .5vw);
  --ratio: 1.18;
  --h6: calc(var(--base-font-size) * var(--ratio));
  --h5: calc(var(--h6) * var(--ratio));
  --h4: calc(var(--h5) * var(--ratio));
  --h3: calc(var(--h4) * var(--ratio));
  --h2: calc(var(--h3) * var(--ratio));
  --h1: calc(var(--h2) * var(--ratio));
}

body {
  font-family: "Noto Sans", Arial, sans-serif;
  font-size: 1.2rem;
  font-size: var(--base-font-size);
  line-height: 1.6;
  color: #313436;
}

#content {
  max-width: 48rem;
  margin: 0 auto;
  text-align: left;
  padding: 1rem 2rem;
}
#content:not(.en) [data-lang=en], #content:not(.sv) [data-lang=sv]{
  display: none;
}
#content.en [data-lang=en],#content.sv [data-lang=sv]{
  display: block;
}

h1 {
  font-family: "Playfair Display", "Noto Sans", Arial, sans-serif;
  font-weight: 900;
  font-size: var(--h1);
  line-height: calc(var(--h1) * 1.2);
}

h2 {
  font-size: var(--h2);
  line-height: calc(var(--h2) * 1.2);
}

h3 {
  font-size: var(--h3);
  line-height: calc(var(--h3) * 1.3);
}

h4 {
  font-size: var(--h4);
  line-height: calc(var(--h4) * 1.3);
}

h5 {
  font-size: var(--h5);
  line-height: calc(var(--h5) * 1.4);
}

h6 {
  font-size: var(--h6);
  line-height: calc(var(--h6) * 1.5);
}

.main-header {
  background-color: #3498DB;
  color: #FFFFFF;
  height: 56px;
  display: flex;
  padding: 0 2rem;
  box-shadow: 0 2px 12px -4px rgba(0, 0, 0, 0.4);
}
.main-header h1 {
  font-size: 1.25rem;
  line-height: 1.35;
  font-family: "Noto Sans", Arial, sans-serif;
}
.main-header .switch {
  margin-left: auto;
}

.switch {
  position: relative;
  cursor: pointer;
  display: flex;
  align-items: center;
}

.current {
  text-transform: uppercase;
  height: 42px;
  position: relative;
  z-index: 1;
  font-size: 1rem;
  text-align: center;
  line-height: 42px;
  display: flex;
  background-color: rgba(0, 0, 0, 0);
  transition: all 0.2s ease-out;
  border-radius: 3px;
  padding: 0 0.2em 0 0.7em;
}
.current span {
  display: inline-block;
  line-height: 1;
  padding: 0.7em 0.2em 0 0;
}
.current .arrow {
  display: inline-block;
  width: 20px;
  height: 20px;
}
.current:hover {
  background-color: rgba(0, 0, 0, 0.1);
}

.options {
  position: absolute;
  right: 0;
  top: 0.6em;
  z-index: 0;
  opacity: 0;
  transition: all 0.36s ease-out;
  display: none;
  overflow: hidden;
  box-shadow: 0 0 0 0 rgba(0, 0, 0, 0);
}
.switch.show-options .options {
  display: block;
  z-index: 5;
}
.switch.anim-options .options {
  opacity: 1;
}
.switch.show-shadow .options {
  box-shadow: 0 2px 12px -4px rgba(0, 0, 0, 0.4);
}

.options-list {
  color: #313436;
  margin: 0;
  padding: 0.4rem 0.8em;
  position: relative;
  z-index: 5;
}
.options-list li {
  list-style: none;
  padding: 0;
  margin: 0;
  padding: 0.75em;
  border-bottom: 1px solid #E1E4E6;
  transform: translateX(10px);
  transition: all 0.24s ease-out;
  transition-delay: 0.1s;
  opacity: 0;
  font-size: 1rem;
  line-height: 1.3;
  white-space: nowrap;
}
.options-list li:last-child {
  border-bottom: none;
}
.options-list li:nth-child(1) {
  transition-delay: 0.1s;
}
.options-list li:nth-child(2) {
  transition-delay: 0.15s;
}
.options-list li:nth-child(3) {
  transition-delay: 0.2s;
}
.options-list li:nth-child(4) {
  transition-delay: 0.25s;
}
.options-list li:nth-child(5) {
  transition-delay: 0.3s;
}
.options-list li.selected {
  color: #3498DB;
}
.switch.anim-options .options-list li {
  transform: translateX(0);
  opacity: 1;
}

#trans-circle {
  width: 80px;
  height: 80px;
  position: absolute;
  top: -80px;
  right: -80px;
  padding: 0;
  margin: 0;
  border: none;
  z-index: 1;
  transition: all 0.4s ease-out;
  transform: scale(0.5);
  overflow: hidden;
}
#trans-circle svg {
  max-width: 100%;
  max-height: 100%;
}

.switch.anim-options #trans-circle {
  transform: scale(9);
}

HTML 部分

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css">

<header class="main-header">
  <h1 data-lang="en">Language Switch</h1>
  <h1 data-lang="sv">Språkbyte</h1>
  
  <div class="switch">
    <div class="current">
      <span>en</span>
      <em class="arrow"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18"><title>ic_arrow_drop_down_18px</title>
        <g fill="#FFFFFF">
          <path d="M5 8l4 4 4-4z"></path>
        </g>
      </svg></em>
    </div>
    <div class="options">
      <ul class="options-list">
        <li data-lang="en">English</li>
        <li data-lang="sv">Svenska</li>
      </ul>
      <div id="trans-circle"> 
        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 120"> 
          <g id="circle" fill="none" fill-rule="evenodd"> 
            <circle id="bg" cx="60" cy="60" r="60" fill="#FFFFFF"/> 
          </g> 
        </svg>
      </div>
    </div>
  </div>
</header>

<main id="content" class="en">
  <h1 data-lang="en">Design</h1>
  <h1 data-lang="sv">konstruktion</h1>
</main>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 08:02:03