3

I have tabs logic that load html templates inside a wrapper. That's works fine, but I included an animation that animate height of the tab wrapper when tab is switched.

The problem is the following: When a template contains <img src="/some-image.png"> the $('#tab-content').load('template-url', function() {...}) callback function sometimes is executed before the browser show the images. And my animation is not working correctly.

Code example (jsFiddle):

var currentHeight = $contentHolder.height();

$contentHolder.load(path, function() {
    $contentHolder.stop();

    function animateHeight() {
        var loadedContentHeight = $contentHolder.css('height', 'auto').height();
        $contentHolder.height(currentHeight);
        $contentHolder.animate({
            height: loadedContentHeight
        }, 800, 'linear');
    }
    animateHeight();
});

I tried to set little timeout, but it's not working every time. If I set more that 300ms timeout, It feels like tabs are changed too slow.

I tried to execute the animation when $('img').load(function() {}) is fired, but with no luck.

This bug occurs most often when the web page is fully refreshed and each tab content loading for first time.

Stephan Muller
  • 27,018
  • 16
  • 85
  • 126
Ifch0o1
  • 900
  • 1
  • 14
  • 32

2 Answers2

1

You can call your animateHeight function as each image in the loaded HTML is in turn loaded. You can expand this selection if you have other objects like videos.

// Call animateHeight as each image loads
var items = $('img', $contentHolder);
items.bind('load', function(){ 
    animateHeight();
});

Updated demo: http://jsfiddle.net/jxxrhvvz/1/

Drakes
  • 23,254
  • 3
  • 51
  • 94
  • Thanks for your answer, I will use your code to solve my problem. I accepted the another answer because is more related with the topic **heading** and provide more explanations. For my little project I need something simple. – Ifch0o1 May 02 '15 at 16:12
1

The image load event is kind of broken. To know when images are loaded you will have to observe the DOM for changes. Then on every change, you have to fetch all the new images and add the onload event to them from the callback. To prevent checking each element every time, once they've been loaded you could mark them as such by adding a data-loaded="true" property for instance.


One way to listen to DOM changes is the MutationObserver event. This is supported by all modern browsers and IE11.

A better supported solution (IE9 and up) can be found in this answer: Detect changes in the DOM. I will not repeat it here (but it's included in the demo below).


On every DOM change first you check for images without the data-loaded attribute that are already loaded anyway (this could happen when an image was still in the browser's cache) by checking element.complete. If so, fire the callback function and add the attribute to it.

If .complete is not the case, add an onload event to them that also fires the callback once it is loaded.

In your case you only want to fire your callback when all images are loaded, so I added a check if there's still images without the data-loaded attribute. If you remove that if-clause your callback would run after each image is loaded.

// Observe the DOM for changes
observeDOM(document.body, function(){ 
    checkNewImages();
});

var checkNewImages = function() {
    var images = $('img:not([data-loaded]').each(function() {
        addImageLoadedEvent( this );
    });
}

var addImageLoadedEvent = function(img) {
    if (img.complete) {
        onImageLoaded(img);
    } else {
        $(img).on('load', function() {
            onImageLoaded(this);   
        });
    }
}

// The callback that is fired once an element is loaded
var onImagesLoaded = function(img) {
    $(img).attr('data-loaded', 'true');

    if($('img:not([data-loaded])').length === 0) {

        // YourCallbackHere();

    }
}

DEMO: fire event on all images loaded

Community
  • 1
  • 1
Stephan Muller
  • 27,018
  • 16
  • 85
  • 126
  • For completeness, there also is a jQuery plugin that does something similar: [waitForImages](https://github.com/alexanderdickson/waitForImages) – Stephan Muller May 02 '15 at 16:11