Search code examples
javascriptxmlhttprequest

Can't center an element generated from XMLHttpRequest


I am building a fairly complex web app. The main page loads and then all menu items (300+), form submissions, ect. are loaded via XMLHttpRequest. I have a basic "panel" template that allows the the panel to look and act (drag, resize, ect.) like a child window of the app. I load all XMLHttpRequest requested pages into the the content section of the "panel" template.

The problem I am running into is that if I try to center the new "panel" it does not seem to find the new "panels" size. My code is setup so that when a menu item is clicked it runs a function that calls the XMLHttpRequest function, the originating function passes to the XMLHttpRequest a callback function. The callback function then clones the panel template, so I can change several element attributes, I then append the response to the cloned "panel" template, in a document fragment. And then all that is appended to the displayed HTML, after which I find the new "panels" size and try to center it but it always fails.

As each function has a lot more going on than just what I spelled out above what follows is hopefully an accurate striped down version of the relevant parts of the code.

The XMLHttpRequest function in nothing unusual, and once it has a successful response the callback will run the "OpenPanel" function (see below).

Callback function:

function OpenPanel(e, response)
{
    var rescontent = response.querySelector('.content');
    var newid = rescontent.getAttribute('data-id');
    var titlebar = rescontent.getAttribute('data-title');

    var frag = document.createDocumentFragment();
    var clonepanel = doc.getElementById('paneltemplate').cloneNode(true);
    clonepanel.id = newid;
    frag.appendChild(clonepanel);

    frag.querySelector('.titlebar').innerHTML = titlebar;
    var replacelem = frag.querySelector('.content');
    replacelem.parentNode.replaceChild(rescontent, replacelem);

    doc.getElementById('mainbody').appendChild(frag);

    var newpanel = document.getElementById(newid);
    newpanel.addEventListener('mousedown', PanelSelect, true);

    newpanel.style.cssText = PanelPosition(newpanel);
}

PanelPosition function:

function PanelPosition(panel)
{
    var lh = panel.clientHeight;
    var lw = panel.clientWidth;
    var wh = panel.parentNode.clientHeight;
    var ww = panel.parentNode.clientWidth;

    var paneltoppos = (wh - lh) / 2;
    var panelleftpos = (ww - lw) / 2;

    return 'top: ' + paneltoppos + 'px; left: ' + panelleftpos + 'px;';
}

I tried using setTimeout with a delay of 1ms, but that causes the panel to flash on the screen, in the wrong position, before its moved. Which from my perspective makes the app feel cheap or like only a second best effort was given. And even if it didn't flash setTimeout seems like a hack more than a solution.

I have tried this code with a few different "pages" (xhr requests) and I almost get the sense that the XMLHttpRequest hasn't finished loading when the callback function is ran (which I doubt is possible). For example, I put

console.log('top: '+wh+' - '+lh+'(wh - lh) left: '+ww+' - '+lw+'(ww - lw)');

in the "PanelPosition" function, and without the setTimeout the panel height (lh) and width (lw) are between 100 and 200 pixels. But with setTimeout the panels usually are over 500 pixels in height and width. And of course that severely effects where centered is.

I have tried several searches over the last few days but nothing has turned up. So if there is a good post or article describing the problem and the solution, feel free point me to it.

Should note that as I am running the web app exclusively in node-webkit/nw.js (chromium/webkit browser) there is no need for a cross-browser solution.


Solution

  • Well I guess I am going to answer my own question.

    While looking for something completely unrelated I found this this SO post. The accepted answer gives a clue that explains the issue.

    Unfortunately, it seems that you have to hand the controls back to the browser (using setTimeout() as you did) before the final dimensions can be observed; luckily, the timeout can be very short.

    Basically javascript does not draw the appended element till the end of the function call. So setTimeout is one solution to my problem. But, there is a second solution. If I just have to wait till the end of the function then lets make that one heck of a small (focused) function. So I just moved all the code need to create the appended "panel" to a totally separate function. And in so doing I solved another pending issue.

    Edit:

    Or not. Apparently a separate function doesn't work now, but it did before I posted. Who knows maybe I didn't save the change before reloading the page.