Cronk
Cronk

Reputation: 473

JS vs DOM timing: .remove() element visually happens, but travesal still includes it

The short description of the functionality that we are trying to achieve: we have a list of source objects on the left, a person can drag new items from the list to a list on the right, items thus get added to the list on the right; they can also remove items from the list on the right. The list on the right then gets saved whenever it is changed. (I don't think the specifics of how/where it is being saved matter...)

I am having a problem with a bit of timing in the JavaScript vs. DOM elements realm of things. Items that are already on the list on the right can be removed. We have some code that fires on a 'remove/delete' type icon/button on a DOM element, that is supposed to remove the element from the DOM visually and permanently (i.e. it doesn't need to be brought back with a 'show'). This visual change should then also show up in the JSON object that is built when the JS traverses the DOM tree to build the new updated list.

However, this chunk of JS code that runs immediately after this .remove() is called, the element that should have just been removed still shows up in the JSON object. This is not good.

Here are what I believe to be the relevant bits of code operating here. This lives in a web browser; much of this is in the document.ready() function. A given list can also have subsections, hence the sub-list parts and loops.

The on-click definition:

$('body').on('click', '.removeLine', function() {
var parent=$(this).parent().parent().parent();     //The button is a few DIVs shy of the outer container
var List=$(this).closest('article');     //Another parent object, containing all the 
parent.fadeOut( 300, 
    function() {
        parent.slideUp(300);
        parent.remove(); 
    }
);
sendList(List);    //  This builds and stores the list based on the DOM elements
});

And then later on, this function definition:

function sendList(List) {
var ListArray=[], 
    subListArray=[], 
    itemsArray = [], 
    subListName = "";
var ListTitle = encodeText(List.find('.title').html());

            // loop through the subLists
List.find('.subList').each(
        function(index, element) {
            subListName=($(this).find('header > .title').html());  // Get sublist Title
            subListID=($(this).attr('id'));               // Get subList ID

            // loop through the line items
            itemsArray=[];
            $(this).find('.itemSearchResult').each(
                function(index, element) {
                            //  Build item Array
                    if( $(this).attr('data-itemid')!= item ) {
                        itemArray.push( $(this).attr('data-itemid'));
                    }
                }
            );

             // Build SubList Array with items Array
            subListArray.push(
                    {
                        "subListName": subListName,
                        "subListID" : subListID,
                        "items" : itemsArray
                    }
            );
        }
); <!-- end SubList Loop -->

// Complete List Array with subListArray
ListArray ={
"ListName": ListTitle,
"ListID": List.attr('id'),
"subLists": subListArray
};
        // Send New List to DataLists Object - the local version of storage
updateDataLists(ListArray);
        // Update remote storage
window.location= URLstring + "&$Type=List" + "&$JSON=" + JSON.stringify(ListArray) + "&$objectID=" + ListArray.ListID;

};

It seems to be the interaction of the 'parent.remove()' step and then the call to 'sendList()' that get their wires crossed. Visually, the object on screen looks right, but if we check the data being sent to the storage, it comes through WITH the object that was visually removed.

Thanks, J

PS. As you can probably tell, we are new at the Javascript thing, so our code may not be terribly efficient or proper. But...it works! (Well, except for this issue. And we have run into this issue a few times. We have a workaround for it, but I would rather understand what is going on here. Learn the deeper workings of JS so we don't create these problems in the first place.)

Upvotes: 1

Views: 170

Answers (1)

Adam Jenkins
Adam Jenkins

Reputation: 55772

There's a few things going on here, but I'm going to explain it by approaching it from an asynchronous programming perspective.

You are calling sendList before the element gets removed from the DOM. Your element doesn't get removed from the DOM until after your fadeOut callback gets executed (which takes 300ms).

Your sendList function gets called immediately after you begin the fadeOut, but your program doesn't wait to call sendList until your fadeOut is finished - that's what the callback is for.

So I would approach it by calling sendList in the callback, after your DOM element has been removed like this:

$('body').on('click', '.removeLine', function() {
    var el = $(this); //maintain a reference to $(this) to use in the callback
    var parent=$(this).parent().parent().parent();     //The button is a few DIVs shy of the outer container
    parent.fadeOut( 300, 
        function() {
            parent.slideUp(300);
            parent.remove(); 
            sendList(el.closest('article'));
       }
    );
});

Upvotes: 4

Related Questions