Himanshu SharmaSoftware engineer
Let’s talk

jQuery · Lesson 31 of 45

Custom animations with .animate()

Animate numeric CSS properties with jQuery .animate(), use relative values and toggle, set duration and easing, and use the step and complete options.

12 minEffects and animationUpdated 23 October 2026

By the end of this lessonWrite your own animations of size, position, opacity and scroll, and know which properties can and cannot be animated by jQuery.

Your own animations

.show(), .fade…() and .slide…() are ready-made animations. .animate() lets you animate any numeric CSS property to a value you choose:

html
<div id="bar" style="width: 40px; height: 24px; background: #22c55e"></div>
<button id="grow">Grow</button>

<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
  $("#grow").on("click", function () {
    $("#bar").animate({ width: 260, opacity: 0.6 }, 500, function () {
      console.log("width:", $(this).css("width"), "| opacity:", $(this).css("opacity"));
    });
  });
</script>
result
width: 260px | opacity: 0.6

The arguments are:

  1. an object of target values (as with .css(), plain numbers get px for sizes);
  2. a duration (default 400ms);
  3. optionally an easing name ("swing" or "linear");
  4. optionally a callback for when it finishes.

All properties in the object animate together.

What can be animated

jQuery animates by stepping a single number from its start to its end value. That works for:

  • width, height, opacity;
  • margin…, padding…, border…Width;
  • top, left, right, bottom (on positioned elements);
  • font-size and line-height;
  • scrollTop and scrollLeft.

It does not work for colours (color, background-color), transform, or anything that is not one number, such as display. For those, use a CSS transition triggered by a class (a later lesson), or a plugin such as jQuery Color.

Relative values and keywords

Values can be relative to the current value, and three keywords do the work of the show and hide effects:

html
<style>
  #stage { position: relative; height: 70px; background: #f8fafc; }
  #ball { position: absolute; left: 0; top: 15px; width: 40px; height: 40px; border-radius: 50%; background: #f97316; }
</style>
<div id="stage"><div id="ball"></div></div>
<button id="right">Move right</button> <button id="collapse">Collapse stage</button>

<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
  $("#right").on("click", function () {
    $("#ball").animate({ left: "+=80" }, 300, function () {
      console.log("ball left:", $(this).css("left"));
    });
  });
  $("#collapse").on("click", function () {
    $("#stage").animate({ height: "toggle" }, 300, function () {
      console.log("stage visible:", $(this).is(":visible"));
    });
  });
</script>
result
ball left: 80px
ball left: 160px
stage visible: false
  • "+=80" and "-=80" add to or subtract from the current value.
  • "show", "hide" and "toggle" animate to or from the element's normal value, and hide or show it at the end.

The ball moves because its container is position: relative and the ball is position: absolute. On an element with the default position: static, top and left change nothing.

Smooth scrolling

scrollTop is animatable, which gives smooth scrolling to a section, a common use of .animate() on older sites:

html
<button id="jump">Jump to the end of the box</button>
<div id="feed" style="height: 80px; overflow: auto; border: 1px solid #cbd5e1">
  <p>One</p><p>Two</p><p>Three</p><p>Four</p><p id="last">The end</p>
</div>

<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
  $("#jump").on("click", function () {
    const $feed = $("#feed");
    const target = $feed.scrollTop() + $("#last").position().top;
    $feed.animate({ scrollTop: target }, 400, function () {
      console.log("scrolled down:", $feed.scrollTop() > 0);
    });
  });
</script>
result
scrolled down: true

For the whole page, animate $("html, body"). Modern browsers can also do this without jQuery: element.scrollIntoView({ behavior: "smooth" }), or scroll-behavior: smooth in CSS.

The options object

Instead of separate arguments, you can pass an options object, which unlocks more control:

html
<p>Downloaded: <span id="pct">0</span>%</p>
<div style="background: #e2e8f0; width: 200px"><div id="fill" style="width: 0; height: 10px; background: #2563eb"></div></div>
<button id="count">Start</button>

<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
  $("#count").on("click", function () {
    $("#fill").animate({ width: 200 }, {
      duration: 600,
      easing: "linear",
      step: function (now) {
        $("#pct").text(Math.round(now / 2));
      },
      complete: function () {
        console.log("complete at", $("#pct").text() + "%");
      }
    });
  });
</script>
result
complete at 100%
  • step(now, tween) runs for every animated property on every frame, with the current value; here it updates the percentage text.
  • progress(animation, fraction, msRemaining) runs once per frame with the overall progress.
  • complete is the callback; done, fail and always are promise-style alternatives.
  • queue: false runs the animation immediately instead of waiting for earlier ones (next lesson).
  • specialEasing: { width: "linear", height: "swing" } sets easing per property.

jQuery or CSS?

For simple movement and fading, CSS transitions and animations are smoother, because the browser can run them off the main JavaScript thread, and they can animate colours and transforms. Use .animate() when the target values are calculated at run time, when you need step callbacks, or when you are maintaining code that already uses it. The CSS transitions lesson later in this section shows how to combine the two.

Common mistakes

  • Animating colours or transforms and seeing nothing happen.
  • Animating left on a static element.
  • Forgetting that animations queue: clicking quickly ten times runs ten animations in a row.
  • Using .animate() with display, which is not a number; use the "show"/"hide" keywords.

Exercise

Animate a box so that one click makes it twice as wide (from its current width) and fades it to 50% opacity, then logs its new width in the callback.

Show one possible answer
html
<div id="box" style="width: 80px; height: 40px; background: #c084fc"></div>
<button id="b">Go</button>

<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
  $("#b").on("click", function () {
    const $box = $("#box");
    $box.animate({ width: $box.width() * 2, opacity: 0.5 }, 400, function () {
      console.log($box.width());
    });
  });
</script>
result
160

Key points

  • .animate({ prop: value }, duration, easing, callback) animates numeric CSS properties to new values.
  • Only numbers animate: width, height, opacity, margins, padding, top/left, font-size and scroll positions, but not colours without a plugin.
  • Values can be relative ("+=50") or the keywords "show", "hide" and "toggle".
  • An options object adds step and progress callbacks, a queue setting and per-property easing.

Check yourself

1. Which property can .animate() NOT animate without a plugin?
2. What does .animate({ left: "+=100" }) do?
3. Why must an element have position set for top and left animations to show?
4. What value does .animate({ height: "toggle" }) animate to?