3

My PM has a requirement that changing the favicon to the animated loading image. it only works in Firefox if I simply point the link.href to a gif file.

I did some researches and find that chrome doesn't support animated favicon. but wiki says chrome 4.0 support animated GIFs.

function changeFavicon() {
  var link = document.createElement('link');
  link.type = 'image/x-icon';
  link.rel = 'shortcut icon';
  link.href = 'http://uploads.neatorama.com/vosa/theme/neatobambino/media/loading.gif';
  document.getElementsByTagName('head')[0].appendChild(link);
}

Above is my code, but favicon only animated in Firefox.

newbie
  • 1,157
  • 2
  • 9
  • 17

4 Answers4

3

This works for Google Chrome:

EXPLANATION: Google Chrome does not support animated Favicons. I've provided this code as an alternative to using a third-party library. In order to avoid continuous server polling, I suggest using Base64 encoded frames.

FYI: You can use an array containing the Base64 encoded icons and cycle through the elements instead of using a switch statement, but I just prefer it broken out this way.

IMPORTANT: Also important to know, the reason for using Base64 encoded images is so that there are not continuous requests made to the server for each frame.

document.head = document.head || document.getElementsByTagName('head')[0];
function changeFavicon(src) {
    var link = document.createElement('link'), oldLink = document.getElementById('dynamic-favicon');
    link.id = 'dynamic-favicon';
    link.rel = 'shortcut icon';
    link.href = src;
    if (oldLink) {
        document.head.removeChild(oldLink);
    }
    document.head.appendChild(link);
    }
function animate(){
    if ( typeof animate.i == 'undefined' ) {
        animate.i = 0;
    }
    switch(animate.i) {
        case 0:
            changeFavicon("[PUT BASE64 for ICO FRAME 1 HERE]");
            break;
        case 1:
            changeFavicon("[PUT BASE64 for ICO FRAME 2 HERE]");
            break;
        case 2:
            changeFavicon("[PUT BASE64 for ICO FRAME 3 HERE]");
            break;
        case 3:
            changeFavicon("[PUT BASE64 for ICO FRAME 4 HERE]");
            break;
        case 4:
            changeFavicon("[PUT BASE64 for ICO FRAME 5 HERE]");
            break;
        case 5:
            changeFavicon("[PUT BASE64 for ICO FRAME 6 HERE]");
            break;
        case 6:
            changeFavicon("[PUT BASE64 for ICO FRAME 7 HERE]");
            break;
        case 7:
            changeFavicon("[PUT BASE64 for ICO FRAME 8 HERE]");
            break;
        case 8:
            changeFavicon("[PUT BASE64 for ICO FRAME 9 HERE]");
            break;
        case 9:
            changeFavicon("[PUT BASE64 for ICO FRAME 10 HERE]");
            break;
        case 10:
            changeFavicon("[PUT BASE64 for ICO FRAME 11 HERE]");
            break;
        case 11:
            changeFavicon("[PUT BASE64 for ICO FRAME 12 HERE]");
            break;
        case 12:
            changeFavicon("[PUT BASE64 for ICO FRAME 13 HERE]");
            break;
        case 13:
            changeFavicon("[PUT BASE64 for ICO FRAME 14 HERE]");
            break;
        case 14:
            changeFavicon("[PUT BASE64 for ICO FRAME 15 HERE]");
            break;
        case 15:
            changeFavicon("[PUT BASE64 for ICO FRAME 16 HERE]");
            break;
    }
    animate.i = animate.i + 1;
    if(animate.i == 16){
        animate.i = 0;
    }
}
setInterval(animate,250);
Robert Talada
  • 317
  • 1
  • 10
  • Works perfectly, thank you! If anyone is looking for an online ico-to-base64-converter: I did it with http://www.askapache.com/online-tools/base64-image-converter/ – Bernhard May 28 '17 at 01:26
  • Why not use an array rather than a large case statement? – Maximillian Laumeister Sep 18 '18 at 20:06
  • @maximillian-laumeister Like I stated in my answer: You can use an array containing the Base64 encoded icons and cycle through the elements instead of using a switch statement, but I just prefer it broken out this way. – Robert Talada Sep 24 '18 at 14:19
  • @RobertTalada Sorry for missing that in your answer. I just mentioned it because using a switch statement instead of an array turns one cycle of animation into an `O(n^2)` operation instead of just `O(n)`. Not that it would really matter unless you had a whole ton of frames. – Maximillian Laumeister Sep 24 '18 at 14:55
  • @maximillian-laumeister Is that truly the case? I don't believe the complexity is exponential because there is no reasonable scenario where I would nest selects. Wouldn't it be O(2n)? I believe this growth is linear but I'm no computer scientist, nor am I well versed in Big-O notation. – Robert Talada Sep 26 '18 at 17:09
  • @RobertTalada Close but that's not quite how Big-O notation works. As an example let's take just a single switch evaluation (for a single animation frame). As-written it would be `O(n)` because for that one frame the interpreter needs to go one by one down the whole frame list to find the frame it's looking for, compared to an array lookup which would be `O(1)`. When you consider a full animation cycle, that's `n` frames, so you are doing `n` times the work of one frame, that's where I got the `O(n^2)` for the switch construct compared to `O(n)` for the array construct. – Maximillian Laumeister Sep 26 '18 at 22:41
  • @RobertTalada Now in practice the distinction between the run times for this application is meaningless unless you're using a very poorly optimized JavaScript runtime plus your animation has many many frames, neither of which are likely to be true. Many compilers (probably including many JS compiler-interpreters actually) [optimize switch statements into jump tables anyways](https://stackoverflow.com/questions/4442835/what-is-the-runtime-complexity-of-a-switch-statement), so in practice the switch construct may be optimized into a compiled structure with `O(1)` lookup time anyways. – Maximillian Laumeister Sep 26 '18 at 22:41
  • @RobertTalada Here's the takeaway I am hoping to impart: It's theoretically better to use an array structure and not a switch structure here. In practice either the compiler-interpreter will optimize it into an equivalent `O(1)` structure, or if not, the miniscule time saved will never make any difference in this specific application. But I think it's important to understand why the array structure is better, in order to make smart decisions in other code where one may be handling many more elements and using a compiler-interpreter that does not optimize. – Maximillian Laumeister Sep 26 '18 at 22:41
  • 1
    @maximillian-laumeister Thanks for the detailed response and explanation. While not understanding the notation necessarily, I've always understood the complexity of a Switch to be O(n). I believe I thought the if statement was O(n) as well which it is not and I thus somehow arrived at O(2n). In any case, I've always known that it was less efficient, I probably chose to write it this way at the time only because it was visually pleasing to me.I copied this code directly from a website I had at the time, only removing the Base64. – Robert Talada Oct 02 '18 at 19:59
  • @maximillian-laumeister Look no futher than at the source for https://robtalada.com/apps/entity-editor8 and you'll see that I don't approach writing javascript with convention/standards or sanity. I sometimes write code in a certain way, just because I prefer the appearance or any other arbitrary reason. As long at it works to my satisfaction, that's good enough for my purposes. – Robert Talada Oct 02 '18 at 20:12
  • @RobertTalada To be clear I'm not attacking your coding style, I just wanted to add some more context to augment this answer and clarify my reasoning to any future SO readers. – Maximillian Laumeister Oct 02 '18 at 22:17
2

I used another favicon.js. and I used 8 static icons to simulate a loading animation icon.

    favicon.animate([
                 "img/loading1.ico", "img/loading2.ico",
                 "img/loading3.ico", "img/loading4.ico",
                 "img/loading5.ico", "img/loading6.ico",
                 "img/loading7.ico", "img/loading8.ico"
               ], 50);
newbie
  • 1,157
  • 2
  • 9
  • 17
1

You may want to give a try to favico.js. It let you use a video as the favicon. Okay, this is a bit heavy for a simple GIF but you get the idea. Plus you can probably get the interesting (and multiplatform) part by looking at the code.

philippe_b
  • 38,730
  • 7
  • 57
  • 59
0

Problem is, there's an existing favicon which you need to remove first.

function removeFavicon() {
  var myHead = document.getElementsByTagName("head")[0];
  var lnks = myHead.getElementsByTagName('link');
  var len = lnks.length;
  for (var i = 0; i < len; ++i) {

    var l = lnks[i];
    if (l.type == "image/x-icon" || l.rel == "shortcut icon") {
      myHead.removeChild(l);
      return; // Returned assuming only one favicon link tag
    }

  }
}


function changeFavicon() {
  var link = document.createElement('link');
  link.type = 'image/x-icon';
  link.rel = 'shortcut icon';
  link.href = 'http://uploads.neatorama.com/vosa/theme/neatobambino/media/loading.gif';
  removeFavicon(); // remove existing favicon
  document.getElementsByTagName('head')[0].appendChild(link);

}

changeFavicon();
Robin Carlo Catacutan
  • 13,249
  • 11
  • 52
  • 85