Bootstrap Collapse не работает совместно с setTimeout

329
08 марта 2017, 17:29

Наверняка многие пользовались функциональностью для отображения и скрытия определенного контента посредством Bootstrap Collapse. Вот и я пользуюсь, все успешно, но у меня возникли некие недочеты, которые я хотел убрать. Есть следующий код:

<input id="btnShowHideDeletedLft" 
       onclick="changeTextBtnDeletedLft()" 
       type="button" 
       class="btn btn-info" 
       data-toggle="collapse" 
       data-target="#tblDeletedLft" 
       value="Посмотреть удаленные" />
<div id="tblDeletedLft" class="collapse" style="margin-top: 10px;">
     @* тут мой контент - небольшая таблица *@
</div>    

Данный блок кода успешно отрабатывает. Но, ведь есть еще JavaScript, в котором меняется текст моей кнопки, вот он:

function changeTextBtnDeletedLft() {
    var elem = document.getElementById("btnShowHideDeletedLft");
    //elem.disabled = true;
    if (elem.value === 'Посмотреть удаленные') {
        elem.value = 'Скрыть удаленные';
    } else {
        elem.value = 'Посмотреть удаленные';
    }
    //setTimeout(function() { elem.disabled = false; }, 500);
}

Этот код тоже работает, но увы, он работает не так, как мне хотелось бы, именно в нем и проблема, а именно: если быстро дважды кликнуть на кнопку, то текст в ней изменится дважды, а таблица отобразится (скроется) один раз. В результате тест в кнопке будет Посмотреть удаленные и таблица уже будет отображена, либо наоборот, то есть будет несоответствие надписи в кнопке и контента.

После этого меня посетила мысль, а что если дизэйблить кнопку на пол секунды после клика, как раз за это время таблица успеет отобразиться, а пользователь не кликнет дважды подряд!

Если задействовать закомментированный код в JavaScript, то Collapse перестает работать. То есть отображения и скрытия таблицы не происходит. Текст в кнопке меняется, она дизэйблится, но отображение и скрытие не работает.

Конечно, можно написать на кнопке Посмотреть/скрыть удаленные и не нужно будет менять текст, а значит и таймауты не нужны, но хочется по фэн-шую, чтобы кнопка дизэйблилась и текст в ней менялся и отображение/скрытие конента работало. Как это сделать?

Answer 1

Раз вы используете Bootstrap, у вас должен быть подключен jQuery.

В момент скрытия элемента блок имеет класс collapsing, по которому можно точно определить, что элемент в данный момент анимируется. В этом случае, отменяем выполнение скрипта:

if ($(elem.data("target")).is(".collapsing")) return;

Итого:

function changeTextBtnDeletedLft() { 
 
  var elem = $("#btnShowHideDeletedLft"); 
 
  if ($(elem.data("target")).is(".collapsing")) return; 
   
 
  if (elem.val() === 'Посмотреть удаленные') { 
    elem.val('Скрыть удаленные'); 
  } else { 
    elem.val('Посмотреть удаленные'); 
  } 
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> 
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script> 
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> 
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous"> 
 
<input id="btnShowHideDeletedLft" onclick="changeTextBtnDeletedLft()" type="button" class="btn btn-info" data-toggle="collapse" data-target="#tblDeletedLft" value="Посмотреть удаленные" /> 
 
<div id="tblDeletedLft" class="collapse" style="margin-top: 10px;"> 
  @* тут мой контент - небольшая таблица *@ 
</div>

Answer 2

Последовал совету @Crantisz добавил стоку кода в вызываемую функцию:

if ($($(elem).data("target")).is(".collapsing")) return;

но, выражение внутри проверки всегда false, посмотрел в отладке и увидел, что collapsing происходит после вызова функции changeTextBtnDeletedLft(). Не знаю, почему у меня код работает немного не так, как в ответе, загадочная магия получается какая-то.

На просторах stackoverflow.com нашел вот такой вот ответ: jQuery show.bs.collapse event for nested Bootstrap collapsible buttons fires on more buttons than expected. Идею взял из этого ответа и вот, дополнил и модифицировал свой JavaScript обработкой событий Collapse:

<script type="text/javascript">    
    var elemBtnShowHide = document.getElementById("btnShowHideDeletedLft");
    function changeTextBtnDeletedLft() {
        if (elemBtnShowHide.value === 'Посмотреть удаленные лифты') {
            elemBtnShowHide.value = 'Скрыть удаленные лифты';
        } else {
            elemBtnShowHide.value = 'Посмотреть удаленные лифты';
        }
    }
    $('#tblDeletedLft').on('shown.bs.collapse', function () {
        elemBtnShowHide.disabled = false;
    }).on('show.bs.collapse', function () {
        elemBtnShowHide.disabled = true;
    });
    $('#tblDeletedLft').on('hidden.bs.collapse', function () {
        elemBtnShowHide.disabled = false;
    }).on('hide.bs.collapse', function () {
        elemBtnShowHide.disabled = true;
    });   
</script>

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

Всю необходимую дополнительную информацию уже нашел тут: Bootstrap JS Collapse, а именно:

Collapse Events

  1. show.bs.collapse - Occurs when the collapsible element is about to be shown
  2. shown.bs.collapse - Occurs when the collapsible element is fully shown (after CSS transitions have completed)
  3. hide.bs.collapse - Occurs when the collapsible element is about to be hidden
  4. hidden.bs.collapse - Occurs when the collapsible element is fully hidden (after CSS transitions have completed)
READ ALSO
Оффлайн база данных для игры

Оффлайн база данных для игры

Здравствуйте, мне немного непонятно по поводу оффлайн баз данныхЯ хочу иметь БД, которая не будет иметь никакого отношения к серверу и будет...

341
Проблема при десериализация JSON

Проблема при десериализация JSON

Использую RestSharp для отправки запроса на API Yandex, но почему-то переменная result не заполняется

317
Обработка нажатия по ItemControl и определение по какому именно элементу был совершен клик. MVVM

Обработка нажатия по ItemControl и определение по какому именно элементу был совершен клик. MVVM

Всем доброго времени суток! Крик души! Осталось реализовать две фичи, но бьюсь над ними уже пару дней

289
Как можно параллельно использовать WebClient.DownloadString?

Как можно параллельно использовать WebClient.DownloadString?

Есть коллекция ссылок и нужно скачать файлы по данным ссылкам

200