Временно обойти переход CSS

Допустим, у меня есть такой стиль с переходом:

#theElement {
    position: relative;
    left: 200px;
    transition: left 1s;
}

И немного кода:

var element = document.getElementById("theElement");

function animateX(px) {
    element.style.left = px + "px";
}

Итак, просто есть функция animateX, которая просто говорит, что она делает, анимирует левое свойство theElement, теперь что, если я также хочу иметь функцию, которая мгновенно устанавливает левое свойство, без перехода :

function setX(px) {
    element.style.transition = "none";
    element.style.left = px + "px";
    element.style.transition = "left 1s";
}

Почему это не работает и как это исправить?


person Community    schedule 11.01.2016    source источник


Ответы (4)


Чтобы это работало, вам необходимо очистить изменения CSS, прочитав их в JS. Ответ на этот вопрос объяснит, как это работает:

Могу ли я использовать javascript заставить браузер сбрасывать все ожидающие изменения макета?

Рабочий пример ниже:

var element = document.getElementById("theElement");

function animateX(px) {
    element.style.left = px + "px";
}

function setX(px) {
    element.style.transition = "none";
    element.style.left = px + "px";
    // apply the "transition: none" and "left: Xpx" rule immediately
    flushCss(element);
    // restore animation
    element.style.transition = "";
}

function flushCss(element) {
  // By reading the offsetHeight property, we are forcing
  // the browser to flush the pending CSS changes (which it
  // does to ensure the value obtained is accurate).
  element.offsetHeight;
}
#theElement {
  position: relative;
  left: 200px;
  transition: left 1s;
  width: 50px;
  height: 50px;
  background: red;
}
.wrapper {
  width: 400px;
  height: 100px;
}
<div class="wrapper">
  <div id="theElement"></div>
</div>
<button onclick="animateX(100)">Animate 100</button>
<button onclick="setX(0)">Set 0</button>

person jperezov    schedule 11.01.2016
comment
Вам действительно нужно выполнить очистку дважды, потому что для ответа lex82 требуется только один тайм-аут? - person ; 11.01.2016
comment
Нет. Я думал, что будет легче понять, если дважды показать флеш, но я полагаю, что это только сбивает с толку. Я отредактировал свой ответ, чтобы продемонстрировать это. - person jperezov; 11.01.2016
comment
Как объясняется в моем ответе, вам не следует восстанавливать анимацию, явно задавая ее для элемента. Вы переопределите таблицу стилей (как вы это делали при подавлении анимации). - person lex82; 11.01.2016
comment
Хороший ответ. Моя версия заключалась в том, чтобы выполнить вторую половину в setTimeout, но так лучше. - person Katana314; 11.01.2016
comment
Действительно интересный способ принудительного применения свойств с помощью offsetHeight - спасибо за понимание! - person Nico Knoll; 02.01.2021

Другие решения хороши, но я хотел предложить альтернативу и объяснить, что происходит.

Причина, по которой он работает не так, как ожидалось в вашем примере, заключается в том, что все свойства CSS изменены, а затем событие перекомпоновки браузера запускается, и переход начинается. Другими словами, для свойства transition установлено значение none, затем свойство left изменяется, затем свойство transition снова изменяется на left 1s.

После обновления всех свойств стиля запускается событие перекомпоновки браузера, перерисовывается CSS, а затем начинается переход:

element.style.transition = "none";
element.style.left = px + "px";
element.style.transition = "left 1s";
// The transition starts after all the CSS has been modified and a reflow has been trigged.

Это сделано по нескольким причинам.

Основная причина - производительность. Вместо того, чтобы перерисовывать каждый элемент после изменения одного свойства CSS, он гораздо эффективнее изменять все свойства и иметь одно событие перерисовки. Кроме того, если вы переносите несколько свойств, вы ожидаете, что каждое свойство будет изменено до перехода элемента (что именно и происходит).

Если вам нужна чистая альтернатива, которая не включает тайм-аутов / задержек или принудительных перекомпоновок, вы можете просто установить для свойства transitionDuration значение 0s перед установкой значения, а затем удалить этот встроенный стиль при переходе элемента.

Например:

var element = document.getElementById("theElement");

function setX(px) {
  element.style.transitionDuration = '0s';
  element.style.left = px + "px";
}

function animateX(px) {
  element.style.transitionDuration = '';
  element.style.left = px + "px";
}
#theElement {
  position: relative;
  width: 100px;
  height: 100px;
  border: 2px solid;
  left: 200px;
  transition: left 1s;
}
<div id="theElement"></div>

<button onclick="animateX(100)">Transition 100</button>
<button onclick="animateX(300)">Transition 300</button>
<button onclick="setX(0)">Set 0</button>
<button onclick="setX(50)">Set 50</button>

Точно так же вы также можете переключить класс элемента перед переходом или установкой значения:

var element = document.getElementById("theElement");

function setX(px) {
  element.classList.remove('transition');
  element.style.left = px + "px";
}

function animateX(px) {
  element.classList.add('transition');
  element.style.left = px + "px";
}
#theElement {
  position: relative;
  width: 100px;
  height: 100px;
  border: 2px solid;
  left: 200px;
}
#theElement.transition {
  transition: left 1s;
}
<div id="theElement"></div>

<button onclick="animateX(100)">Transition 100</button>
<button onclick="animateX(300)">Transition 300</button>
<button onclick="setX(0)">Set 0</button>
<button onclick="setX(50)">Set 50</button>

Приведенный выше фрагмент работает, но также стоит отметить, что вы можете прослушать событие transitionend и удалить класс по окончании перехода:

Например:

function animateX(px) {
  element.classList.add('transition');
  element.style.left = px + "px";
  element.addEventListener('transitionend', callback);

  function callback() {
    this.classList.remove('transition');
    this.removeEventListener('transitionend', callback);
  }
}
person Josh Crozier    schedule 11.01.2016
comment
Отличный ответ. Я не знал, что вы можете прослушать событие transitionend. Поддерживается ли это мероприятие в браузере? - person jperezov; 11.01.2016
comment
@jperezov Да, у него есть хорошая поддержка браузером. Он поддерживается во всех современных браузерах, хотя вам может потребоваться использовать версию с префиксом для старых браузеров, например, webkitTransitionEnd, msTransitionEnd. - person Josh Crozier; 11.01.2016

Вы можете это сделать, но вам нужно восстановить старое свойство перехода с тайм-аутом.

function setX(px) {
    element.style.transition = "none";
    element.style.left = px + "px";
    setTimeout(function() {
        element.style.transition = "left 1s";
    });
}

JSFiddle

Если вы не используете тайм-аут для восстановления старого свойства перехода, браузер никогда не узнает, что оно вообще изменилось. Он не отобразит страницу, пока ваш код не будет выполнен полностью.

Я также рекомендую отключить свойство перехода следующим образом:

element.style.transition = "";

В противном случае вы навсегда переопределите таблицу стилей для этого элемента. Прежде чем вы установите стиль перехода для элемента, переход определяется таблицей стилей. Если вы установите стиль перехода для элемента, вы переопределите таблицу стилей.

Обновление. Похоже, тайм-аута недостаточно. Я пробовал это в FF / Linux, и это не сработало. Таким образом, вы либо добавляете еще несколько миллисекунд (не рекомендуется), либо применяете решение @jperezov (рекомендуется).

person lex82    schedule 11.01.2016
comment
Кроме того, не нужен ли setTimeout второй аргумент для миллисекунд? - person ; 11.01.2016
comment
Это не так. По умолчанию - ноль, поэтому задержки практически нет. - person lex82; 11.01.2016
comment
Что вы имеете в виду, когда хотите навсегда переопределить таблицу стилей, не могли бы вы привести пример? - person ; 11.01.2016
comment
Не рекомендуется рекомендовать setTimeout, не зная, что происходит за занавеской, что лучше объяснено в ответе Джоша Крозьера. - person Jan Turoň; 28.10.2020

Просто чтобы внести некоторые дополнения в полностью достаточный и хорошо объясненный ответ Джоша Крозьера:

Также существует событие ontransitioncalcel, которое срабатывает, когда событие отменяется, поэтому вы можете проверить, отменяют ли его какие-либо методы. Из текущего черновика я предпочитаю:

Если элемента больше нет в документе, реализации должны отменить все выполняющиеся переходы.

чтобы вы могли удалить элемент, внести изменения и затем вернуть элемент в исходное положение; установка display: none имеет различные побочные эффекты, останавливая переход между ними,

Если у элемента есть текущий переход для свойства и нет соответствующего значения свойства перехода, то реализации должны отменить текущий переход.

поэтому вы можете попытаться удалить значение свойства перехода,

Если текущее значение свойства в текущем переходе равно значению свойства в стиле после изменения, или если эти два значения не являются переходными, то реализации должны отменить текущий переход.

поэтому вы можете сначала установить для свойства left значение auto или initial, чтобы прервать переход.

Увы, ни одно из событий перехода не может быть отменено, поэтому вызов e.preventDefault() в них не должен останавливать их.

person Jan Turoň    schedule 28.10.2020