Переходы CSS не работают, если они назначены через JavaScript

У меня возникла серьезная головная боль при попытке применить переходы CSS3 к слайд-шоу через JavaScript.

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

Теперь моя "маленькая" проблема. Все работает как положено, все слайды имеют правильные стили, код работает без ошибок (пока). Но указанные переходы не работают, хотя применены правильные стили. Кроме того, стили и переходы работают, когда я сам применяю их через инспектор.

Поскольку я сам не мог найти логического объяснения, я подумал, что кто-нибудь здесь может ответить на него, пожалуйста?

Я собрал небольшой пример кода прямо сейчас: http://g2f.nl/38rvma Или используйте JSfiddle (без изображения): http://jsfiddle.net/5RgGV/1/


person Jon Koops    schedule 21.11.2011    source источник


Ответы (3)


Чтобы transition заработало, должны произойти три вещи.

  1. свойство элемента должно быть явно определено, в данном случае: opacity: 0;
  2. элемент должен иметь определенный переход: transition: opacity 2s;
  3. новое свойство должно быть установлено: opacity: 1

Если вы назначаете 1 и 2 динамически, как в вашем примере, перед 3 должна быть задержка, чтобы браузер мог обработать запрос. Причина, по которой это работает при отладке, заключается в том, что вы создаете эту задержку, проходя через нее, давая браузеру время для обработки. Дайте отсрочку назначению .target-fadein:

window.setTimeout(function() {
  slides[targetIndex].className += " target-fadein";
}, 100); 

Или поместите .target-fadein-begin непосредственно в свой HTML, чтобы он анализировался при загрузке и был готов к переходу.

Не добавление transition к элементу запускает анимацию, а изменение свойства.

// Works
document.getElementById('fade1').className += ' fade-in'

// Doesn't work
document.getElementById('fade2').className = 'fadeable'
document.getElementById('fade2').className += ' fade-in'

// Works
document.getElementById('fade3').className = 'fadeable'

window.setTimeout(function() {
  document.getElementById('fade3').className += ' fade-in'
}, 50)
.fadeable {
  opacity: 0;
}

.fade-in {
  opacity: 1;
  transition: opacity 2s;
}
<div id="fade1" class="fadeable">fade 1 - works</div>
<div id="fade2">fade 2 - doesn't work</div>
<div id="fade3">fade 3 - works</div>

person ThinkingStiff    schedule 21.11.2011
comment
Спасибо, я очень ценю вашу помощь! Я посмотрю на это, когда вернусь домой. - person Jon Koops; 21.11.2011
comment
Да, кажется, единственное, что нужно сделать. Жаль, что нет события для применения стилей. Я думаю, мне просто нужно подождать 100 мс :) - person Jon Koops; 27.11.2011
comment
Я только что столкнулся с этой проблемой, и мне потребовались часы, чтобы понять, что после применения свойства перехода должно пройти некоторое время. Известно ли что-нибудь о том, ПОЧЕМУ это так, ведь обычно стили применяются моментально? Это ошибка или задуманное поведение? - person BlackWolf; 20.01.2014
comment
В моем случае я использовал задержку 0 миллисекунды в setTimeout, и она все еще работает. - person nonzaprej; 14.12.2017
comment
Есть ли способ сделать это с помощью promise или мы не сможем этого сделать, потому что присваивание className не является асинхронным объектом? - person Isa; 29.09.2019

Обмани механизм компоновки!

function finalizeAndCleanUp (event) {
    if (event.propertyName == 'opacity') {
        this.style.opacity = '0'
        this.removeEventListener('transitionend', finalizeAndCleanUp)
    }
}
element.style.transition = 'opacity 1s'
element.style.opacity = '0'
element.addEventListener('transitionend', finalizeAndCleanUp)
// next line's important but there's no need to store the value
element.offsetHeight
element.style.opacity = '1'

Как уже упоминалось, transition работают путем интерполяции из состояния A в состояние B. Если ваш скрипт вносит изменения в одну и ту же функцию, механизм компоновки не может разделить, где заканчивается состояние A и начинается B. Если только вы не подскажете.

Поскольку нет официального способа сделать подсказку, вы должны полагаться на побочные эффекты некоторых функций. В этом случае .offsetHeight геттер, который неявно заставляет механизм компоновки останавливаться, оценивать и вычислять все установленные свойства и возвращать значение. Как правило, этого следует избегать из соображений производительности, но в нашем случае это именно то, что необходимо: консолидация состояний.

Код очистки добавлен для полноты.

person transistor09    schedule 13.08.2015
comment
Чтобы подчеркнуть, что важная строка является неназначенным вычисляемым выражением, вы можете написать void element.offsetHeight - person trincot; 29.10.2016
comment
Предупреждение о transitionend: запуск этого события не гарантируется. Браузер может оптимизировать его, если, например, вкладка не видна. Не полагайтесь на него и не имейте запасной вариант! - person transistor09; 29.10.2016
comment
Спасибо за предупреждение, @transistor09 - person trincot; 29.10.2016

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

Вот стандарт, если вам нужны подробности: http://dev.w3.org/csswg/css-transitions/#starting

person user3723889    schedule 20.01.2015