Steve
Steve

Reputation: 2148

Event to determine when innerHTML has loaded

Is it possible to determine when setting an innerHTML has loaded? I'm not sure if it's a synchronous operation. I assume the 'building the DOM' is synchronous, but loading tags, contents, etc isn't.

So in a nutshell - is there a way to get an event when innerHTML has completed loading?

Thanks!

Upvotes: 13

Views: 15670

Answers (8)

David Spector
David Spector

Reputation: 1671

If your problem is waiting until innerHTML on an element has been completely rendered and painted, for example to measure the element's final size, use this code inside your top-level or async function:

await YieldForFrame();
await YieldForFrame();

where the following code is part of your program:

function YieldForFrame()
    {
    // Create Promise for use with 'await'
    return new Promise(function(resolve, reject)
        {
        requestAnimationFrame(resolve);
        });
    } // YieldForFrame

Upvotes: 0

caiohamamura
caiohamamura

Reputation: 2718

It looks like @zahanm answer won't work if you replace the entire document HTML, which was what I needed. So I had to draw back to setInterval. Based on @shawndumas answer I've created a more modern approach using Promise:

function waitUntilSelectorExist(selector, interval = 100, timeout = 20000) {
    let intervalId;
    let elapsed = 0;

    let promise = new Promise(function(resolve, reject) {
        intervalId = setInterval(() => {
            let element = document.querySelector(selector);
            if (element !== null) {
                clearInterval(intervalId);
                resolve(element);
            }
            elapsed += interval;
            if (elapsed > timeout) {
                clearInterval(intervalId);
                reject(`The selector ${selector} did not enter within the ${timeout}ms frame!`);
            }
        }, interval);
    });

    return promise;
}

Also I use querySelector instead, so you can search for whatever selector you like, either .class, #id, etc.

If the function ever find the querySelector within the document the Promise will resolve, so you can use it with the new await keyword.

(async() => {
    try {
        await waitUntilSelectorExist("#test");
        console.log("Do stuff!");
    } catch (error) {
        console.warn(error); // timeout!
    }
})();

Of course you could also wrap around MutationObserver within a Promise if you are really just changing some elements within the document:

function waitUntilSelectorExist(selector) {
    let MutationObserver = window.MutationObserver || window.WebKitMutationObserver;

    let promise = new Promise(function(resolve) {
        let observer;
        observer = new MutationObserver(function(mutations) {
            let element = document.querySelector(selector);
            if (element !== null) {
                observer.disconnect();
                resolve();
            }
        });
        observer.observe(document.body, { childList: true, subtree: true });
    });
    
    return promise;
}

Upvotes: 2

Melo Waste
Melo Waste

Reputation: 161

since img elements added through innerHTML do trigger their onload event you could add a tiny image at the end of your original code inserted in innerHTML, and in their onload event you could call a function. Also you can delete the image in there.

<img src='data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7' onload='hasLoadedFunc();this.parentNode.removeChild(this);'>

JSFiddle sample

src='data:image' code is useful here since no specific image is needed.

I found this solution in another answer, I just can't find it. I will edit this solution once I find the reference.

Upvotes: 4

Igor Kustov
Igor Kustov

Reputation: 3276

Have you tried to use the window.onload event? This event fires when the entire page is completely loaded including all its content like images, scripts, inner frames, etc.

P.S. If you use JQuery library, then you can use their $(window).load event wrapper.

Upvotes: -2

Kai Mysliwiec
Kai Mysliwiec

Reputation: 354

How about creating a container div and add an onload event handler to it.

var container = document.createElement('div');
container.addEventListener('load', function() {
    // Do something here
}, false);
container.innerHTML = '<h1>Here comes my HTML</h1>';
document.getElementById('someId').appendChild(container);

Upvotes: -2

zahanm
zahanm

Reputation: 993

You need to use DOM Mutation Observers, which currently only work in Chrome and Firefox. They replace the performance-killing DOM Mutation Events.

Example code, from HTML5Rocks:

var insertedNodes = [];
var MutationObserver = window.MutationObserver || window.WebKitMutationObserver;

var observer = new MutationObserver(function(mutations) {
 mutations.forEach(function(mutation) {
   for (var i = 0; i < mutation.addedNodes.length; i++)
     insertedNodes.push(mutation.addedNodes[i]);
 })
});

observer.observe(document, { childList: true });
console.log(insertedNodes);

Here is the DOM4 spec. Please, don't use the setTimeout() hacks. :)

Upvotes: 22

shawndumas
shawndumas

Reputation: 1413

 var waitUntil = function (fn, condition, interval) {
    interval = interval || 100;

    var shell = function () {
            var timer = setInterval(
                function () {
                    var check;

                    try { check = !!(condition()); } catch (e) { check = false; }

                    if (check) {
                        clearInterval(timer);
                        delete timer;
                        fn();
                    }
                },
                interval
            );
        };

    return shell;
};

Use it like this:

waitUntil(
  function () {
    // the code you want to run here...
  },
  function() {
    // the code that tests here... (return true if test passes; false otherwise)
    return !!(document.getElementById('<id of the div you want to update>').innerHTML !== '');
  },
  50 // amout to wait between checks
)();

Upvotes: 1

THE DOCTOR
THE DOCTOR

Reputation: 4555

Yes, you can do something like this:

window.document.getElementById("Testing").innerHTML = 
"<img src='test.jpg' width='200' height='400'>";
alert('test');

The javascript will be executed sequentially and therefore the alert will go off once your innerHTML has been loaded.

Also, take a look at the Javascript Execution Model

Upvotes: -2

Related Questions