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();
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
}
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
}
}
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')
})
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With