2

I'm trying to animate an element from height: 20px to height: 0, but its not working. However, a transition from 0 to 20 does work. I'm using angular to add/remove items from an array:

angular.module('app', [])
  .controller('ctl', ctl);

ctl.$inject = ['$timeout'];

function ctl($timeout) {
  var self = this;

  self.newItemText = '';
  self.deleteItem = function(id) {
    self.items[id].class = 'hidden';
  };
  self.addItem = function() {
    var newItem = {
      id: self.items.length,
      class: 'hidden',
      text: self.newItemText
    };
    self.items.push(newItem);
    self.items[self.items.length - 1].class = 'visible';
    self.newItemText = '';
  }

  self.items = [{
    id: 0,
    class: 'visible',
    text: 'one'
  }, {
    id: 1,
    class: 'visible',
    text: 'two'
  }, {
    id: 2,
    class: 'visible',
    text: 'three'
  }, {
    id: 3,
    class: 'visible',
    text: 'one'
  }, {
    id: 4,
    class: 'visible',
    text: 'two'
  }, {
    id: 5,
    class: 'visible',
    text: 'three'
  }, {
    id: 6,
    class: 'visible',
    text: 'one'
  }, {
    id: 7,
    class: 'visible',
    text: 'two'
  }, {
    id: 8,
    class: 'visible',
    text: 'three'
  }, ];
};
body {
  font-family: arial;
}
.text {
  display: inline-block;
}
.close {
  cursor: pointer;
}
.visible {
  height: 20px;
  transition: height 0.2s linear;
}
.hidden {
  height: 0;
  overflow: hidden;
  transition: height 0.2s linear;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>

<div ng-app='app' ng-controller='ctl as c'>
  <input ng-model='c.newItemText' />
  <button ng-click='c.addItem()'>
    add
  </button>
  <div>
    <ul>
      <li ng-repeat='item in c.items' class='{{item.class}}'>
        <span class='text'>{{item.text}}</span>
        <span class='close' ng-click='c.deleteItem(item.id)'>x</span>
      </li>
    </ul>
  </div>

</div>

relevant css:

.visible {
  height: 20px;
  transition: height 0.2s linear;
}
.hidden {
  height: 0;
  overflow: hidden;
  transition: height 0.2s linear;
}

full code:

https://jsfiddle.net/1rqhLo83/

Josh Crozier
  • 233,099
  • 56
  • 391
  • 304
Abdul Ahmad
  • 9,673
  • 16
  • 64
  • 127
  • Try adding `overflow: auto` to the `.visible` class. to counteract the `overflow: hidden` on the `.hidden` class. -- but in reality.. I'm wondering if you are expecting the new items to transition. Since they aren't *already* part of the DOM, I don't think they'll transition via CSS. – Scott Feb 08 '16 at 00:29
  • @SOIA tried it, didn't do anything – Abdul Ahmad Feb 08 '16 at 00:31
  • I think you may have written this question a bit backwards -- 20 to 0 works. 0 to 20 does not.... but see my previous comment. – Scott Feb 08 '16 at 00:33
  • 1
    @SOIA I think they're part of the dom, they just have 0 height at first, then I update their height (see the angular code) – Abdul Ahmad Feb 08 '16 at 00:34
  • It's because your element's height is already on `20px`. Have you tried using css `keyframes`? – Lekz Flores Feb 08 '16 at 00:36
  • 1
    @LekzFlores no I haven't, but how is the height already on 20? try to comment out the code in the add function in the angular controller where the class is updated. the li won't appear. – Abdul Ahmad Feb 08 '16 at 00:38
  • Inside your `JS` code. As soon as you add `items` and set its class as `visible`, is it you already set its height to `20px`? Btw, using `keyframes` https://jsfiddle.net/1rqhLo83/1/. Just need a little fix. – Lekz Flores Feb 08 '16 at 00:43

1 Answers1

4

Here is the order in which the events occur:

  • The element is appended with a class of hidden
  • The class is subsequently changed to visible
  • Then a reflow/paint event occurs and the CSS is updated visually

In doing so, you aren't seeing the transition when the element is initially appended because the repaint event doesn't occur until the class is already visible.

One solution is to append the element with both classes and then remove the visible class after a slight 10ms timeout. In doing so, a reflow event will occur and the transition will take effect as expected.

Updated Example

self.addItem = function() {
    var newItem = {
    id: self.items.length,
    class: 'visible hidden',
    text: self.newItemText
  };
  self.items.push(newItem);
  $timeout(function () {
    self.items[self.items.length - 1].class = 'visible';
  }, 10);
  self.newItemText = '';
}

Then modify your CSS to the following:

.visible {
  height: 20px;
  transition: height 1s linear;
  overflow: hidden;
}
.hidden {
  height: 0;
}
Community
  • 1
  • 1
Josh Crozier
  • 233,099
  • 56
  • 391
  • 304