纯JS幻灯片菜单,关闭它的能力"点击菜单外的#34;

时间:2017-01-03 16:21:10

标签: javascript jquery html css

我正在尝试重新解释我的问题,并将完成我所做的所有步骤,特别是在我失败的地方。我不是很了解JS,而是通过实践和社区的帮助来学习。

我偶然发现了this answer并意识到了这个好处。由于我不想使用jQuery,我开始在JS中重写它。

  1. 第一步是写一个基本的简单功能,打开菜单点击'并使用blur()在关注元素外部的单击上关闭它;方法
  2. 引用@zzzzBov的jQuery代码:

    $('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>
    &#13;
    &#13;
    &#13;

    1. 我尝试继续第二步,即解决两个主要问题:
    2.   

      首先,对话框中的链接不可点击。尝试   单击它或选项卡将导致对话框关闭之前   互动发生。这是因为聚焦内部元素   在再次触发focusin事件之前触发焦点事件。

           

      修复是在事件循环上对状态更改进行排队。这可以   通过对浏览器使用setImmediate(...)或setTimeout(...,0)来完成   不支持setImmediate。排队后,可以通过a取消   随后关注:

           

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

           

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

      引用@zzzzBov的jQuery代码:

      $('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();
        });
      });
      

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

      &#13;
      &#13;
      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();
        });
      });
      &#13;
      .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;
      }
      &#13;
      <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>
      &#13;
      &#13;
      &#13;

      我相信我还有很多东西需要学习,但我们将非常感谢帮助。非常感谢你们。

3 个答案:

答案 0 :(得分:7)

您可以在显示后立即将焦点设置在navmenu上。如果用户点击它之外,将触发blur事件并删除菜单。由于点击链接也会触发blur事件,因此当用户点击菜单内的任何位置时,我们必须将菜单保留在屏幕上。可以使用isMouseDown标志来监控。

以下是问卷第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>

答案 1 :(得分:3)

我最近遇到了同样的问题,并不像听起来那么棘手。你需要给你的触发器一个tabindex&#39; (为了使其可聚焦,0是好的)。给它一个点击&#39;像这样的事件处理程序......

document.getElementById('myTrigger').addEventListener('click', function(){this.focus(); this.classList.toggle('openClass');});

在哪里&#39; openClass&#39;是触发菜单的那个。然后(假设var myTrigger)......

myTrigger.addEventListener('blur', function(){ this.classList.remove('openClass');})

在此处,单击切换可打开和关闭打开的类,但它也会以幻灯片方式设置焦点。点击时,元素失去焦点,模糊&#39;事件触发并且处理程序删除了类......

答案 2 :(得分:1)

我采取了不同的方法。我使用toggleClass确定菜单是否打开。根据这个类名,我改变了你的css,这样只要将类'showMenu'添加到我们的html标签,菜单就会打开。

clickOutside方法中的代码检查您是否在给定的classNames外部点击(在这种情况下是.js-site-nav和.js-site-nav-btn - toggle)。如果您单击的元素不是具有给定类名的元素,则菜单将关闭。

对于此回复中的错误加价感到抱歉,我正在工作,所以当我在家时,我会尝试改进此消息。

以下是我使用的代码:

<强> HTML

<div class="container">

    <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>
</div>

<强> CSS

.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;
}
.showMenu .js-site-nav {
  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;
}

.container
{
    width: 100%;
    height: 100%;
    background: red;
}

<强>的JavaScript

var $parent = $('html');
var toggleClass = 'showMenu';
var container = $(".js-site-nav, .js-site-nav-btn--toggle");

function init()
{
    $('.js-site-nav-btn--toggle').on('click touchend', toggleMenu);
    $(document).on('click touchend', clickOutside);
}

function toggleMenu()
{
    $parent.toggleClass(toggleClass);
}

function clickOutside(e)
{ 

    if (!container.is(e.target) // if the target of the click isn't the container...
    && container.has(e.target).length === 0
    && $parent.hasClass(toggleClass)) // ... nor a descendant of the container
    {
        $parent.removeClass(toggleClass);
    }
}
init();

https://jsfiddle.net/h7drcett/9/