Search code examples
javascriptdompromiseecmascript-5

Synchronously manipulate DOM?


I'm currently developing for a custom browser, which pretty much runs plain ES5 with Promises and no support for non-native code at all. This browser was made to simulate the comportment of an specific hardware whose processing isn't anything close to great. Also, I have very short application size limits.

With this in mind and the task of showing different views within the same window, one at a time and only once each time the application is used, I've come out with the idea of reloading only an iframe instead of the whole page.

To do this, I wrote the next code:

function loadFrame(page){
    return new Promise(function(resolve, reject){
        var iframe = document.createElement('iframe');
        iframe.classList.add('hidden');
        iframe.src = page;

        resolve(iframe);
    });
}
function appendTo(elem, parentId){
    return new Promise(function(resolve, reject){
        var parentNode = document.getElementById(parentId);
        parentNode.appendChild(elem);

        resolve(elem);
    });
}
function robOf(elem, id){
    return new Promise(function(resolve, reject){
        var parent = elem.parentNode;
        var doc = elem.contentDocument; // body hasn't loaded so...
        var toImport = doc.getElementById(id); // ... this returns undefined...
        var imported = parent.importNode(toImport, true); //... thus breaking it here
        parent.removeChild(elem);

        resolve(imported);
    });
}

loadFrame('example/myPage.htm')
.then(function(elem){ return appendTo(elem, 'myParentId') })
.then(function(parent){ return robOf(parent, 'myElemId') })
.then(doSomething);

The idea behind it is pretty simple:

  • create an iframe;
  • load it with a local file;
  • look the iframe's document for a given element;
  • import that given element to my page's document;
  • remove the iframe.

However, it bricks. Sometimes the iframe just hasn't loaded in time, so when I define doc it's body is empty and I pass undefined on .importNode(), blowing up the whole stuff. Thinking that it should be an asynchronous execution problem, I wrapped the function in Promises and end up with the code like it is but the problem stills. I would like to know exactly what I'm getting wrong about Promises, why the code isn't working and how can I correct it, preferably without the use of setTimeout().


Solution

  • You can listen for the load event on the <iframe> to ensure the iframe is never undefined.

    function loadFrame(page){
        return new Promise(function(resolve, reject){
            var iframe = document.createElement('iframe');
            iframe.addEventListener('load', function(e) {
                resolve(this);
            });
            iframe.classList.add('hidden');
            iframe.src = page;
        });
    }
    

    Also, calling .bind on your functions may also be cause to some of your issues.

    Try instead .then(function(x) { return functionCall(x,p2,p3) });