0

I am looping over a response that I got from an ajax response, the response is an array containing 1000 objects , this response is used to created an html table with 1000 rows :

1st scenario :

    for (var i in msg.myObjects) {
    $('#mytablebody').append('<tr><td>' + msg.myObjects['item1'] + '</td><td>' + 
msg.myObjects['item2'] + '</td><td>' + msg.myObjects['item3'] + '</td><td>' + 
msg.myObjects['item4'] + '</td><td>' + msg.myObjects['item5'] + '</td><td>' + 
msg.myObjects['item6'] + '</td><td>' + msg.myObjects['item7'] + '</td> .... </tr>');
    }

Result => memory leak my RAM went to 2Go and my browser crashed

2nd scenario :

    for (var i in msg.myObjects) {
        document.getElementById('mytablebody').innerHTML = document.getElementById('mytablebody').innerHTML + '<tr><td>' + 
msg.myObjects['item1'] + '</td><td>' + 
    msg.myObjects['item2'] + '</td><td>' + msg.myObjects['item3'] + '</td><td>' + 
    msg.myObjects['item4'] + '</td><td>' + msg.myObjects['item5'] + '</td><td>' + 
    msg.myObjects['item6'] + '</td><td>' + msg.myObjects['item7'] + '</td> .... </tr>';
        }

Result => memory leak my RAM went to 800Mo and my browser crashed with a second ajax call

3rd scenario :

var stringResponse = '';
        for (var i in msg.myObjects) {
            stringResponse += '<tr><td>' + msg.myObjects['item1'] + '</td><td>' + 
        msg.myObjects['item2'] + '</td><td>' + msg.myObjects['item3'] + '</td><td>' + 
        msg.myObjects['item4'] + '</td><td>' + msg.myObjects['item5'] + '</td><td>' + 
        msg.myObjects['item6'] + '</td><td>' + msg.myObjects['item7'] + '</td> .... </tr>';
            }
document.getElementById('mytablebody').innerHTML = stringResponse 

Result => no memory leak

Ok until here I concluded that, first of all, .append() causes memory leaks, and second of all , you should never play with DOM elements inside a loop. But when I did the 4th scenario I concluded that the first conclusion was wrong (not exactly correct) and the second one is still correct.

4th scenario :

    var stringResponse = '';
            for (var i in msg.myObjects) {
                stringResponse += '<tr><td>' + replaceNulls(msg.myObjects['item1']) + '</td><td>' + 
            msg.myObjects['item2'] + '</td><td>' + msg.myObjects['item3'] + '</td><td>' + 
            msg.myObjects['item4'] + '</td><td>' + msg.myObjects['item5'] + '</td><td>' + 
            msg.myObjects['item6'] + '</td><td>' + msg.myObjects['item7'] + '</td> .... </tr>';
                }
    document.getElementById('mytablebody').innerHTML = stringResponse 

function replaceNulls(input) {
  return input != null ? input : ''
}

Result => memory leak my RAM went to 2Go and my browser crashed

My questions are:

when we call functions that occurs outside a loop , it may causes a memory leak, why ?

How can I avoid this (without removing the function or moving its processing to inside the loop) ?

11
  • Try pushing the html strings in an array and use Array.join() method when assigning it to the innerHTML Commented Jan 24, 2018 at 11:51
  • 1
    The issue is simply because you're making thousands of DOM operations in a loop, very quickly. Without knowing anything about the internals of the browser's JS engine, it's safe to say that's less than ideal. This is why the 3rd method you created (building the HTML string in memory, then appending it in a single DOM operation) is the best performing. This is the way all DOM appends should be done, but in most cases they are such small scale that any performance issues are not noticed. Commented Jan 24, 2018 at 11:52
  • it may causes a memory leak, why ? There is no memory leak here.. Commented Jan 24, 2018 at 11:53
  • @RoryMcCrossan yes I agree , but i am stuck now with the 4th scenario, i can not use functions within a loop Commented Jan 24, 2018 at 11:53
  • The behavior you are experiencing is quite normal. You are spawning 1000 DOM elements. I dont know what your specific project needs are but I would suggest rethinking your UI by putting a button to limit the objects on the screen or using lazy loading Commented Jan 24, 2018 at 11:54

1 Answer 1

2

1000 table entries for modern web browsers should not cause any issue.

Here I'm adding 10,000 items into a table without ram problems. The reason why it's fast is because I build up the list inside a detached DOM element, then attach when done.

Your problem might be just down to DOM draw issue, the browser having to redraw on all your updates.

Another thing I noticed, for (var i in msg.myObjects) depending on what your msg.myObjects contains this is not a good thing to use. If you can use modern JS,. for (const i of msg.myObjects) is better.

var table = document.querySelector("table");

var tbody = document.createElement("tbody");

function addLine (txt) {
  var tr = document.createElement("tr");
  var td = document.createElement("td");
  tr.appendChild(td);
  td.innerText = txt;
  tbody.appendChild(tr);
}

for (var l = 1; l <= 10000; l += 1) {
  addLine("This is Line " + l + ", and some extra text");
}

table.appendChild(tbody);
<table>
  <thead><tr><th>Test</th></tr></thead>

</table>

Sign up to request clarification or add additional context in comments.

9 Comments

let me test your idea and i will tell you the result , but i am sure it will cause a memory leak for my case , because i have 14 colmuns full of data
I've tried to create 14 nodes on jsfiddle and append them on the table body , it did work great , i will test it now on my code and Ill tell you the result right after
why should i use const instead of using var It's not so much the const, but it's the for in, it will also iterate none owned properties, it's the reason you see a lot of obj.hasOwnProperty(prop) when for in is often used, for of does not have this issue. const is also a good habit for use in modern JS engines, for performance & linting.
Okay now I completely understood the difference between "for in" and "for of", i tried to create an array prototype outside this scope, and when I used the "for in" loop it is shown up like if it was an element on my array, and when I used the "for of" it is not shown up. so the "for of" is more usefull here. +1
Placing this at the begining of your click function will remove the old tbody, -> var otoby = table.querySelector("tbody");if (otoby) otoby.remove();
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.