Несколько вкладок, выбранных после определенных событий касания

#javascript #jquery #html #css

#javascript #jquery #HTML #css

Вопрос:

Я настроил некоторый код для переключения между разными вкладками, и по большей части он работает довольно хорошо, за исключением некоторых крайних случаев на мобильных устройствах, которые доставляют мне проблемы.

Если я нажимаю одну из кнопок обычным образом (т. Е. позиции 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>  

Ответ №1:

Я думаю, что понимаю взаимодействие, к которому вы стремитесь на рабочем столе, и, вероятно, пытаюсь имитировать это на мобильном устройстве.

На рабочем столе у вас есть активное состояние (т. Е. Биография), но при наведении курсора мыши на вкладки вы игнорируете активную вкладку и получаете эффект наведения курсора, если пользователь не выбирает опцию и наводит курсор, активная вкладка возобновляет свой активный стиль, но если пользователь выбирает новую вкладку, новая принимает активное состояние.

Вот какие изменения я бы предложил:

На 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 а также предварительный просмотр.

введите описание изображения здесь