Question3r Question3r - 3 years ago 168
CSS Question

Recycling DOM structure instead of rebuilding it

I create divs dynamically and want to place new divs in the correct order by checking the ids of the divs.

When creating new data, I sort my array and create a new div container. When building the DOM for the first time, it works fine because I create the data first and then create the div containers for each element in my array.

When creating new containers by clicking on a button, the array will get sorted but the new div containers are just appended below the other ones.

Each div container has a unique id, so I could check the placemenet by checking their id.



var myObjs = [];

function Obj(number, name) {
this.number = number;
this.name = name;
}

$(document).ready(function () {
myObjs.push(new Obj(4, "div # " + 4)); // create test data
myObjs.push(new Obj(15, "div # " + 15));
myObjs.push(new Obj(9, "div # " + 9));

myObjs = sortMyList(); // SORT

$(myObjs).each(function (index, current) {
buildContainer(current); // create the divs
});

createNewContainer(12); // create more test data by button click
createNewContainer(3); // create test data
});

function buildContainer(currentObj){ // Create a DOM element here
var container = $("<div></div>");
container.addClass("singleContainer");
container.attr("id", currentObj.number);
container.html(currentObj.name);
$("#listContainer").append(container);
}

function createNewContainer(newNumber){
var newObj = new Obj(newNumber, "div # " + newNumber);
myObjs.push(newObj);

myObjs = sortMyList(); // SORT

buildContainer(newObj);
}

function sortMyList() {
return myObjs.sort(function (a, b) {
var key = "number";
var x = a[key];
var y = b[key];
return ((x < y) ? -1 : ((x > y) ? 1 : 0));
});
}

.singleContainer{
height: 50px;
background: red;
color: white;
margin-top: 10px;
}

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="listContainer">

</div>





So as you can see, I need to fix the
buildContainer()
function to achieve this. The new created container just needs to know where to put itself between the other existing containers.

The only idea I have is destroying all the containers and rebuilding them all again. But because of performance, I don't want this, right?

So I could go with this code, I would have to place it into my
createNewContainer()
function right after sorting the array.

$("#listContainer").empty(); // Remove all children / div containers
$(myObjs).each(function (index, current) {
buildContainer(current); // build them all again
});


Some help would be awesome!

Answer Source

Taking into account that current divs are already in order in the DOM, one option is to select all your current containers and create a loop to find the right position...

var newId = 10;
var pos = 0;

$('div.singleContainer').each(function() {
    if (newId < $(this).attr('id')) {
        pos = parseInt($(this).attr('id'));
        return false; // We have the position, we don't need to keep looping!
    }
});

if (pos > 0)
    $('div.singleContainer#'+pos).before('<div id="'+newId+'" class="singleContainer">div # '+newId+'</div>');
else
    $('div#listContainer').append('<div id="'+newId+'" class="singleContainer">div # '+newId+'</div>');

NOTE: In case the new id equals to an existing one, this will position the new div before the existing one.

So, in your case...

function buildContainer(currentObj) { // Create a DOM element here

    var pos = 0;

    $('div.singleContainer').each(function() {
        if (currentObj.number < $(this).attr('id')) {
            pos = parseInt($(this).attr('id'));
            return false; // We have the position, we don't need to keep looping!
        }
    });

    if (pos > 0)
        $('div.singleContainer#'+pos).before('<div id="'+currentObj.number+'" class="singleContainer">'+currentObj.name+'</div>');
    else
        $('div#listContainer').append('<div id="'+currentObj.number+'" class="singleContainer">'+currentObj.name+'</div>');
}

EDITED:

Having a look at Martin Chaov answer, he has a good point. And considering that you have an array of objects that mantain the same order and elements, probably a better option would be to search the position in that array, instead of in the DOM elements. If you have many divs, this option could be faster...

function buildContainer(currentObj) { // Create a DOM element here

    var pos = 0;

    for (var i=0, l=myObjs.length; i<l; i++) {
        if (currentObj.number < myObjs[i].number) {
            pos = myObjs[i].number;
            break;
        }
    }

    if (pos > 0)
        $('div.singleContainer#'+pos).before('<div id="'+currentObj.number+'" class="singleContainer">'+currentObj.name+'</div>');
    else
        $('div#listContainer').append('<div id="'+currentObj.number+'" class="singleContainer">'+currentObj.name+'</div>');
}

I hope it helps

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download