0

I am tying to dynamically append divs to a DOM element using pure javascript. When I am using it without the setTimeout function it works properly, but when I am using setTimeout function the document fragment isn't appended (I can see it exists if I console.log it but I am obviously doing something wrong).

I have to use a setTimeout since the array that I am about to loop through consists of 1-5000 items and I don't want to lock the entire gui while rendering those.

<html>
    <body>
        <head>
        </head>
        <div id="outputContainerFlowchart"></div>
        <script>
            console.log(document.getElementById("outputContainerFlowchart"));
            let dummyArr = ["D", "u", "M"];
            let cont = document.getElementById("outputContainerFlowchart");
            let frag = document.createDocumentFragment();
            for (const letter of Object.values(dummyArr)) {
                setTimeout(function() {
                        var printedNodeTest = document.createElement("div");
                        printedNodeTest.className = "printedNode";
                        printedNodeTest.innerHTML = "Testnode";
                        frag.appendChild(printedNodeTest);
                }, 0);
            }
            cont.appendChild(frag);
        </script>
    </body>
</html>

Edit:

I realized that I'd forgot that setTimeout "makes it async" (which James also noticed, thanks!) - no wonder it didn't work. Wrapped it inside a Promise instead and this is the result:

<html>
    <body>
        <head>
        </head>
        <div id="outputContainerFlowchart"></div>
        <script>
            console.log("initializing");
            console.log(document.getElementById("outputContainerFlowchart"));
            let dummyArr = ["D", "u", "M"];
            let cont = document.getElementById("outputContainerFlowchart");
            let renderFunc = function(dummyArr) {
                return new Promise(function(resolve, reject) {
                    let frag = document.createDocumentFragment();
                    for (const letter of Object.values(dummyArr)) {
                        console.log("current letter inside for: "+letter);
                            console.log("inside timeout");
                            var printedNodeTest = document.createElement("div");
                            printedNodeTest.className = "printedNode";
                            printedNodeTest.innerHTML = "Testnode";
                            frag.appendChild(printedNodeTest);
                    }
                    resolve(frag);
                });
            }
            renderFunc(dummyArr).then(function(frag) {
                console.log("appending frag");
                cont.appendChild(frag);
            });        
        </script>
    </body>
</html>
Eken
  • 103
  • 1
  • 1
  • 10
  • 1
    Well it's because the code following the timeouts has already executed once the timeouts complete. When you use setTimeout, the code inside gets taken out of the ordinary execution flow and happens "later". So, you create a bunch of timeouts, cont.append frag, and then later execute those timeouts. – James May 14 '19 at 19:46
  • Thanks James! I realized it myself just a while ago, been staring into it for a few hours, don't really know what I was thinking... I wrapped it inside a Promise now inside, will it automatically be running asynchronously if I do it like that? Calling it with functionName(bla, bla).then(function(result){console.log(result)}); Or do I have to put the "wrapper" function as an async one? – Eken May 14 '19 at 19:51

0 Answers0