203

Twitter Bootstrap's buttons have a nice Loading... state available.

The thing is that it just shows a message like Loading... passed through the data-loading-text attribute like this:

<button type="button" class="btn btn-primary start" id="btnStartUploads"
        data-loading-text="@Localization.Uploading">
    <i class="icon-upload icon-large"></i>
    <span>@Localization.StartUpload</span>
</button>

Looking at Font Awesome, you see that there's now an animated spinner icon.

I tried to integrate that spinner icon when firing an Upload operation like this:

$("#btnStartUploads").button('loading');
$("#btnStartUploads i").removeAttr('class');
$("#btnStartUploads i").addClass('icon-spinner icon-spin icon-large');

but this had no effect at all, that is, I just see the Uploading... text on the button.

Is it possible to add an icon when the button is in the Loading state? Looks like somehow Bootstrap just removes the icon <i class="icon-upload icon-large"></i> inside the button while in the Loading state.


Here's a simple demo that shows the behavior I describe above. As you see when it enters the Loading state the icon just disappears. It reappears right after the time interval.

2

10 Answers 10

340

Simple solution for Bootstrap 3 using CSS3 animations.

Put the following in your CSS:

.glyphicon.spinning {
    animation: spin 1s infinite linear;
    -webkit-animation: spin2 1s infinite linear;
}

@keyframes spin {
    from { transform: scale(1) rotate(0deg); }
    to { transform: scale(1) rotate(360deg); }
}

@-webkit-keyframes spin2 {
    from { -webkit-transform: rotate(0deg); }
    to { -webkit-transform: rotate(360deg); }
}

Then just add the spinning class to a glyphicon while loading to get your spinning icon:

<button class="btn btn-lg btn-warning">
    <span class="glyphicon glyphicon-refresh spinning"></span> Loading...    
</button>

Based on http://www.bootply.com/128062#

  • Note: IE9 and below do not support CSS3 animations.
10
  • 4
    Shouldn't it be animation and not -animation? Commented Feb 6, 2015 at 9:16
  • 3
    Excellent solution. I'm having issues with this animating in Safari on the desktop and on the iPad. It shows the icon but doesn't animate it. Ever experience something like that? Commented Mar 18, 2015 at 21:18
  • @JayhawksFan93 yes I recently noticed the same in IE. Will look into it soon
    – Flion
    Commented Mar 19, 2015 at 8:19
  • 2
    the answer was updated to animation instead of -animation. With that change fine in FF and IE for me. The firefox animation doesnt look very smooth though.
    – Flion
    Commented Apr 7, 2015 at 9:28
  • 1
    +1. Found a similar one here.... Posting for record...
    – Fr0zenFyr
    Commented Aug 1, 2015 at 11:10
101

If you look at the bootstrap-button.js source, you'll see that the bootstrap plugin replaces the buttons inner html with whatever is in data-loading-text when calling $(myElem).button('loading').

For your case, I think you should just be able to do this:

<button type="button"
        class="btn btn-primary start"
        id="btnStartUploads"
        data-loading-text="<i class='icon-spinner icon-spin icon-large'></i> @Localization.Uploading">
    <i class="icon-upload icon-large"></i>
    <span>@Localization.StartUpload</span>
</button>
9
  • 1
    Works great gurch101! I forget that one can mix HTML with text in a tag property. :-) Commented Feb 10, 2013 at 16:23
  • 11
    Fiddle not working for Safari 6.0.5 (7536.30.1), Chrome 31.0.1604.0 canary on Mac OS X. Commented Aug 21, 2013 at 21:01
  • 16
    Fixed: jsfiddle.net/6U5q2/270 Note this is for v2.3.2. Don't know if it works for 3.x Commented Sep 3, 2013 at 22:59
  • 14
    data-loading-text is deprecated since v3.3.5 and will be removed in v4.
    – Jonathan
    Commented Jun 28, 2015 at 19:57
  • 4
    @Jonathan If it is deprecated in v3.3.5 what is the replacement for that in both after v3.3.5 and v4?
    – PaladiN
    Commented Jan 2, 2017 at 5:54
66

There's now a full-fledged plugin for that:

http://msurguy.github.io/ladda-bootstrap/

2
  • 1
    Hi @Eru Penkman. What is the difference between yours and the original?
    – Ivan Wang
    Commented Mar 8, 2014 at 17:33
  • Hey Ivan, sort but I never got around to updating my copy of ladda! It's just the original, I removed my previous comment. Sorry about that!
    – roo2
    Commented Mar 9, 2014 at 12:11
14

To make the solution by @flion look really great, you could adjust the center point for that icon so it doesn't wobble up and down. This looks right for me at a small font size:

.glyphicon-refresh.spinning {
  transform-origin: 48% 50%;
}
3
  • 1
    .glyphicon-refresh.spinning { transform-origin: 50% 58%; } worked for me
    – oluckyman
    Commented Sep 26, 2015 at 9:24
  • hum, { transform-origin: 50% 49%; } solve in my case where I'm using cog instead. Commented Mar 18, 2016 at 17:49
  • I also notice the wobble, but what's the rationale for changing these numbers? How should I tune them?
    – elachell
    Commented Jan 21, 2018 at 20:00
5

A lazy way to do this is with the UTF-8 entity code for a half circle \25E0 (aka &#x25e0;), which looks like ◠ and then keyframe animate it. It's a simple as:

.busy
{
animation: spin 1s infinite linear;
display:inline-block;
font-weight: bold;
font-family: sans-serif;
font-size: 35px;
font-style:normal;
color:#555;
}

.busy::before
{
content:"\25E0";
}

@keyframes spin
{
0% {transform: rotate(0deg);}
100% {transform: rotate(359deg);}
}
<i class="busy"></i>

2

Here's my solution for Bootstrap 4:

<button id="search" class="btn btn-primary" 
data-loading-text="<i class='fa fa-spinner fa-spin fa-fw' aria-hidden='true'></i>Searching">
  Search
</button>

var setLoading = function () {
  var search = $('#search');
  if (!search.data('normal-text')) {
    search.data('normal-text', search.html());
  }
  search.html(search.data('loading-text'));
};

var clearLoading = function () {
  var search = $('#search');
  search.html(search.data('normal-text'));
};

setInterval(() => {
  setLoading();
  setTimeout(() => {
    clearLoading();
  }, 1000);
}, 2000);

Check it out on JSFiddle

2

These are mine, based on pure SVG and CSS animations. Don't pay attention to JS code in the snippet bellow, it's just for demoing purposes. Feel free to make your custom ones basing on mine, it's super easy.

var svg = d3.select("svg"),
    columnsCount = 3;

['basic', 'basic2', 'basic3', 'basic4', 'loading', 'loading2', 'spin', 'chrome', 'chrome2', 'flower', 'flower2', 'backstreet_boys'].forEach(function(animation, i){
  var x = (i%columnsCount+1) * 200-100,
      y = 20 + (Math.floor(i/columnsCount) * 200);
  
  
  svg.append("text")
    .attr('text-anchor', 'middle')
    .attr("x", x)
    .attr("y", y)
    .text((i+1)+". "+animation);
  
  svg.append("circle")
    .attr("class", animation)
    .attr("cx",  x)
    .attr("cy",  y+40)
    .attr("r",  16)
});
circle {
  fill: none;
  stroke: #bbb;
  stroke-width: 4
}

.basic {
  animation: basic 0.5s linear infinite;
  stroke-dasharray: 20 80;
}

@keyframes basic {
  0%    {stroke-dashoffset: 100;}
  100%  {stroke-dashoffset: 0;}
}

.basic2 {
  animation: basic2 0.5s linear infinite;
  stroke-dasharray: 80 20;
}

@keyframes basic2 {
  0%    {stroke-dashoffset: 100;}
  100%  {stroke-dashoffset: 0;}
}

.basic3 {
  animation: basic3 0.5s linear infinite;
  stroke-dasharray: 20 30;
}

@keyframes basic3 {
  0%    {stroke-dashoffset: 100;}
  100%  {stroke-dashoffset: 0;}
}

.basic4 {
  animation: basic4 0.5s linear infinite;
  stroke-dasharray: 10 23.3;
}

@keyframes basic4 {
  0%    {stroke-dashoffset: 100;}
  100%  {stroke-dashoffset: 0;}
}

.loading {
  animation: loading 1s linear infinite;
  stroke-dashoffset: 25;
}

@keyframes loading {
  0%    {stroke-dashoffset: 0;    stroke-dasharray: 50 0; }
  50%   {stroke-dashoffset: -100; stroke-dasharray: 0 50;}
  100%  { stroke-dashoffset: -200;stroke-dasharray: 50 0;}
}

.loading2 {
  animation: loading2 1s linear infinite;
}

@keyframes loading2 {
  0% {stroke-dasharray: 5 28.3;   stroke-dashoffset: 75;}
  50% {stroke-dasharray: 45 5;    stroke-dashoffset: -50;}
  100% {stroke-dasharray: 5 28.3; stroke-dashoffset: -125; }
}

.spin {
  animation: spin 1s linear infinite;
  stroke-dashoffset: 25;
}

@keyframes spin {
  0%    {stroke-dashoffset: 0;    stroke-dasharray: 33.3 0; }
  50%   {stroke-dashoffset: -100; stroke-dasharray: 0 33.3;}
  100%  { stroke-dashoffset: -200;stroke-dasharray: 33.3 0;}
}

.chrome {
  animation: chrome 2s linear infinite;
}

@keyframes chrome {
  0%    {stroke-dasharray: 0 100; stroke-dashoffset: 25;}
  25%   {stroke-dasharray: 75 25; stroke-dashoffset: 0;}
  50%   {stroke-dasharray: 0 100;  stroke-dashoffset: -125;}
  75%    {stroke-dasharray: 75 25; stroke-dashoffset: -150;}
  100%   {stroke-dasharray: 0 100; stroke-dashoffset: -275;}
}

.chrome2 {
  animation: chrome2 1s linear infinite;
}

@keyframes chrome2 {
  0%    {stroke-dasharray: 0 100; stroke-dashoffset: 25;}
  25%   {stroke-dasharray: 50 50; stroke-dashoffset: 0;}
  50%   {stroke-dasharray: 0 100;  stroke-dashoffset: -50;}
  75%   {stroke-dasharray: 50 50;  stroke-dashoffset: -125;}
  100%  {stroke-dasharray: 0 100;  stroke-dashoffset: -175;}
}

.flower {
  animation: flower 1s linear infinite;
}

@keyframes flower {
  0%    {stroke-dasharray: 0  20; stroke-dashoffset: 25;}
  50%   {stroke-dasharray: 20 0;  stroke-dashoffset: -50;}
  100%  {stroke-dasharray: 0 20;  stroke-dashoffset: -125;}
}

.flower2 {
  animation: flower2 1s linear infinite;
}

@keyframes flower2 {
  0%    {stroke-dasharray: 5 20;  stroke-dashoffset: 25;}
  50%   {stroke-dasharray: 20 5;  stroke-dashoffset: -50;}
  100%  {stroke-dasharray: 5 20;  stroke-dashoffset: -125;}
}

.backstreet_boys {
  animation: backstreet_boys 3s linear infinite;
}

@keyframes backstreet_boys {
  0%    {stroke-dasharray: 5 28.3;  stroke-dashoffset: -225;}
  15%    {stroke-dasharray: 5 28.3;  stroke-dashoffset: -300;}
  30%   {stroke-dasharray: 5 20;  stroke-dashoffset: -300;}
  45%    {stroke-dasharray: 5 20;  stroke-dashoffset: -375;}
  60%   {stroke-dasharray: 5 15;  stroke-dashoffset: -375;}
  75%    {stroke-dasharray: 5 15;  stroke-dashoffset: -450;}
  90%   {stroke-dasharray: 5 15;  stroke-dashoffset: -525;}
  100%   {stroke-dasharray: 5 28.3;  stroke-dashoffset: -925;}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<svg width="600px" height="700px"></svg>

Also available on CodePen: https://codepen.io/anon/pen/PeRazr

1

Here is a full-fledged css solution inspired by Bulma. Just add

    .button {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      position: relative;
      min-width: 200px;
      max-width: 100%;
      min-height: 40px;
      text-align: center;
      cursor: pointer;
    }

    @-webkit-keyframes spinAround {
      from {
        -webkit-transform: rotate(0deg);
        transform: rotate(0deg);
      }
      to {
        -webkit-transform: rotate(359deg);
        transform: rotate(359deg);
      }
    }
    @keyframes spinAround {
      from {
        -webkit-transform: rotate(0deg);
        transform: rotate(0deg);
      }
      to {
        -webkit-transform: rotate(359deg);
        transform: rotate(359deg);
      }
    }

    .button.is-loading {
      text-indent: -9999px;
      box-shadow: none;
      font-size: 1rem;
      height: 2.25em;
      line-height: 1.5;
      vertical-align: top;
      padding-bottom: calc(0.375em - 1px);
      padding-left: 0.75em;
      padding-right: 0.75em;
      padding-top: calc(0.375em - 1px);
      white-space: nowrap;
    }

    .button.is-loading::after  {
      -webkit-animation: spinAround 500ms infinite linear;
      animation: spinAround 500ms infinite linear;
      border: 2px solid #dbdbdb;
      border-radius: 290486px;
      border-right-color: transparent;
      border-top-color: transparent;
      content: "";
      display: block;
      height: 1em;
      position: relative;
      width: 1em;
    }
1

The only thing I found that worked was a post here: https://stackoverflow.com/a/44548729/9488229

I improved it, and now it provides all these features:

  • Disable the button after click
  • Show an animated loading icon using native bootstrap
  • Re-enable the button after the page is done loading
  • Text goes back to original when page loading is done

Javascript:

$(document).ready(function () {
    $('.btn').on('click', function() {
        var e=this;
        setTimeout(function() {
            e.innerHTML='<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> Searching...';
            e.disabled=true;
        },0);
        return true;
    });
});
4
  • Welcome to Stack Overflow. I don't see where you've used the solution Andreas used, and I don't see in the provided code where the button gets re-enabled or restored. Commented Jul 30, 2020 at 1:43
  • You're right it was another answer, I've updated the link. As for how the button gets re-enabled, it works. I think it has to do with the fact it's being disabled inside a setTimeout() function that everything that happens inside that function is undone when the page finishes loading. It's a really clean solution.
    – Colin
    Commented Jul 31, 2020 at 8:38
  • It may be that the re-enable and text restoration is being taken care of by Bootstrap. Try running this code without the Bootstrap JavaScript on a test page. Commented Jul 31, 2020 at 13:51
  • This has worked for me with the Form submit button. Commented Mar 8, 2021 at 10:26
0

You can use the Bootstrap spinner. Use "position: absolute" to make both buttons over each other. With the JavaScript code you can remove the front button and the back button will be displayed.

        button {
            position: absolute;
            top: 50px;
            left: 150px;
            width: 150px;
            font-size: 120%;
            padding: 5px;
            background: #B52519;
            color: #EAEAEA;
            border: none;
            margin: 120px;
            border-radius: 5px;
            display: flex;
            align-content: center;
            justify-content: center;
            transition: all 0.5s;
            height: 40px
        }

        #orderButton:hover {
            color: #c8c8c8;
        }
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">

<button><div class="spinner-border"></div></button>
<button id="orderButton" onclick="this.style.display= 'none';">Order!</button>

0

Not the answer you're looking for? Browse other questions tagged or ask your own question.