纯 JS 幻灯片菜单,能够“在菜单外部单击”关闭它

2024-02-05

我正在尝试重新表述我的问题,并将完成我所做的所有步骤,特别是我失败的地方。我对 JS 的了解并不深,但有通过实践学习的意愿以及社区的帮助。

我偶然发现这个答案 https://stackoverflow.com/a/38317768/5597188并实现了好处。因为我不想使用 jQuery,所以我开始用 JS 重写它。

  1. 第一步是编写一个基本的简单函数,以在“单击”时打开菜单,并在使用模糊()在焦点元素外部单击时关闭菜单;方法。

参考 jQuery 代码@zzzzBov https://stackoverflow.com/a/38317768/5597188 :

$('a').on('click', function () {
  $(this.hash).toggleClass('active').focus();
});

$('div').on('focusout', function () {
  $(this).removeClass('active');
});

我的JS代码:

var navToggle = document.getElementsByClassName('js-site-nav-btn--toggle')[0];
var navMenu = document.getElementsByClassName('js-site-nav')[0];

navToggle.addEventListener('click', function() {
  this.focus();
  navMenu.classList.toggle('js-site-nav--open');
});

navMenu.addEventListener('blur', function() {
  this.classList.remove('js-site-nav--open');
}, true);

打开菜单有效,问题是,如果之前单击了焦点元素(菜单)一次,它只会在菜单外部“单击”时关闭:

var navToggle = document.getElementsByClassName('js-site-nav-btn--toggle')[0];
var navMenu = document.getElementsByClassName('js-site-nav')[0];

navToggle.addEventListener('click', function() {
  this.focus();
  navMenu.classList.toggle('js-site-nav--open');
});

navMenu.addEventListener('blur', function() {
  this.classList.remove('js-site-nav--open');
}, true);
.c-site-nav {
  color: black;
  list-style-type: none;
  padding-top: 20px;
  position: fixed;
  overflow: hidden;
  top: 0;
  right: -200px;
  width: 200px;
  height: 100%;
  transition: right .6s cubic-bezier(0.190, 1.000, 0.220, 1.000);
  opacity: .9;
  background-color: green;
}
.js-site-nav--open {
  right: 0;
}
.c-site-nav-btn:hover {
  cursor: pointer;
  background-color: red;
}
.c-site-nav-btn {
  position: fixed;
  top: 20px;
  right: 20px;
  border: 0;
  outline: 0;
  background-color: black;
  position: fixed;
  width: 40px;
  height: 40px;
}
.c-site-nav-btn__line {
  width: 20px;
  height: 2px;
  background-color: white;
  display: block;
  margin: 5px auto;
}
<button class="c-site-nav-btn js-site-nav-btn--toggle">
  <span class="c-site-nav-btn__line"></span>
  <span class="c-site-nav-btn__line"></span>
  <span class="c-site-nav-btn__line"></span>
</button>
<nav class="c-site-nav js-site-nav" tabindex="-1" role="navigation">
  <ul class="c-site-nav__menu">
    <li>
      <a class="c-site-nav__item js-site-nav__item" href="/">TOPMENU</a>
    </li>
    <li>SUBMENU
      <ul>
        <li>
          <a class="c-site-nav__item js-site-nav__item" href="/">MENU</a>
        </li>
        <li>
          <a class="c-site-nav__item js-site-nav__item" href="/">MENU</a>
        </li>
        <li>
          <a class="c-site-nav__item js-site-nav__item" href="/">MENU</a>
        </li>
      </ul>
    </li>
    <li>
      <a class="c-site-nav__item js-site-nav__item" href="/portfolio">TOPMENU</a>
    </li>
  </ul>
</nav>
  1. 我尝试继续第二步,即解决两个主要问题:

首先是对话框中的链接不可单击。尝试 单击它或按 Tab 键将导致对话框在 发生交互作用。这是因为聚焦内部元素 在再次触发 focusin 事件之前触发 focusout 事件。

修复方法是在事件循环中对状态更改进行排队。这可以是 通过在浏览器中使用 setImmediate(...) 或 setTimeout(..., 0) 来完成 不支持 setImmediate。一旦排队,它可以被取消 后续重点:

第二个问题是链接打开时对话框不会关闭 再次按下。这是因为对话框失去焦点,触发 关闭行为,之后单击链接会触发对话框 重新打开。

与上一期类似,焦点状态需要管理。 鉴于状态更改已经排队,这只是一个 处理对话框触发器上的焦点事件的问题:

参考 jQuery 代码@zzzzBov https://stackoverflow.com/a/38317768/5597188 :

$('a').on('click', function () {
  $(this.hash).toggleClass('active').focus();
});

$('div').on({
  focusout: function () {
    $(this).data('timer', setTimeout(function () {
      $(this).removeClass('active');
    }.bind(this), 0));
  },
  focusin: function () {
    clearTimeout($(this).data('timer'));
  }
});

$('a').on({
  focusout: function () {
    $(this.hash).data('timer', setTimeout(function () {
      $(this.hash).removeClass('active');
    }.bind(this), 0));
  },
  focusin: function () {
    clearTimeout($(this.hash).data('timer'));  
  }
});

我的JS代码:

var navToggle = document.getElementsByClassName('js-site-nav-btn--toggle')[0];
var navMenu = document.getElementsByClassName('js-site-nav')[0];
var navLink = document.getElementsByClassName('js-site-nav__item')[0];

navToggle.addEventListener('click', function() {
  this.focus();
  navMenu.classList.toggle('js-site-nav--open');
});

navMenu.addEventListener('focus', function() {
  this.blur(function() {
    setTimeout(function() {
      this.classList.remove('js-site-nav--open');
    }.bind(this), 0);
  });
  this.focus(function() {
    clearTimeout();
  });
});

navLink.addEventListener('blur', function() {
  navLink.blur(function() {
    setTimeout(function() {
      navMenu.classList.remove('js-site-nav--open');
    }.bind(), 0);
  });
  navLink.focus(function() {
    clearTimeout();
  });
});

打开菜单仍然有效,但关闭外部点击停止工作,经过研究,我认为模糊和聚焦是正确的方法,但我想我错过了一些重要的东西。

var navToggle = document.getElementsByClassName('js-site-nav-btn--toggle')[0];
var navMenu = document.getElementsByClassName('js-site-nav')[0];
var navLink = document.getElementsByClassName('js-site-nav__item')[0];

navToggle.addEventListener('click', function() {
  this.focus();
  navMenu.classList.toggle('js-site-nav--open');
});

navMenu.addEventListener('focus', function() {
  this.blur(function() {
    setTimeout(function() {
      this.classList.remove('js-site-nav--open');
    }.bind(this), 0);
  });
  this.focus(function() {
    clearTimeout();
  });
});

navLink.addEventListener('blur', function() {
  navLink.blur(function() {
    setTimeout(function() {
      navMenu.classList.remove('js-site-nav--open');
    }.bind(), 0);
  });
  navLink.focus(function() {
    clearTimeout();
  });
});
.c-site-nav {
  color: black;
  list-style-type: none;
  padding-top: 20px;
  position: fixed;
  overflow: hidden;
  top: 0;
  right: -200px;
  width: 200px;
  height: 100%;
  transition: right .6s cubic-bezier(0.190, 1.000, 0.220, 1.000);
  opacity: .9;
  background-color: green;
}
.js-site-nav--open {
  right: 0;
}
.c-site-nav-btn:hover {
  cursor: pointer;
  background-color: red;
}
.c-site-nav-btn {
  position: fixed;
  top: 20px;
  right: 20px;
  border: 0;
  outline: 0;
  background-color: black;
  position: fixed;
  width: 40px;
  height: 40px;
  z-index:9999;
}
.c-site-nav-btn__line {
  width: 20px;
  height: 2px;
  background-color: white;
  display: block;
  margin: 5px auto;
}
<button class="c-site-nav-btn js-site-nav-btn--toggle">
  <span class="c-site-nav-btn__line"></span>
  <span class="c-site-nav-btn__line"></span>
  <span class="c-site-nav-btn__line"></span>
</button>
<nav class="c-site-nav js-site-nav" tabindex="-1" role="navigation">
  <ul class="c-site-nav__menu">
    <li>
      <a class="c-site-nav__item js-site-nav__item" href="/">TOPMENU</a>
    </li>
    <li>SUBMENU
      <ul>
        <li>
          <a class="c-site-nav__item js-site-nav__item" href="/">MENU</a>
        </li>
        <li>
          <a class="c-site-nav__item js-site-nav__item" href="/">MENU</a>
        </li>
        <li>
          <a class="c-site-nav__item js-site-nav__item" href="/">MENU</a>
        </li>
      </ul>
    </li>
    <li>
      <a class="c-site-nav__item js-site-nav__item" href="/portfolio">TOPMENU</a>
    </li>
  </ul>
</nav>

我确信我还有很多东西需要学习,但非常感谢您的帮助。非常感谢大家。


你可以把焦点放在navmenu一旦显示。如果用户在其外部单击,blur事件将被触发并且菜单将被删除。由于点击链接也会触发blur事件,当用户单击菜单内的任意位置时,我们必须将菜单保留在屏幕上。这可以通过以下方式进行监控:isMouseDown flag.

这是问题第 1 部分中给出的代码片段的增强版本。

var navToggle = document.getElementsByClassName('js-site-nav-btn--toggle')[0];
var navMenu = document.getElementsByClassName('js-site-nav')[0];
var isMouseDown = false;

navToggle.addEventListener('click', function() {
  this.focus();
  navMenu.classList.toggle('js-site-nav--open');
  navMenu.focus();
});

navMenu.addEventListener('mousedown', function() {
  isMouseDown = true;  
});

navMenu.addEventListener('mouseup', function() {
  isMouseDown = false;  
});

navMenu.addEventListener('mouseleave', function() {
  isMouseDown = false;  
});

navMenu.addEventListener('blur', function() {
  if (!isMouseDown) {
    navMenu.classList.remove('js-site-nav--open');
  }
}, true);
.c-site-nav {
  color: black;
  list-style-type: none;
  padding-top: 20px;
  position: fixed;
  overflow: hidden;
  top: 0;
  right: -200px;
  width: 200px;
  height: 100%;
  transition: right .6s cubic-bezier(0.190, 1.000, 0.220, 1.000);
  opacity: .9;
  background-color: green;
}
.js-site-nav--open {
  right: 0;
}
.c-site-nav-btn:hover {
  cursor: pointer;
  background-color: red;
}
.c-site-nav-btn {
  position: fixed;
  top: 20px;
  right: 20px;
  border: 0;
  outline: 0;
  background-color: black;
  position: fixed;
  width: 40px;
  height: 40px;
}
.c-site-nav-btn__line {
  width: 20px;
  height: 2px;
  background-color: white;
  display: block;
  margin: 5px auto;
}
<button class="c-site-nav-btn js-site-nav-btn--toggle">
  <span class="c-site-nav-btn__line"></span>
  <span class="c-site-nav-btn__line"></span>
  <span class="c-site-nav-btn__line"></span>
</button>
<nav class="c-site-nav js-site-nav" tabindex="-1" role="navigation">
  <ul class="c-site-nav__menu">
    <li>
      <a class="c-site-nav__item js-site-nav__item" href="/">TOPMENU</a>
    </li>
    <li>SUBMENU
      <ul>
        <li>
          <a class="c-site-nav__item js-site-nav__item" href="/">MENU</a>
        </li>
        <li>
          <a class="c-site-nav__item js-site-nav__item" href="/">MENU</a>
        </li>
        <li>
          <a class="c-site-nav__item js-site-nav__item" href="/">MENU</a>
        </li>
      </ul>
    </li>
    <li>
      <a class="c-site-nav__item js-site-nav__item" href="/portfolio">TOPMENU</a>
    </li>
  </ul>
</nav>
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

纯 JS 幻灯片菜单,能够“在菜单外部单击”关闭它 的相关文章

  • 解密签名并验证 JWT

    我知道还有其他库可以让我更轻松地使用 JWT 在 Node js 中 在本例中 我使用 crypto js 以手动方式学习 JWT 以下给了我令牌 var header alg HS256 typ JWT var wordArrayHead
  • 如何在 django 表单中设置自定义 HTML 属性?

    我有一个 Django 表单 它是页面的一部分 假设我有一个字段 search input forms CharField u Search word required False 我只能通过模板访问它 form search input
  • Django CBV表单提交返回的JSON显示为新页面

    我正在使用 Django 3 2 我正在创建一个简单的时事通讯订阅表格 表单提交将 JSON 返回到前端 然后应该使用前端来更新页面的部分内容 但是 当我发布表单时 JSON 字符串将在新页面上显示为文本 这是调用视图的路由 urlpatt
  • 表格中与文本一起内嵌 D3 迷你图

    假设有一个这样的表 var data Orange Orange 6 3 3 2 5 Apple Red 6 2 6 5 5 Grape Purple 9 1 2 3 1 我希望将字符串表示为字符串 但将数字数组表示为 D3 折线图 如果这
  • 如何使用 Nokogiri 漂亮地打印 HTML?

    我用 Ruby 编写了一个网络爬虫 并且正在使用Nokogiri HTML来解析页面 我需要打印该页面 在 IRB 中闲逛时 我注意到一个pretty print方法 然而它需要一个参数 我不知道它想要什么 我的爬虫正在缓存网页的 HTML
  • 将 HTML 编辑器的内容保存为桌面上的 HTML 文件

    我想通过单击按钮来保存 TinyMce HTML 编辑器的内容 TinyMce 是本地安装的 我在 Chrome 中使用它 我见过这个answer https stackoverflow com a 30740104 3154274然后on
  • d3 饼图中的文本被路径覆盖

    我正在尝试按照 d3 的饼图示例进行操作http bl ocks org mbostock 3887235 http bl ocks org mbostock 3887235 我的代码的最小示例 const container graph
  • fancybox - 如何添加打开图像的链接?

    大伙计们有什么想法吗 我正在尝试链接 fancybox 中打开的图像 我到处都找遍了 听起来很简单 这是我正在使用的代码 a href img src example thumb png alt example a
  • 在 Promise 中中止 ajax 请求

    我正在构建一个表单验证并学习承诺 我决定使用承诺模式实现异步验证函数 var validateAjax function value return new Promise function resolve reject ajax data
  • JavaScript:String 和 Array 上的 indexOf 方法的效率差异

    我很好奇效率是否存在差异indexOf两者都可用的方法Array and String在 JavaScript 中 我以为indexOf在 String 上的效率低于在 Array 上的效率 而我的new测试结果支持了这一点 例如 var
  • 如何将嵌套对象数组转换为 CSV?

    我有一个包含嵌套对象的数组 例如 name 1 children name 1 1 children 1 2 id 2 thing name 2 1 children 2 2 name 3 stuff name 3 1 children 3
  • 如果没有其他函数链接到承诺,则默认行为

    我想打开一个 确认您要取消对话框 如果没有链接其他功能 则默认导航回页面 window history back 如果我传递回调 我可能会这样做 function openCancelModal form callback if form
  • 如何取消 ComponentWillUnmount 中的所有请求?

    根据docs https facebook github io react docs react component html componentwillunmount ComponentWillUnmount 能够取消请求 我有一个页面发
  • 但为什么浏览器 DOM 经过 10 年的努力仍然这么慢?

    Web 浏览器 DOM 自 90 年代末以来就已存在 但它仍然是性能 速度方面最大的限制之一 我们拥有来自 Google Mozilla Microsoft Opera W3C 和其他各种组织的一些世界上最聪明的人才 为我们所有人致力于 W
  • 在CSS中重置Margins/Padding时使用*是错误的吗?

    应该避免以下内容 还是应该因其简单性而受到赞扬 作为记录 我在我构建的每个站点中都使用它 但我注意到它并不存在于许多主流 CSS 重置框架中 他们是否也不使用它 margin 0 padding 0 最好不要使用它 因为它会导致表单元素出现
  • 电子邮件通讯未正确呈现

    我是 CSS 和 HTML 新手 我有一个包含 HTML 和 CSS 代码的电子邮件模板 我在新闻通讯模板的右上角放置了一个粉色圆圈框 http www pedersenshotell se newsletter http www pede
  • 使用 JSON 传递 HTML

    我正在使用 JSON 将数据传递到 iPhone 和 iPad 数据的一个字段是 HTML 问题是编码 这是我得到的回复 gt GadgetHTML strong Hello strong gt from Catworld br n img
  • 如何在 jest 中测试调用和应用函数?

    这是我的callnapply js file const callAndApply caller object method nameArg ageArg tShirtSizeArg method call object nameArg a
  • 获取类的公共属性而不创建它的实例?

    假设我们有一个 JavaScript 类 var Person function function Person name surname this name name this surname surname Person prototy
  • axios在自调用函数内部只调用一次(Internet Explorer)

    我有一个函数每 2 5 秒调用自己一次来检查后台运行的任务 它调用 axiosget如果响应错误 则返回一个 url 如果响应成功 我将停止该函数 这在 Chrome 和 Mozilla 上完美运行 但由于某种原因 它在 IE 版本 11

随机推荐

  • 在文件夹中查找最新文件并打开它(vba 访问)

    我正在尝试使用以下代码通过访问按钮宏打开文件夹中的最新文件 使用 if 语句进行测试 没有发现任何问题 但是一旦我使用了 do while 我就收到了运行时 6 溢出的错误消息 does len dir 不能使用循环 下面是我的代码 Pri
  • 在终端中,将多个文件夹合并为一个

    我有一个由 WDBackup 西部数据外置硬盘备份实用程序 创建的备份目录 其中包含每天备份的目录以及备份内容的增量内容 所以层次结构看起来像这样 20100101 My Documents Letter1 doc My Music Bes
  • 根据从 WSDL 中提取的多个模式验证 XML

    我尝试使用 SAAJ 手动处理 Web 服务请求 但仍然根据 Web 服务的 WSDL 架构验证接收到的请求 这个特定的 WSDL 包含多个元素 我使用 wsdl4j 提取这些元素 当我尝试创建新的验证器时 我收到一条错误消息 说明该验证器
  • Android:监听电源键按下

    我目前正在尝试监听何时按下电源按钮 最终 我希望在按两次电源按钮时运行一些代码 以检查屏幕是否锁定或解锁 我目前有这个 Override public void onCreate Bundle savedInstanceState supe
  • 在 datagridview 中右对齐列不起作用

    我有一个datagridiview这是动态绑定到datatable 我想将标题中的一些列右对齐 我尝试了这个设置datagridview对于单元格样式和标题单元格 对于单元格样式 它显示正确 但对于标题 它不是 我使用的代码 this da
  • 在 GitHub 中编辑 git 提交消息

    有没有办法在线编辑提交消息GitHub com 提交后 从命令行 人们可以做 git commit amend m New commit message 正如以下问题中正确建议的那样 如何修改现有的 未推送的提交消息 https stack
  • 如何防止表单被提交?

    我有一个表单 其中某处有一个提交按钮 但是 我想以某种方式 捕获 提交事件并防止其发生 我有什么办法可以做到这一点吗 我无法修改提交按钮 因为它是自定义控件的一部分 与其他答案不同 返回false只是part的答案 考虑一下在 return
  • 在 Yii 中创建三个以上表的关系

    当我尝试在 Yii 中创建三个以上表的关系时 以下方法给我带来了麻烦 public function relations return array info gt array self BELONGS TO Software ITEM ID
  • Swift 可以从异步 Void 返回块返回值吗?

    我想创建一个函数来检查 user id 是否已在我的数据库中 class func checkIfUserExsits uid String gt Bool userRef childByAppendingPath uid observeS
  • printf 的包装

    我在Arduino下编码 我想开发串行打印格式化功能 所以我尝试使用sprintf未知大小的缓冲区 基本上 我们可以避免谈论 Arduino 及其串行输出 并考虑将文本写入缓冲区 然后使用printf 我试过这个 include
  • matplotlib.mplot3d 立方体表面的底图

    正如标题所示 我试图在 matplotlib mplot3d 线图的 z 0 表面上绘制底图地图 我知道 Axes3D 对象能够在 z 0 表面上绘图 通过 Axes3D plot Axes3D scatter 等 但我不知道如何使用 Ba
  • 从一组坐标中找到最接近的坐标

    我有大约 1000 组地理坐标 纬度 经度 给定一个坐标 我想从该集合中找到最接近的一个 我的方法是测量距离 但每秒数百个请求对于服务器执行所有数学运算来说可能有点粗糙 对此最好的优化解决方案是什么 Thanks 您将想要使用 最近邻算法
  • 在 setter 方法的情况下,为什么 irb 会回显赋值的右侧而不是返回值?

    令人惊讶的是 在所有其他情况下 irb 都会回显方法的返回值 为什么通过 setter 进行分配的行为会有所不同 我正在使用 Ruby 2 2 2 irb main 001 0 gt def x value puts puts from x
  • 统计同一张表上多个条件的SQL

    我正在尝试编写一个查询 返回在 2020 年 1 月 1 日之后购买 镜子 或 自行车 并且在 12 个月内还购买了 书 或 沙发 的客户 使用计数以防他们购买多次 我编写的查询不完整 因此我需要一些帮助 我缺少计算未来 12 个月内订单数
  • 如何将 LINQ 查询相互附加?

    我有一个表单 可以根据他们选择的内容过滤数据 我正在尝试将 linq 查询相互附加 以便最终结果是他们在屏幕上选择的结果 这是我的代码 private void button Search Click object sender Event
  • Spring JPA:从异步方法处理时数据未保存到数据库

    我有一个使用 Spring JPA 的应用程序 并使用 Async spring 注释执行一些后台进程 该过程需要将参数保存在数据库中 或者如果参数已经存在则更新数据库 这不能正常工作 因为在 Async 方法完成后我的数据实体没有保留在数
  • beans.xml 上的文件过早结束

    xml 的内容
  • 通过 Rails 3 中的范围进行预加载

    我一直在尝试根据 Rails3 应用程序中的某些范围急切加载关联 但找不到任何解决方案 我的应用程序有以下型号 class Project has many entries has many to dos class ToDo has ma
  • 强制触摸 UITableViewCell 内的 UICollectionView

    我有一个带有 Tableview 的 viewController 多个 TableViewCell 并且在每个 TableViewCell 中都有一个带有多个 UICollectionViewItems 的 UICollectionVie
  • 纯 JS 幻灯片菜单,能够“在菜单外部单击”关闭它

    我正在尝试重新表述我的问题 并将完成我所做的所有步骤 特别是我失败的地方 我对 JS 的了解并不深 但有通过实践学习的意愿以及社区的帮助 我偶然发现这个答案 https stackoverflow com a 38317768 559718