4

My project is almost ready and one the of the last things I would like do is to animate .quote-container element's height. Currently, it changes very quickly without any animation effect and unfortunately, this line of CSS didn't work:

.quote-container {
  transition: height 1s;
}

However, the same line only applied to the #new-quote selector, worked:

#new-quote {
  transition: height 1s;
}
See the Pen gomeYN by Lukas (@Kestis500) on CodePen.

I've made a video so you can understand what I mean: https://www.youtube.com/watch?v=21V3NbAagnk&feature=youtu.be.

Also, in the 0:08 - 0:09 seconds there is a buggy up and down height change of the .quote-container element. What's happening here?

I've looked at the animate() function in the jQuery documentation but I don't understand how to implement it if text in the .quote-container changes every time you press the #new-quote button.


It's not an exactly the same question as this one. I've done what was written in that question's answer but it still doesn't work like it should. Plus it's using CSS, not JS. I've saved my codepen: https://codepen.io/Kestis500/pen/gomeYN?editors=0100. Can someone please take a look and say what I've done wrong? Here are my changes:

//CSS
.quote-container, #new-quote { transition: max-height 1s; }

//JS
$("#new-quote").on("click", function() {
  $(".quote-container").css({ maxHeight: 0, overflow: "hidden" });
  $(".quote-text, .quote-author").fadeTo(1000, 0, function() {
    getQuote(function() {
      $(".quote-container, #new-quote").css({
        maxHeight: $(".quote-container").outerHeight()
      });
      $(".quote-text, .quote-author").fadeTo(1000, 1);
    });
  });
});

EDIT 1 I've changed my code so when the page loads, it changes the max-height smoothly resulting in the smooth animation what I needed. Codepen: https://codepen.io/Kestis500/pen/WdpBqv?editors=0010, however, I'm still working on the click event because it's not working the same way as page load.

EDIT 2 Thank you Twisty for your huge help :) I've done some changes to my own preference and now the only thing that needs to be fixed is that animation should start at the same time (#new-quote and .quote-container elements), however, I think it's gonna be pretty easy to fix :)

EDIT 3 Here we go! Exactly what I needed and the animation is amazing! :) https://jsfiddle.net/z5hds4Lp/.

EDIT 4 https://jsfiddle.net/z5hds4Lp/2/ - final edit.

Note: Don't use pixels either in the media queries or CSS, use rems for CSS and ems for media queries: https://engageinteractive.co.uk/blog/em-vs-rem-vs-px and https://zellwk.com/blog/media-query-units/.

  • plus +1 for the video XD – plonknimbuzz Dec 28 '17 at 16:37
  • 1. your recent codepan script is different with your video. 2. your height is flexible with the content. if you have same lines number, your div will have same height, but if your next new quote have more or less lines number, your div will get higher or lower. what wrong with this? this is fine for me. what do you want? – plonknimbuzz Dec 28 '17 at 16:48
  • 2
    @plonknimbuzz he wants it to animate in a smooth transition from one height to the next instead of jumping when the size changes (similar to what you see the new quote div on the right side doing) – abney317 Dec 28 '17 at 16:51

2 Answers2

4

To use .animate() to change the height of an element, you must set a value of Pixels.

Example:

$(function() {
  $("#clickme").click(function() {
    $("#book").animate({
      opacity: 1,
      height: 123
    }, 1000, function() {
      // Animation complete.
    });
  });
});
#book {
  width: 20px;
  height: 30px;
  border: 1px solid black;
  background-color: #CCC;
  opacity: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="clickme">
  Click here
</div>
<div id="book"></div>

You can adjust a lot of CSS Properties. Most will only take integers. Some will take alternatives.

In addition to numeric values, each property can take the strings 'show', 'hide', and 'toggle'. These shortcuts allow for custom hiding and showing animations that take into account the display type of the element. In order to use jQuery's built-in toggle state tracking, the 'toggle' keyword must be consistently given as the value of the property being animated.

Animated properties can also be relative. If a value is supplied with a leading += or -= sequence of characters, then the target value is computed by adding or subtracting the given number from the current value of the property.

For your code, you may consider something like (Snippet):

$("#new-quote").on("click", function() {
  $(".quote-text, .quote-author").fadeTo(1000, 0, function() {
    getQuote(function() {
      $("#new-quote").animate({
        height: $(".quote-container").outerHeight(),
        opacity: 1
      }, 1000);
    });
  });
});

getQuote(function() {
  $("#new-quote").animate({
    height: $(".quote-container").outerHeight(),
    opacity: 1
  }, 500);
});

Working: https://jsfiddle.net/k24ykxeb/2/

Hope this helps.

Update 1

Added some logging, including timing, and I see this:

Start. 0
Ready. 14
Animate body, .button:not(#new-quote) Background. 19
Animate Text Color Change. 29
Updating Text Color. 31
Appending  to Head. 32
JSON Callback Started. 888
Running Callback Function. 890
Start Resize Animation. Target Outer Height: 222; 893
JSON Callback Complete. 898
Completed Resize Animation, new height: 222, opacity: 1, after 500ms 1405
Completed Resize Animation, new height: 222, opacity: 1, after 500ms 1406

Working Example: https://jsfiddle.net/k24ykxeb/7/

Whats interesting here is that I am see the animation appear to complete twice. Events seem to be executing as intended. I was concerned that the resize was triggering before the Quote and title were appended. Does not appear to be the case.

Still investigating.

Update 2

Found that the padding for #new-quote was being defined by the .button class and did not match the .quote-container. I adjuste it to the following:

padding: 2.5rem 0.75rem;

This addressed the 40px gap that was being left. This in working example can be seen here: https://jsfiddle.net/k24ykxeb/9/

Another option would be to make .quote-container contain #new-quote within it. This would allow it to reflow with the parent.

The cloning technique will work for the Ajax call, where you have no idea how much text there will be, thus you cannot calculate the new height until after the text as been placed in the div. If you pull the text, clone the div, add the text to the clone, you can get the height property. Then drop the clone, resize the container and fadein the text.

Update 3

A lot was in motion and it had to be set into motion at specific times to make the animation work properly. Enter .queue().

Example: https://jsfiddle.net/k24ykxeb/26/

jQuery Snippet

var getQuote = function(e) {
  /***
  Steps
  1) Fade Text Out
  2) Bounce expansion effect
  3) Change color
  4) Animate to new size
  5) Fade New Text In
  ***/

  var nq, nc, nh = 0;

  $(".quote-container").queue(function() {
      // 1) Fade out text
      console.log("Step 1", Date.now() - start);
      $(".quote-container").children().animate({
        opacity: 0
      }, 750);
      $(this).dequeue();
    })
    .queue(function() {
      console.log("Step 3", Date.now() - start);
      nq = getNewQuote();
      nc = getRandomColor();
      // 3) Change Color
      changeColor(nc);
      $(this).dequeue();
    })
    .queue(function() {
      console.log("Step 4", Date.now() - start);
      // 4) Animate to new size
      nh = calcNewHeight(nq);
      $(".quote-container, #new-quote").animate({
        height: nh
      }, 1000);
      $(this).dequeue();
    })
    .queue(function() {
      console.log("Step 5", Date.now() - start);
      // 5) Fade in new text
      updateText($(".quote-container"), nq);
      $(".quote-container").children().fadeTo(750, 1);
      $(this).dequeue();
    })
};

$(function() {
  console.log("Ready.", Date.now() - start);
  $("#new-quote").on("click", getQuote);
  getQuote();
});

Also the $.getJSON() was executing asyncronously, so I switched to $.ajax() for the async: false benefit.

Community
  • 1
  • 1
Twisty
  • 30,304
  • 2
  • 26
  • 45
  • I checked your fiddle. The `.quote-container` isn't showing so I added it next to the `#new-quote` animation. Something is wrong here: https://image.prntscr.com/image/jLobIHibRe2OIJEHWdsgSQ.png. – Lukas Naujokaitis Dec 28 '17 at 17:46
  • @LukasNaujokaitis what I see is that `.outerHeight()` is returning the `height` from the `style` attribute initially. See here: https://jsfiddle.net/k24ykxeb/3/ – Twisty Dec 28 '17 at 21:08
  • I've done some changes and now this problem is kinda fixed (after 4 hours of work :D). – Lukas Naujokaitis Dec 28 '17 at 21:43
  • @LukasNaujokaitis some updates: https://jsfiddle.net/k24ykxeb/4/ no fix yet – Twisty Dec 28 '17 at 22:31
  • @LukasNaujokaitis something to look at: https://stackoverflow.com/questions/8047284/using-jquery-to-animate-inserting-text-into-div which leads to http://jsfiddle.net/MGrrR/ - The idea here is that the div is cloned, appended with text, and the new height is captured from there. Then the height could be adjust before the text is added. Allowing for a smooth transition. – Twisty Dec 29 '17 at 00:17
  • I took a look, it's using slideDown() function but it won't work in both directions, only up or down. Check out my edit, finally made it work but using setTimeout(), which, unfortunately, could lead to delays. I'd like to fix this first problem without setTimeout(), so yeah, still investigating. – Lukas Naujokaitis Dec 29 '17 at 08:34
  • Check my codepen, Twisty. I think I've done some improvement, however, still don't understand why sometimes it works, sometimes - no. I mean - about 90% works and 10% not. – Lukas Naujokaitis Dec 29 '17 at 16:41
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/162188/discussion-between-lukas-naujokaitis-and-twisty). – Lukas Naujokaitis Dec 29 '17 at 20:05
  • Thank you, it seems to work. I'll take a closer look and change some things up to my own preference :) – Lukas Naujokaitis Dec 31 '17 at 09:53
  • However, how to make .quote-container and #new-quote slide at the same time, because now #new-quote animation is a bit slower. – Lukas Naujokaitis Dec 31 '17 at 09:58
  • Also, if you press a "new-quote" button like 10 times in 3seconds, it's gonna bug very hard and animations don't work correctly. I tried using `e.preventDefault();`, but it didn't help. – Lukas Naujokaitis Dec 31 '17 at 17:16
0

Another option you can try - though with a slightly different animation effect is to .slideUp(), change the .html(), and then .slideDown() again. It's not exactly what you're looking for but it's a nice easy alternative and might be useful for someone else reading.

$('#container').slideUp(function(){
     $('#container').html('New content');
     $('#container').slideDown();
});

Note the .html() and .slideDown() commands are nested in a function of .slideUp. This makes sure the content or animation doesn't happen until the .slideUp() is complete.

Colin R. Turner
  • 1,323
  • 15
  • 24