134

I'm trying to have an ellipsis animate, and was wondering if it was possible with CSS animations...

So it might be like

Loading...
Loading..
Loading.
Loading...
Loading..

And basically just continue like that. Any ideas?

Edit: like this: http://playground.magicrising.de/demo/ellipsis.html

danronmoon
  • 3,814
  • 5
  • 34
  • 56
Rey
  • 3,639
  • 5
  • 33
  • 40

10 Answers10

133

How about a slightly modified version of @xec's answer: http://codepen.io/thetallweeks/pen/yybGra

CSS Animation that uses steps. See MDN docs

.loading:after {
  overflow: hidden;
  display: inline-block;
  vertical-align: bottom;
  -webkit-animation: ellipsis steps(4, end) 900ms infinite;
  animation: ellipsis steps(4, end) 900ms infinite;
  content: "\2026";
  /* ascii code for the ellipsis character */
  width: 0px;
}

@keyframes ellipsis {
  to {
    width: 40px;
  }
}

@-webkit-keyframes ellipsis {
  to {
    width: 40px;
  }
}
<h1 class="loading">Loading</h1>

@xec's answer has more of a slide-in effect on the dots, while this allows the dots to appear instantly.

Gangula
  • 5,193
  • 4
  • 30
  • 59
thetallweeks
  • 6,875
  • 6
  • 25
  • 24
  • 3
    I mean you did answer this like 3 years later but this is probably better. – Rey Dec 21 '15 at 17:41
  • 7
    @xckpd7 yea, but I googled this today and just found this answer. SO isn't just for the OP, it's a resource for all! – Matt Parrilla Dec 21 '15 at 18:17
  • 1
    @MattParrilla I am the OP, and if you noticed I changed the answer I accept to this one before I made that comment. – Rey Dec 21 '15 at 18:51
  • 12
    If you're using this on text that's centred or aligned right, I'd suggest adding an initial `margin-right` (or padding?) of `20px` and animating it to `0px` if you don't want your text shifting during the animation. – Kimball Apr 27 '16 at 01:10
  • `1em` in place of `20px` may work better for the CSS in this answer – Jeevan Takhar Apr 14 '20 at 21:10
  • Not sure how this answer is better than @CodeBrauer's answer. The above answer doesn't work for me, and I also can't see why it would work. The display: inline-block; actually hides the text entirely. – rm.rf.etc Apr 19 '21 at 23:27
72

Even a more simple solution, works pretty well!

<style>
    .loading::after {
      display: inline-block;
      animation: dotty steps(1,end) 1s infinite;
      content: '';
    }
    
    @keyframes dotty {
        0%   { content: ''; }
        25%  { content: '.'; }
        50%  { content: '..'; }
        75%  { content: '...'; }
        100% { content: ''; }
    }
</style>
<div class="loading">Loading</div>

Just edited the content with animation instead of hiding some dots...

Demo here: https://jsfiddle.net/f6vhway2/1/


Edit: Thanks to @BradCollins for pointing out that content is not an animatable property.

Currently, (2021) this works in Chrome/WebKit/Blink/Electron and Firefox and new version of Edge.

CodeBrauer
  • 2,690
  • 1
  • 26
  • 51
  • 1
    I was looking at this thread just last week. Nice simple answer! – r8n5n Dec 06 '16 at 09:31
  • 2
    +1 for animating `content`. To get an even animation rhythm you should use `steps(1)` and define one extra key frame. The step function dictates the number of steps between key frames and since we're specifying each frame we just want a single step between them. http://codepen.io/anon/pen/VmEdXj – Jeff Camera Dec 14 '16 at 04:54
65

You could try to use the animation-delay property and time each ellipsis character. In this case I've put each ellipsis character in a <span class> so I can animate them separately.

I made a demo, which isn't perfect, but it shows at least what I mean :)

The code from my example:

HTML

Loading<span class="one">.</span><span class="two">.</span><span class="three">.</span>​

CSS

.one {
    opacity: 0;
    -webkit-animation: dot 1.3s infinite;
    -webkit-animation-delay: 0.0s;
    animation: dot 1.3s infinite;
    animation-delay: 0.0s;
}

.two {
    opacity: 0;
    -webkit-animation: dot 1.3s infinite;
    -webkit-animation-delay: 0.2s;
    animation: dot 1.3s infinite;
    animation-delay: 0.2s;
}

.three {
    opacity: 0;
    -webkit-animation: dot 1.3s infinite;
    -webkit-animation-delay: 0.3s;
    animation: dot 1.3s infinite;
    animation-delay: 0.3s;
}

@-webkit-keyframes dot {
    0% {
        opacity: 0;
    }
    50% {
        opacity: 0;
    }
    100% {
        opacity: 1;
    }
}

@keyframes dot {
    0% {
        opacity: 0;
    }
    50% {
        opacity: 0;
    }
    100% {
        opacity: 1;
    }
}
Christofer Vilander
  • 17,232
  • 6
  • 31
  • 26
  • I like this idea and just expanded on it a bit to show "marching elipses": http://jsfiddle.net/toddwprice/cRLMw/ – Todd Price Nov 14 '12 at 23:20
  • It didn't work(?), so I added display: inline; and then the dots. Is this how it was meant to work? http://jsfiddle.net/cRLMw/3/ – Christofer Vilander Nov 15 '12 at 10:25
  • 4
    Sorry @Christofer -- forgot to save my updated fiddle. Here it is again: http://jsfiddle.net/toddwprice/cRLMw/8/ Also, here's an article I just read that has some interesting CSS animations: http://tympanus.net/Tutorials/LoadingAnimations/index4.html – Todd Price Nov 16 '12 at 16:04
  • Using Firefox I can't drag it if I simply click and drag the image in one shot. But if I click on the image first and then click and drag, I am not prevented from dragging. – Sam Rueby Nov 08 '13 at 16:22
  • 2
    I tweaked the HTML and CSS a little to use tags... http://jsfiddle.net/DkcD4/77/ – Adam Youngers Mar 20 '14 at 19:02
18

Short answer is "not really". However, you can play around with animating width and overflow hidden, and maybe get an effect that is "close enough". (code below tailored for firefox only, add vendor prefixes as needed).

html

<div class="loading">Loading</div>

css

.loading:after {
    overflow: hidden;
    display: inline-block;
    vertical-align: bottom;
    -moz-animation: ellipsis 2s infinite;
    content: "\2026"; /* ascii code for the ellipsis character */
}
@-moz-keyframes ellipsis {
    from {
        width: 2px;
    }
    to {
        width: 15px;
    }
}

demo: http://jsfiddle.net/MDzsR/1/

edit

It appears chrome has issues with animating the pseudo-element. An easy fix is to wrap the ellipsis in its own element. Check out http://jsfiddle.net/MDzsR/4/

xec
  • 17,349
  • 3
  • 46
  • 54
  • [Not working in Chromium](http://jsfiddle.net/davidThomas/MDzsR/2/) (yes, I changed the vendor-prefix to `-webkit` from `-moz`). – David Thomas Oct 22 '12 at 18:34
  • @DavidThomas you're right - tested in chrome now and it seems it has issues with the pseudo element. You could wrap the ellipsis in its own element and animate that instead (would work in firefox too) http://jsfiddle.net/MDzsR/4/ – xec Oct 23 '12 at 07:52
  • 1
    Really nice solution, and just perfect for a Firefox OS app that I'm developing. Tweaked it a little bit: http://jsfiddle.net/feklee/x69uN/ – feklee Jun 24 '14 at 09:37
  • 1
    Here's an improved version that works on Chrome, as well as works correctly with non-left aligned elements (doesn't change the width). Also, it shows each dot consecutively, without this sliding revealing artifact: http://jsfiddle.net/fze2qxsv/ – Aayla Secura Jun 06 '20 at 20:28
  • @AaylaSecura The accepted answer has a cleaner solution using steps instead https://stackoverflow.com/a/28074607/833146 – xec Jul 03 '20 at 12:52
  • @xec I like the steps, however it does not work with non-left aligned elements. See this: https://jsfiddle.net/pgb6e15j/ Using `clip-path` instead of `width` does work. I just can't seem to integrate the `clip-path` and steps, it behaves strangely, see this: http://jsfiddle.net/yagu423f/ – Aayla Secura Jul 03 '20 at 21:25
  • @AaylaSecura I solved it based on your idea: https://stackoverflow.com/a/75179539/263061 – nh2 Jan 20 '23 at 00:28
9

A late addition but I found a way to do this which supports centered text.

<element>:after {
    content: '\00a0\00a0\00a0';
    animation: progress-ellipsis 5s infinite;
}

@keyframes progress-ellipsis {
    0% {
        content: '\00a0\00a0\00a0';
    }
    30% {
        content: '.\00a0\00a0';
    }
    60% {
        content: '..\00a0';
    }
    90% {
        content: '...';
    }
}
anon-e-mouse
  • 91
  • 1
  • 1
4

You can animate clip (or better clip-path if you don't need IE support)

div {
  display: inline-block;
  font-size: 1.4rem;
}

div:after {
  position: absolute;
  margin-left: .1rem;
  content: ' ...';
  animation: loading steps(4) 2s infinite;
  clip: rect(auto, 0px, auto, auto);
}

@keyframes loading {
  to {
    clip: rect(auto, 20px, auto, auto);
  }
}
<div>Loading</div>
Sobral
  • 135
  • 5
Jakob E
  • 4,476
  • 1
  • 18
  • 21
3

Well Actually there is a pure CSS way of doing this.

I got the example from CSS Tricks, but made it also to be supported in Internet Explorer (I have tested it in 10+).

Check the Demo: http://jsfiddle.net/Roobyx/AT6v6/2/

HTML:

<h4 id="searching-ellipsis"> Searching
    <span>.</span>
    <span>.</span>
    <span>.</span>
</h4>

CSS:

@-webkit-keyframes opacity {
  0% {
    filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100);
    opacity: 1;
  }
  100% {
    filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0);
    opacity: 0;
  }
}

@-moz-keyframes opacity {
  0% {
    filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100);
    opacity: 1;
  }

  100% {
    filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0);
    opacity: 0;
  }
}

@-webkit-keyframes opacity {
  0% {
    filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100);
    opacity: 1;
  }
  100% {
    filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0);
    opacity: 0;
  }
}

@-moz-keyframes opacity {
  0% {
    filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100);
    opacity: 1;
  }
  100% {
    filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0);
    opacity: 0;
  }
}

@-o-keyframes opacity {
  0% {
    filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100);
    opacity: 1;
  }
  100% {
    filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0);
    opacity: 0;
  }
}

@keyframes opacity {
  0% {
    filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100);
    opacity: 1;
  }
  100% {
    filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0);
    opacity: 0;
  }
}
#searching-ellipsis span {
  -webkit-animation-name: opacity;
  -webkit-animation-duration: 1s;
  -webkit-animation-iteration-count: infinite;
  -moz-animation-name: opacity;
  -moz-animation-duration: 1s;
  -moz-animation-iteration-count: infinite;
  -ms-animation-name: opacity;
  -ms-animation-duration: 1s;
  -ms-animation-iteration-count: infinite;
}
#searching-ellipsis span:nth-child(2) {
  -webkit-animation-delay: 100ms;
  -moz-animation-delay: 100ms;
  -ms-animation-delay: 100ms;
  -o-animation-delay: 100ms;
  animation-delay: 100ms;
}
#searching-ellipsis span:nth-child(3) {
  -webkit-animation-delay: 300ms;
  -moz-animation-delay: 300ms;
  -ms-animation-delay: 300ms;
  -o-animation-delay: 300ms;
  animation-delay: 300ms;
}
MRadev
  • 411
  • 9
  • 19
  • You are adding proprietary IE-only filters in mozilla-specific and webkit-specific keyframes. How is this an improvement over the already accepted solution? It even has the same issue (in frames 4 and 5 only the two last and very last dots are visible, respectively, as opposed to what is outlined in the question, which has 3 repeating states, not 5) – xec May 14 '14 at 21:41
  • The question is about creating loading dots, and there is just a near example, not mandatory. What I have added is prefixes, so IE can recognize it better and display it. – MRadev May 29 '14 at 11:59
  • 4
    `-webkit-keyframes` will only apply to webkit, and inside you have IE-only code. This code does nothing but waste space. – xec May 30 '14 at 09:17
1

I found clip-path to be the cleanest, with the following benefits:

Does not use width, thus:

  • works independent of how wide the ellipsis is in whatever font is used.
  • Does not shift the layout (good for performance, and allows more text behind it without that moving around or reflow).

.loading-ellipsis:after {
    overflow: hidden;
    display: inline-block;
    vertical-align: bottom;
    animation: ellipsis-animation steps(1,end) 2s infinite;
    content: "\2026"; /* ascii code for the ellipsis character */
    /* Enable this to see what is going on: */
    /* background-color: red; */
}

@keyframes ellipsis-animation {
    0%  { clip-path: inset(0 100% 0 0); }
    25% { clip-path: inset(0 66.6% 0 0); }
    50% { clip-path: inset(0 33.3% 0 0); }
    75% { clip-path: inset(0 0 0 0); }
}
<span class="loading-ellipsis">Loading</span> More text behind it that does not move

Credits go to @AaylaSecura's comment, and I improved that to use steps(1,end). This works because I end the animation at 75%, so that the last step shows the full expansion of the ellipsis (the third dot).

(There is an implicit 100% { clip-path: inset(0 0 0 0); } behind it that need not be written.)

nh2
  • 24,526
  • 11
  • 79
  • 128
0

Here is my solution with pure css https://jsfiddle.net/pduc6jx5/1/ explained: https://medium.com/@lastseeds/create-text-ellipsis-animation-with-pure-css-7f61acee69cc

scss



.dot1 {
 animation: visibility 3s linear infinite;
}

@keyframes visibility {
 0% {
 opacity: 1;
 }
 65% {
 opacity: 1;
 }
 66% {
 opacity: 0;
 }
 100% {
 opacity: 0;
 }
}

.dot2 {
 animation: visibility2 3s linear infinite;
}

@keyframes visibility2 {
 0% {
  opacity: 0;
 }
 21% {
  opacity: 0;
 }
 22% {
  opacity: 1;
 }
 65% {
  opacity: 1;
 }
 66% {
  opacity: 0;
 }
 100% {
  opacity: 0;
 }
}

.dot3 {
 animation: visibility3 3s linear infinite;
}

@keyframes visibility3 {
 0% {
  opacity: 0;
 }
 43% {
  opacity: 0;
 }
 44% {
  opacity: 1;
 }
 65% {
  opacity: 1;
 }
 66% {
  opacity: 0;
 }
 100% {
  opacity: 0;
 }
}

html

Loading <span class="dot dot1">.</span><span class="dot dot2">.</span><span class="dot dot3">.</span>
repo
  • 748
  • 1
  • 8
  • 19
0

I did exactly what @CodeBrauer very cleanly did above, but because my text was text-align: center (this works for right, too) and I didn't want the text to move over every time a period was added, I added "punctuation spaces":

<style>
    .loading::after {
      display: inline-block;
      animation: dotty steps(1,end) 1s infinite;
      content: '';
    }
    
    @keyframes dotty {
      0%   { content: '\2008\2008\2008'; }
      25%  { content: '.\2008\2008'; }
      50%  { content: '..\2008'; }
      75%  { content: '...'; }
      100% { content: '\2008\2008\2008'; }
    }
</style>
<div class="loading">Loading</div>
lanewinfield
  • 183
  • 1
  • 1
  • 8