首页 > 解决方案 > 在某些触摸事件后选择了多个选项卡

问题描述

我已经设置了一些代码来在不同的选项卡之间切换,它在大多数情况下工作得很好,除了移动设备上的一些边缘情况给我带来了麻烦。

如果我正常点击其中一个按钮(即touchstart和的位置touchend都在同一个按钮上),它的行为正常;点击的按钮变为活动状态,相应的视图变为可见。它也适用于桌面。我什至添加了一些mouseenter事件mouseleave来使一个选项卡(只有一个选项卡)在悬停时处于活动状态。

touchstart然而,当和事件的位置发生变化时,就会出现问题touchend。例如,如果选择了“联系人”选项卡,我点击“更新”,将手指移到“简历”上,然后松开,“简历”和“联系人”似乎都被选中,如下所示。

选择了多个选项卡

在这种情况下,预期的行为将是“Bio”在显示“Bio”视图时处于活动状态。至少还有两种其他边缘情况,例如在按钮上滑动或从按钮上滑动,在我当前的配置中不能按预期工作。任何不破坏有效案例的帮助表示赞赏。

$(document).ready(function () {
    // show appropriate view based on document hash
    if (document.location.hash) {
        setTimeout(function () {
            window.scrollTo(0, 0);
        }, 1);
    }

    if (document.location.hash === '' ||
        document.location.hash === '#' ||
        document.location.hash === '#bio') {
        $('#bio').show();
        $('#tabs-bio').addClass('active');
    } else if (document.location.hash === '#updates') {
        $('#updates').show();
        $('#tabs-updates').addClass('active');
    } else if (document.location.hash === '#contact') {
        $('#contact').show();
        $('#tabs-contact').addClass('active');
    }

    // define tabs behavior
    var $activeTab = $('.active');
    var $tabsChildren = $('.tabs > li');
    $tabsChildren.on('click touchstart', function (event) {
        event.preventDefault();
        event.stopPropagation();

        $tabsChildren.each(function () {
            $($(this).data('href')).hide();
            if (event.type === 'click') {
                $(this).removeClass('active');
            }
        });

        $activeTab = $(this);
        $($(this).data('href')).show();
    });

    $('.tabs').mouseenter(function () {
        $activeTab.removeClass('active');
    });
    $('.tabs').mouseleave(function () {
        $activeTab.addClass('active');
    });
    $(window).on('touchstart', function () {
        $activeTab.addClass('active');
    });
});
ul {
  padding-left: 0;
}

ul li {
  list-style: none;
}

.center {
  text-align: center;
}

.tabs {
  display: grid;
  grid-gap: 0.25rem;
  color: black;
  margin-bottom: 2rem;
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  -khtml-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}

.tabs-3 {
  grid-template-columns: 33% 33% 33%;
  grid-template-columns: calc((100% - 0.5rem)/3) calc((100% - 0.5rem)/3) calc((100% - 0.5rem)/3);
}

.tabs li {
  border-radius: 4px;
  background-color: #ced4da;
  padding: 0.5rem;
}

.tabs li:hover {
  background-color: #007bff;
  color: white;
  cursor: pointer;
}

.tabs .active {
  background-color: #007bff;
  color: white;
}

#page > div {
  display: none;  
}
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container">
  <ul class="tabs tabs-3">
    <li class="center" id="tabs-bio" data-href="#bio">Bio</li>
    <li class="center" id="tabs-updates" data-href="#updates">Updates</li>
    <li class="center" id="tabs-contact" data-href="#contact">Contact</li>
  </ul>
  <div id="page">
    <div id="bio">
      <h3>Bio</h3>
    </div>
    <div id="updates">
      <h3>Updates</h3>
    </div>
    <div id="contact">
      <h3>Contact</h3>
    </div>
  </div>
</div>

标签: javascriptjqueryhtmlcss

解决方案


我想我理解您在桌面上进行的交互,并且可能试图在移动设备上进行模仿。

在桌面上,您有一个活动状态(即 Bio),但是在将鼠标悬停在选项卡上时,您会忽略活动选项卡并具有悬停效果,如果用户未选择任何选项并将活动选项卡悬停,则活动选项卡将恢复其活动样式,但如果用户选择了新选项卡 新选项卡处于活动状态。

这是我建议的更改:

touchstart不实际更改选项卡上,因为用户可能最终并不想选择选项卡 - 要么滑开,要么他们实际上只是试图滚动,你的函数重写:

$tabsChildren.on('click touchstart', function (event) {
    event.preventDefault();
    event.stopPropagation();

    $tabsChildren.removeClass('active');

    if (event.type === 'click') {
      $tabsChildren
        .removeClass('active')
        .each(function () {
          $($(this).data('href')).hide();
        });

      $activeTab = $(this);
      $($(this).data('href')).show();
    } else {
      $(this).addClass('active');
    }
});

请注意,我仍在添加active类只是为了显示“悬停”样式。

现在用于touchend确定用户是否真的打算选择选项卡:

$tabsChildren.on('touchend', function (event) {
  var changes = event.changedTouches[0];
  var $endEl = $(document.elementFromPoint(changes.pageX, changes.pageY));

  if ($endEl.parent('.tabs').length) {
    $tabsChildren
      .removeClass('active')
      .each(function () {
        $($(this).data('href')).hide();
      });

    $activeTab = $endEl;
    $endEl.addClass('active');
    $($endEl.data('href')).show();
    event.stopPropagation();
  }
});

我们要做的不是使用$(this)(用户开始触摸的地方),而是找出元素最后是什么。如果用户实际上在不同的选项卡结束 - 选择它。如果用户最终根本不在选项卡上,请不要做任何事情,让它抓住它:

$(window).on('touchend', function () {
  $tabsChildren.removeClass('active');
  $activeTab.addClass('active');
});

我们将选项卡重置回原来的活动选项卡。

这是运行中的代码https://codepen.io/anon/pen/pBzeEP?editors=0010以及预览。

在此处输入图像描述


推荐阅读