javascript - 在某些触摸事件后选择了多个选项卡
问题描述
我已经设置了一些代码来在不同的选项卡之间切换,它在大多数情况下工作得很好,除了移动设备上的一些边缘情况给我带来了麻烦。
如果我正常点击其中一个按钮(即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>
解决方案
我想我理解您在桌面上进行的交互,并且可能试图在移动设备上进行模仿。
在桌面上,您有一个活动状态(即 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以及预览。
推荐阅读
- react-native - Firebase 计划在 10 分钟后运行一个函数
- angular - 奇怪的主题行为:为什么它表现得像我的订阅迟到了?
- ruby-on-rails - 如何让 ESLint 定位 Rails 应用程序中的全局符号,而不会在代码中乱扔注释?
- javascript - 减小模态框内的图像大小
- excel - Power Query 计算列与新列:错误:“,”的语法不正确
- r - R:将“tidyr/tidyverse”语句转换为“dplyr”和“reshape2”
- r - 出现后结束动画
- django - Django & docker-compose - psycopg2 连接错误
- python - 使用 TouchAction 滚动
- api - VueJS - 方法在第二次点击时触发两次