Proper way to reset a GIF animation with display:none on Chrome

Title is self-explanatory, but I'll provide a step-by-step view on the matter. Hopefully I'm not the first one to have noticed this (apparently) bug on Webkit/Chrome.

I want to reset a GIF animation. All of the examples I've seen so far either simply set the src of the image to itself or set it to an empty string followed by the original src again.

Take a look at this JSFiddle for reference. The GIF resets perfectly fine on IE, Firefox and Chrome.

The issue which I have is when the image has display:none on Google Chrome only.

Check this JSFiddle. The GIF resets fine on IE and Firefox before being displayed in the page, but Chrome simply refuses to reset its animation!

What I've tried so far:

  • Setting the src to itself as in Fiddle, doesn't work in Chrome.
  • Setting the src to an empty string and restoring it to the default, doesn't work either.
  • Putting an wrapper around the image, emptying the container through .html('') and putting the image back inside of it, doesn't work either.
  • Changing the display of the image through .show() or .fadeIn() right before setting the src doesn't work either.

The only workaround which I've found so far is keeping the image with its default display and manipulating it through .animate()ing and .css()ing the opacity, height and visibility when necessary to simulate a display:none behaviour.

The main reason (context) of this question is that I wanted to reset an ajax loader GIF right before fading it in the page.

So my question is, is there a proper way to reset a GIF image's animation (which avoids Chrome's display:none "bug") or is it actually a bug?

(ps. You may change the GIF in the fiddles for a more appropriate/longer animation gif for testing)

Answers:

Answer

Chrome deals with style changes differently than other browsers.

In Chrome, when you call .show() with no argument, the element is not actually shown immediately right where you call it. Instead, Chrome queues the application of the new style for execution after evaluating the current chunk of JavaScript; whereas other browsers would apply the new style change immediately. .attr(), however, does not get queued. So you are effectively trying to set the src when the element is still not visible according to Chrome, and Chrome won't do anything about it when the original src and new src are the same.

Instead, what you need to do is to make sure jQuery sets the src after display:block is applied. You can make use of setTimeout to achieve this effect:

var src = 'http://i.imgur.com/JfkmXjG.gif';
$(document).ready(function(){
    var $img = $('img');
    $('#target').toggle(
        function(){
            var timeout = 0; // no delay
            $img.show();
            setTimeout(function() {
                $img.attr('src', src);
            }, timeout);
        },
        function(){
            $img.hide();
        }
    );
});

This ensures that src is set after display:block has been applied to the element.

The reason this works is because setTimeout queues the function for execution later (however long later is), so the function is no longer considered to be part of the current "chunk" of JavaScript, and it provides a gap for Chrome to render and apply the display:block first, thus making the element visible before its src attribute is set.

Demo: http://jsfiddle.net/F8Q44/19/

Thanks to shoky in #jquery of freenode IRC for providing a simpler answer.


Alternatively, you can force a redraw to flush the batched style changes. This can be done, for example, by accessing the element's offsetHeight property:

$('img').show().each(function() {
    this.offsetHeight;
}).prop('src', 'image src');

Demo: http://jsfiddle.net/F8Q44/266/

Answer

The most reliable way to "reset" a GIF is by appending a random query string. However this does mean that the GIF will be redownloaded every time so make sure it's a small file.

// reset a gif:
img.src = img.src.replace(/\?.*$/,"")+"?x="+Math.random();
Answer

This solution preloads the gif and takes it out of the dom and then back in the src (thus avoiding another download)

I just tested it using jquery to remove the attribute and it works fine.

Example:

<html>
<head>

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js" type="text/javascript"></script>

<script>

$(function() {

    $('.reset').click(resetGif);

    function resetGif() 
    {
        $('.img1').removeAttr('src', '');
    }
});

</script>

</head>

<body>
    <img class="img1" src="1.gif" />
    <a href="#" class="reset">reset gif</a>
</body>
</html>
Answer

Just because I still need this every now and then I figured the pure JS function I use might be helpful for someone else. This is a pure JS way of restarting an animated gif, without reloading it. You can call this from a link and/or document load event.

<img id="img3" src="../_Images/animated.gif">

<a onClick="resetGif('img3')">reset gif3</a>

<script type="text/javascript">

// reset an animated gif to start at first image without reloading it from server.
// Note: if you have the same image on the page more than ones, they all reset.
function resetGif(id) {
    var img = document.getElementById(id);
    var imageUrl = img.src;
    img.src = "";
    img.src = imageUrl;
};

</script>

On some browsers you only need to reset the img.src to itself and it works fine. On IE you need to clear it before resetting it. This resetGif() picks the image name from the image id. This is handy in case you ever change the actual image link for a given id because you do not have to remember to change the resetGiF() calls.

--Nico

Answer

here's my hack for background images:

$(document).on('mouseenter', '.logo', function() {
  window.logo = (window.logocount || 0) + 1;
  var img = new Image();
  var url = "/img/mylogimagename.gif?v=" + window.logocount;
var that = this;
  $(img).load(function(){

     $(that ).css('background-image','url(' + url + ')');
  });
  img.src = url;
});
Answer

This seemed to work for me in Chrome, it runs each time just before I fade in the image and clears then refills the src and my animation now starts from the beginning every time.

var imgsrc = $('#my_image').attr('src');
$('#my_image').attr('src', '');
$('#my_image').attr('src', imgsrc);
Answer

I've a button with the an animated no-loop image in it. I just reload the image with some jquery and this seems to be working for me.

var asdf = $(".settings-button img").attr("src");
$(".settings-button img").attr("src", "").attr("src", asdf);
Answer

I came across this thread after searching many others. David Bell's post led me to the solution I needed.

I thought I'd post my experience in the event that it could be useful for anyone trying to accomplish what I was after. This is for an HTML5/JavaScript/jQuery web app that will be an iPhone app via PhoneGap. Testing in Chrome.

The Goal:

  1. When user taps/clicks button A, an animated gif appears and plays.
  2. When user taps/clicks button B, gif disappears.
  3. When user taps/clicks button A again, after tapping/clicking button B, animated gif should reappear and play from the beginning.

The Problem:

  • On tap/click of button A, I was appending the gif to an existing div. It would play fine.
  • Then, on tap/click of button B, I was hiding the container div, then setting the img src of the gif to an empty string (''). Again, no problem (that is, the problem wasn't evident yet.)
  • Then, on tap/click of button A, after tap/click of button B, I was re-adding the path to the gif as the src.

    - This did not work. The gif would show up on subsequent taps/clicks of button A...however, the more I tapped/clicked button A, the more times the gif would load and start over. That is, if I went back and forth, tapping/clicking button A then button B 3 times, the gif would appear and then start/stop/start 3 times...and my whole app started to chug. I guess the gif was being loaded multiple times, even though I had set the src to an empty string when button B was tapped/clicked.

The Solution:

After looking at David Bell's post, I arrived at my answer.

  1. I defined a global variable (let's call it myVar) that held the container div and the image (with the source path) within.
  2. On the tap/click function of button A, I appended that container div to an existing parent div in the dom.
  3. In that function, I created a new variable that holds the src path of the gif.

Just like David suggested, I did this (plus an append):

$('#mainParent').append(myVar);
var imgsrc = $('#my_image').attr('src');
$('#my_image').attr('src', '');
$('#my_image').attr('src', imgsrc);

THEN, in the function for button B, I set the src to an empty string and then removed the div containing the gif:

$('#my_image').attr('src', '');
$('#mainParent').find('#my_image').remove();

Now, I can tap/click button A then button B then button A, etc., all day long. The gif loads and plays on tap/click of button A, then hides on tap/click of button B, then loads and plays from the beginning on subsequent taps of button A every time with no issues.

Answer

I worked out a complete solution for this problem. It can be found here: https://stackoverflow.com/a/31093916/1520422

My solution restarts the animation WITHOUT re-loading the image data from the network.

It also enforces the image to repaint to fix some painting artefacts that occured (in chrome).

Answer

I experienced problems with all of the above solutions. What finally worked was replacing the src temporarily with a transparent 1px gif:

var transparent1PxGif = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
var reloadGif = function(img) {
    var src = img.src;
    img.src = transparent1PxGif;
    img.offsetHeight; // triggers browser redraw
    img.src = src;
};
Answer

It's been several years and I've decided to revisit this since we have a number of new options at our disposal.

The issue with my previous answer is that it forces a re-download of the GIF every single time you want to re-start it. While that's fine for small files, it's still an overhead that's best avoided if possible.

With that in mind, I've got a new solution that uses AJAX to download the GIF once, and then converts it into a data URL (via a FileReader) and uses that as the source with a random query string attached.

This way, the browser only ever downloads the image once, and can even cache it properly, and the "reset" pulls from that pre-downloaded resource.

The only catch, of course, is that you have to make sure it's properly loaded before you can use it.

Demo: http://adamhaskell.net/misc/numbers/numbers.html

Relevant code:

var url = "something.gif"; // fallback until the FileReader is done
function setup() {
    var xhr = new XMLHttpRequest();
    xhr.open("GET",url,true);
    xhr.responseType = "blob";
    xhr.onreadystatechange = function() {
        if( this.readyState == 4) {
            var fr = new FileReader();
            fr.onload = function() {
                url = this.result; // overwrite URL with the data one
            };
            fr.readAsDataURL(this.response);
        }
    };
    xhr.send();
}
function getGIF() {
    return url+"?x="+Math.random();
}

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.