Search code examples
javascriptappendchildchoropleth

JavaScript appended child (div) only shows up after ALL code has been executed


Long story short: got an upload file element and a button with an onclick function called "start". So, all of these happens way after all DOM content is loaded.

createLoader: function(){
    var outerDiv = document.createElement('div');
    var innerDiv = document.createElement('div');
    innerDiv.className = '_gisplayloader';

    var mapDiv = this.getContainer();

    /*outerDiv.style = ' opacity: 0.5; background-color: grey; justify-content: center; display: flex;';
    outerDiv.style.position = 'absolute';
    outerDiv.style.zIndex = '999999999';*/
    outerDiv.className = '_gisplayLoaderOuterDiv';
    outerDiv.style.height = mapDiv.offsetHeight;
    outerDiv.style.width = mapDiv.offsetWidth;
    outerDiv.appendChild(innerDiv);
    this.loaderDiv = outerDiv;

    mapDiv.parentElement.insertBefore(outerDiv, mapDiv);
}

This is the loader/spinner create and append code. It works instantly if I call it through the browser console.

Inside start(), it reads the uploaded file and onloadend calls another function that calls createLoader().

function start(){
    //var data = new Array();
    var time = Date.now();

    console.log("starting...");

    var reader = new FileReader();
    reader.onloadend = function(){
        var data = JSON.parse(reader.result);
        var datareadtimestamp = Date.now();
        makeChoropleth(map, data ,options,null);
    }
    reader.readAsText(document.getElementById("file").files[0]);    
}

The simplified version of makeChoropleth function:

makeChoropleth: function(bgmap, geometry, options,defaultid){

    var gismap = new Choropleth(bgmap, geometry, options); //inside here it calls createLoader()

    //the next 3 functions take about 5-10s to execute all together
    gismap.processData(geometry);
    gismap.draw();
    gismap.buildLegend();

    if(options.loader != false){
        //  gismap.loader(); that would hide the loader. disabled it so i could check if the loader was appearing at all
    }
}

Unless I put a breakpoint somewhere inside makeChoropleth, the loader only shows up upon all code completion. The following code takes almost 10 seconds to finish, which is more than enough to create the loader (assuming it is asynchronous). Why does that happen? How could one fix it?


Solution

  • if you want the loader to show up before the file finishes reading you need to call it before the onloadend event, in the start() function.

    function start(){
        //var data = new Array();
        var time = Date.now();
    
        console.log("starting...");
    
        var reader = new FileReader();
        reader.onloadend = function(){
            //stuff here only runs after the file is read completely!
            var data = JSON.parse(reader.result);
            var datareadtimestamp = Date.now();
            makeChoropleth(map, data ,options,null);
        }
        reader.readAsText(document.getElementById("file").files[0]);
        createLoader();    
    }
    

    if you want the loader to show up after the file read finishes but before your Choropleth code runs, the easiest way is to put a 1ms timeout on your 5-10s operation to give the browser time to do a reflow. (otherwise the blocking code will run first). Try:

    makeChoropleth: function(bgmap, geometry, options,defaultid){
    
        var gismap = new Choropleth(bgmap, geometry, options); //inside here it calls createLoader()
    
        //the next 3 functions take about 5-10s to execute all together
        setTimeout(function(){
            gismap.processData(geometry);
            gismap.draw();
            gismap.buildLegend();
        },1);
    
        if(options.loader != false){
            //  gismap.loader(); that would hide the loader. disabled it so i could check if the loader was appearing at all
        }
    }