Доступно скрывать и показывать контент после анимации - PullRequest
3 голосов
/ 17 апреля 2020

Я реализую компонент, имеющий скрытое содержимое, отображаемое при нажатии кнопки. Я хотел бы выполнить переход на max-height раскрытого контента, например, так:

<button id="show-hide">Toggle content</button>
<div id="revealable-content" class="content is-collapsed">
Content to be revealed
  <a href="https://www.example.com">with a link</a>
</div>
.content {
  overflow: hidden;
  height: auto;
  max-height: 200px;
  width: 200px;
  background-color: darkgray;
  transition: max-height 1000ms;
}

.content.is-collapsed {
  max-height: 0
}
const button = document.getElementById('show-hide')
const content = document.getElementById('revealable-content')
let hidden = true

button.addEventListener('click', () => {
  hidden = !hidden

  if (hidden) {
    content.classList.add('is-collapsed')  
  } else {
    content.classList.remove('is-collapsed')
  }
})

Пока все хорошо. Теперь я хочу сделать это более доступным, поэтому я добавляю атрибут hidden к содержимому div и устанавливаю для него значение true или false, когда я знаю, что анимация была выполнена с использованием setTimeout:

// at the bottom of the event handler...
setTimeout(() => {
  content.hidden = hidden
}, 1000)

Это нарушает «расширяющуюся» анимацию, но, как ни странно, не «разрушающуюся» анимацию. При падении анимация перехода запускается в течение 1 секунды, как и ожидалось. Однако при расширении max-height применяется немедленно без перехода.

См. этот кодовый элемент для демонстрации.

Что происходит, и как я могу это исправить

Ответы [ 4 ]

2 голосов
/ 17 апреля 2020

Обновление:
Решение кажется намного проще, чем ожидалось. Использование короткого тайм-аута, например, 10 мс до удаления is-collapsed -класса, поможет вам:

Примечание: я установил тайм-аут на 100 мс, поскольку при использовании firefox переход не всегда такой плавный как в chrome.

const button = document.getElementById('show-hide');
const content = document.getElementById('revealable-content');
let hidden = true;

button.addEventListener('click', () => {
    hidden = !hidden;
  
  if (hidden) {
    content.classList.add('is-collapsed');
    return setTimeout(() => {
      content.hidden = hidden;
    }, 1000);
  }

  content.hidden = hidden;
  return setTimeout(() => {
    content.classList.remove('is-collapsed');
  }, 100);
})
.content {
  overflow: hidden;
  height: auto;
  max-height: 200px;
  width: 200px;
  background-color: darkgray;
  transition: max-height 1000ms linear;
}

.content.is-collapsed {
  max-height: 0
}
<button id="show-hide">Toggle content</button>
<div hidden id="revealable-content" class="content is-collapsed">
Content to be revealed
  <a href="https://www.example.com">with a link</a>
</div>
0 голосов
/ 17 апреля 2020

height: auto; не имеет анимационного эффекта, но имеет свойство height. Но когда ваш элемент скрыт (display: none), вы не можете получить высоту. Так что вам нужно клонировать его и получить высоту, тогда вы можете применить анимацию, используя высоту.

Полный кредит идет на это [QA]

<!DOCTYPE html>
<html>
<head>
<title>Avinash</title>

<style>
  #slider {
    margin:0px auto;
    padding:0px;
    width:400px;
    border:1px solid #000;
    background:#0f0;   
  height:20px;
  overflow:hidden;
  }
</style>
<script>
  
var minheight = 20;
var maxheight = 300;
var time = 1000;
var timer = null;
var toggled = false;

window.onload = function() {
  var controler = document.getElementById('slide');
  var slider = document.getElementById('slider');
  slider.style.height = minheight + 'px';
  controler.onclick = function() {  
    clearInterval(timer);
    var instanceheight = parseInt(slider.style.height);
    var init = (new Date()).getTime();
    var height = (toggled = !toggled) ? maxheight: minheight; 
    
    var disp = height - parseInt(slider.style.height);
    timer = setInterval(function() {
      var instance = (new Date()).getTime() - init;
      if(instance < time ) {
        var pos = Math.floor(disp * instance / time);
        result = instanceheight + pos;
        slider.style.height =  result + 'px';
        document.getElementById('log').innerHTML = 'Current Height : <b>' + result + '</b><br /> Current Time : <b>' + instance + '</b>';
      }else {
        slider.style.height = height + 'px'; //safety side ^^
        clearInterval(timer);
        controler.value = toggled ? ' Slide Up ' :' Slide Down ';
        document.getElementById('log').innerHTML = 'Current Height : <b>' + height + '</b><br /> Current Time : <b>' + time + '</b>';
      }
    },1);
  };
};
</script>
</head>
<body>
<h1> Toggle Slide </h1>
  <input type="button" id="slide" value =" Slide Down "/>
  <span id="log" style="position:absolute; left:10px; top:150px;"></span>
  <br />
  <div id="slider">
     content goes here
  </div>
</body>
</html>
0 голосов
/ 17 апреля 2020

Проблема в js logi c. Когда контент скрыт, и вы нажимаете кнопку, чтобы показать его, анимация применяется, пока контент все еще скрыт, а после завершения анимации вы удаляете скрытый атрибут, который немедленно отображает контент.

Просто измените свой обработчик кликов код:

if(hidden){
  content.hidden = false
}else {
  setTimeout(() => {
    content.hidden = true
  }, 1000)
}

hidden = !hidden


setTimeout(() => {
   if (hidden) {
    content.classList.add('is-collapsed')  
   } else {
    content.classList.remove('is-collapsed')
  }
})

Второй тайм-аут требуется по причине, указанной в ответе Мухаммеда Тахаззота - высота неизвестна, пока элемент не скрыт. setTimeout без задержки выполняет код в следующей задаче - когда элемент не скрыт, поэтому можно получить высоту.

А зачем вообще нужен скрытый атрибут? Если вы хотите скрыть контент от программ чтения с экрана, используйте aria-hidden = "true", если вы хотите, чтобы контент не фокусировался на вкладке, используйте tabindex = "- 1"

0 голосов
/ 17 апреля 2020

Похоже, что элемент с атрибутом hidden имеет браузер porpertis следующим образом:

div[Attributes Style] {
display: none;
}

display: none; повреждает анимацию.

Это может быть решением, работает для меня :

ОБНОВЛЕНО

const button = document.getElementById('show-hide')
const content = document.getElementById('revealable-content')
let hidden = true

button.addEventListener('click', () => {
  hidden = !hidden
  
  
  if (hidden) {    
    content.classList.add('is-collapsed')    
  } else {
    content.style.display = 'block'; // set display block before class is-collapsed removes
    setTimeout(() => {
      content.classList.remove('is-collapsed')
      content.style.display = ''; // clear css display  
    }, 100); //changed time interval for Firefox 
    
  }
  
  setTimeout(() => {
    content.hidden = hidden
  }, 1000)
})
.content {
  overflow: hidden;
  height: auto;
  max-height: 200px;
  width: 200px;
  background-color: darkgray;
  transition: max-height 1000ms;
  display: block; 
}

.content.is-collapsed {
  max-height: 0;
}

/*added to hide collapsed element*/
.content.is-collapsed[hidden] {
  display: none; 
}
<button id="show-hide">Toggle content</button>
<div hidden id="revealable-content" class="content is-collapsed">
Content to be revealed
  <a href="https://www.example.com">with a link</a>
</div>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...