Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fire event when images in generated content are loaded

I've got a problem with some Javascript-generated content :

I use the onload() event of my html page to load HTML code from a server and inject it in the page. Since it already is html I use the innerHtml property of the nodes I want to fill with content.

Once I've injected the HTML I call a handmade adjustHeights() function that normalizes the height of the elements I created this way to make them all match the height of the tallest element.

All of this works perfectly unless the code inside innerHtml contains some sort of image or other media that takes time to load (particularly videos) because adjustHeights() is called between the injection of the code and the end of the loading time of the image/video/etc.

Is there any way to wait for every element to be loaded before calling adjustHeights() ? I've already tried the document.onreadystatechange property but the state is already on 'completed' when I start to inject code.

If it's possible I would rather not use time-based calls on adjustHeights().

To make it clearer here's an example :

var mylist = document.getElementById('mycontent');
var li;
for (var i = 0; i < res.length; i++)
{
    li = document.create('li');
    li.innerHTML=res[i];
    mylist.appendChild(li);
}
adjustHeights();
like image 438
Melekus Avatar asked Apr 30 '15 14:04

Melekus


2 Answers

To do this without adding an inline onload attribute to all the images/videos/etc you will have to observe the DOM for changes. Then on every change, you have to fetch all the new media 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.


A cross-browser solution to observe DOM changes 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).


Note: I use images as an example, but this should also work for videos and other media.

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.

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

// Loop through all new media, add the event
var checkNewMedia = function() {

    // extend this by using document.querySelectorAll("img:not([data-loaded), video:not([data-loaded])") etc.
    var media = document.querySelectorAll('img:not([data-loaded]');
    for(var i = 0; i < media.length; i++) {
        addMediaLoadedEvent(media[i]);   
    }
}

// Fire the callback if complete, otherwise bind to onload
var addMediaLoadedEvent = function(element) {
    if (element.complete) {
        onMediaLoaded(element);
    } else {
        element.addEventListener('load', function(event) {
            onMediaLoaded(event.target);   
        });
    }
}

// The callback that is fired once an element is loaded
var onMediaLoaded = function(element) {
    element.setAttribute('data-loaded', 'true');

    adjustHeights(); // <-- your function
}

DEMO: fire event on each image load


If you only want to fire the event once all images are loaded, you could add an extra check that counts how many images are still not loaded:

// The callback that is fired once an image is loaded
var onMediaLoaded = function(element) {
    element.setAttribute('data-loaded', 'true');

    // only fire when there are no media elements without the 'data-loaded' attribute left
    // again, can be extended to use video, etc.
    if(document.querySelectorAll('img:not([data-loaded])').length === 0) {
        adjustHeights(); // <-- your function
    }
}

DEMO: fire event on all images loaded

like image 197
Stephan Muller Avatar answered Oct 19 '22 07:10

Stephan Muller


For image and other sort of media, you should use "onload" event so that you can call adjust height after that. Example:

<img src="someimage.png" onload="loadImage()" >

<script>
   function loadImage() {
     // code to adjust heights
    }
</script>

Here is the standard version:

var img = document.querySelector('img')

function loaded() {
  alert('loaded');
  // do something to adjust height
}

if (img.complete) {
  loaded()
} else {
  img.addEventListener('load', loaded)
  img.addEventListener('error', function() {
      alert('error')
  })
}
like image 33
roxxypoxxy Avatar answered Oct 19 '22 06:10

roxxypoxxy