Richard Knop
Richard Knop

Reputation: 83755

How to increase performance of this js script (function calls exponentially increasing)?

Any general tips how to increase a longish js code like this? It works great but sometimes it gets a little sluggish and all the drag/drop and ajax features slow down.

I know this code is a little longer so I am not looking for specific suggestions. Just your first thoughts after taking a quick glance at this code.

EDIT:

I have found a pretty scary thing. The calls to dragDrop() are exponentially increasing in the $('.folderListOnclick').click event. After each click dragDrop() is called 1 time, 2 times, 4 times, 8 times. That's what's slowing it down.

But I don't understand why it is happening.

<script type="text/javascript">
    //<!--
$(document).ready(function() {

    function in_array (needle, haystack, argStrict) {
        var key = '', strict = !!argStrict; 
        if (strict) {
            for (key in haystack) {
                if (haystack[key] === needle) {
                    return true;            }
            }
        } else {
            for (key in haystack) {
                if (haystack[key] == needle) {
                    return true;
                }
            }
        }
         return false;
    }

    var openedFolders = new Array();

    var start = 0;
    var stop = 0;
    $('.drag').each(function() {
        var draggables = $(this).parents('table').find('.drag');
        var $next = draggables.filter(':gt(' + draggables.index(this) + ')').first();
        var width = $(this).css('width');
        var nextWidth = $next.css('width');
        if (nextWidth > width /*&& 30 == parseInt(width)*/) {
            $(this).removeClass('ordinaryFolderClosed');
            $(this).removeClass('ordinaryFolderOpened');
            if (in_array($(this).attr('rel'), openedFolders)) {
                $(this).addClass('ordinaryFolderOpened');
            } else {
                $(this).addClass('ordinaryFolderClosed');
            }               
        }
        if (in_array($(this).attr('rel'), openedFolders)) {
            start = 1;
        }
        if (1 == start && stop < 2) {               
            if (30 == parseInt(width)) {
                stop++;
            }
        } else {
            start = 0;
            stop = 0;
            if (parseInt(width) > 30) {
                $(this).parent().parent().hide();
            }
        }
    });

    function dragDrop()
    {       
        $('.folders .trow1').hover(
            function () {
                if ($(this).css('backgroundColor') != 'rgb(52, 108, 182)' && $(this).css('backgroundColor') != '#346cb6') {
                    $(this).css('background', "#C2E3EF");
                }
            },
            function () {
                if ($(this).css('backgroundColor') != 'rgb(52, 108, 182)' && $(this).css('backgroundColor') != '#346cb6') {
                    $(this).css('background', 'transparent');   
                }
            }
        );

        $('.drag').click(function() {

            if ($(this).hasClass('noclick')) {
                $(this).removeClass('noclick');
            } else {

                var draggables = $(this).parents('table').find('.drag');
                var $next = draggables.filter(':gt(' + draggables.index(this) + ')').first();
                var width = $(this).css('width');
                var nextWidth = $next.css('width');
                if (nextWidth > width /*&& 30 == parseInt(width)*/) {
                    var isVisible = $next.is(':visible');
                    if (isVisible) {
                        $(this).removeClass('ordinaryFolderClosed');
                        $(this).removeClass('ordinaryFolderOpened');
                        $(this).addClass('ordinaryFolderClosed');
                    } else {
                        $(this).removeClass('ordinaryFolderClosed');
                        $(this).removeClass('ordinaryFolderOpened');
                        $(this).addClass('ordinaryFolderOpened');
                    }
                    clickedId = $(this).attr('rel');
                    clickedId = clickedId.split(',');
                    clickedType = clickedId[1];
                    clickedId = clickedId[0];
//                  $.ajax({
//                      type:    'POST',
//                      url:     'body/obsah/mediaManager/setOpenedFolder.php',
//                      data:    'id='+clickedId+'&type='+clickedType,
//                      success: function(msg){
//                          //alert(msg);
//                      }
//                  });
                    var start = 0;
                    var stop = 0;
                    var i = 0;
                    // close folder
                    if (isVisible) {
                        $('.drag').each(function() {
                            if (0 == start) {
                                iteratedId = $(this).attr('rel');
                                iteratedId = iteratedId.split(',');
                                iteratedId = iteratedId[0];
                                if (iteratedId == clickedId) {
                                    start = 1;
                                }
                            }
                            if (1 == start && stop < 2) {
                                if ($(this).css('width') > width) {
                                    $(this).parent().parent().hide();
                                    if ($(this).hasClass('ordinaryFolderClosed') || $(this).hasClass('ordinaryFolderOpened')) {
                                        $(this).removeClass('ordinaryFolderClosed');
                                        $(this).removeClass('ordinaryFolderOpened');
                                        $(this).addClass('ordinaryFolderClosed');
                                    }
                                } else {
                                    stop++;
                                }
                            }
                            i++;
                        }); 
                    }
                    // open folder
                    else {
                        $('.drag').each(function() {
                            if (0 == start) {
                                iteratedId = $(this).attr('rel');
                                iteratedId = iteratedId.split(',');
                                iteratedId = iteratedId[0];
                                if (iteratedId == clickedId) {
                                    start = 1;
                                }
                            }
                            if (1 == start && stop < 2) {
                                if (parseInt($(this).css('width')) == parseInt(width)+5) {
                                    $(this).parent().parent().show();                   
                                }

                                if (parseInt($(this).css('width')) == parseInt(width)) {
                                    stop++;
                                }
                            }
                            i++;
                        });
                    }
                }

            }
        });

        var dragId = 0;
        var dragType = 0;
        var dropId = 0;
        var dropType = 0;
        var isFile = false; 

        $('.drag').draggable({ 
            revert: true,
            cursorAt: {top: 0, left: 0},
            drag: function() {
                if ($(this).attr('rel') !== undefined) {
                    dragId = $(this).attr('rel');
                    dragId = dragId.split(',');
                    dragType = dragId[1];
                    dragId = dragId[0];
                }
                isFile = false;
            },
            start: function(event, ui) {
                 $(this).addClass('noclick');
            }
        });

        $('.drag2').draggable({
            revert: true,
            cursorAt: {top: 0, left: 0},
            drag: function() {
                if ($(this).attr('rel') !== undefined) {
                    dragId = $(this).attr('rel');
                    dragId = dragId.split(',');
                    dragType = dragId[1];
                    dragId = dragId[0];
                }
                isFile = true;
            }
        });     

        $('.drop').droppable({
            tolerance: 'pointer',
            drop: function() {
                if ($(this).attr('rel') !== undefined) {
                    dropId = $(this).attr('rel');
                    dropId = dropId.split(',');
                    dropType = dropId[1];
                    dropId = dropId[0];
                    if (dropId != dragId) {
                        if (false == isFile) {
                            $.ajax({
                                type:    'POST',
                                url:     'body/obsah/mediaManager/folder_move.php',
                                data:    'nid='+dragId+'&pid='+dropId+'&ft='+dropType,
                                success: function(msg){
                                    ajaxElementCall('left1', 'body/obsah/mediaManager/folder_list.php?type=1', 'right1', 'body/obsah/mediaManager/file_list.php?type=1&browse=0&assignType=0&CKEditorFuncNum=0&idFolder=0');
                                    dragDrop();
                                }
                            });
                        } else if (true == isFile) {
                            $.ajax({
                                type:    'POST',
                                url:     'body/obsah/mediaManager/file_move.php',
                                data:    'fid='+dragId+'&did='+dropId+'&ft='+dropType,
                                success: function(msg){
                                    ajaxElementCall('right1', 'body/obsah/mediaManager/file_list.php?type=1&browse=0&assignType=0&CKEditorFuncNum=0&idFolder=0&reloadTree=0');
                                    dragDrop();
                                }
                            });
                        }
                    }
                }           
            }
        });

    }

    dragDrop();

    $('.folderListOnclick').click(function() {
        var append = 'idFolder='+$(this).attr('rel')+'&browse=0&assignType=0&CKEditorFuncNum=0&reloadTree=0';
        ajaxElementCall('right1', 'body/obsah/mediaManager/file_list.php?type=1&'+append);
        dragDrop();
        $('.trow1').css('background', 'transparent');
        $('.trow1').css('color', '#3e4245');
        $(this).parent().css('background', "#346cb6 url('img/menuButtonOver.png') left top repeat-x");
        $(this).parent().css('color', 'white');
    });

    $('.folderEditOnclick').click(function() {
        var append = 'idFolder='+$(this).attr('rel')+'&browse=0&assignType=0&CKEditorFuncNum=0';
        showModal('modal_div', 'Editácia adresára'); 
        ajaxElementCall('modal_div', 'body/obsah/mediaManager/folder_edit.php?kam=edit1&'+append);
    });

    $('.folderDeleteOnclick').click(function() {
        var append = 'idFolder='+$(this).attr('rel')+'&browse=0&assignType=0&CKEditorFuncNum=0';
        showModal('modal_div', 'Vymazanie adresára'); 
        ajaxElementCall('modal_div', 'body/obsah/mediaManager/folder_delete.php?kam=del1&'+append);
    });

    $('.addNewFolder').click(function() {
        showModal('modal_div', 'Nový adresár');
        var id = '0';
        $('.folders .trow1').each(function() {
            if ($(this).css('backgroundColor') == 'rgb(52, 108, 182)' || $(this).css('backgroundColor') == '#346cb6') {
                id = $(this).attr('rel');
                id = id.split(',');
                id = id[0];
            }
        });
        ajaxElementCall('modal_div', '/body/obsah/mediaManager/folder_add.php?type=1&kam=new1&idFolder='+id+'&browse=0&assignType=0&CKEditorFuncNum=0');
    });

});    //-->

</script>

Upvotes: 1

Views: 218

Answers (5)

Pointy
Pointy

Reputation: 413996

Well you have not posted any of your HTML markup, and you have not posted the details of what the "ajaxElementCall" function does. Therefore it's hard to say exactly how you should fix the problem. It is true however that on every "drop" event, you end up calling the "dragDrop()" setup function again. You say that "ajaxElementCall" reloads some portion of the page, but your "dragDrop()" code always installs new event handlers on all ".drag" and ".drop" elements on the page. If "ajaxElementCall" only updates part of the page, then all the unchanged ".drag" and ".drop" elements will get additional event handlers piled on.

When you call .click() or .hover() for some selector, jQuery adds the event handler you supply to the set of handlers already registered. Thus, because you register new event handlers every time "dragDrop()" is called, you'll get more and more piled up. When an event happens, all of those handlers will be run.

Probably what you need to do is change "dragDrop()" so that you can tell it to only operate on a particular fraction of the page. Either that, or else when it runs it should "mark" each element it affects and then check for that mark before applying new event handlers. That way it will only affect newly-loaded code. (It would be more efficient to narrow down the search, however; the expression $('.drag') may have to look through every DOM element on the page, so it would be preferable to use something more precise anyway.)

Upvotes: 2

Yoav Weiss
Yoav Weiss

Reputation: 2270

One thing that can significantly optimize the above code is replacing the in_array function and the openedFolders array a JS object that functions as a dictionary and is much more efficient. Decleration:

openedFolders={};

Add a new folder to the opened folders object:

openedFolders[folderName]=valueRelatedToTheFolderIfYouHaveOneOrWhatEver; // :)

Search for an opened folder:

if(openedFolders.hasOwnProperty(folderName)){/*folderName is open*/}

Upvotes: 0

David M&#229;rtensson
David M&#229;rtensson

Reputation: 7620

For one, all the $(this) should be changed to use a variable.

var me = $(this); 

me.XXX

$(this) is a method call and its unnessesary to call it over and over again on the same object.

This needs to be done on a per block basis as this will be differet objects in every block ;)

Upvotes: 2

Thariama
Thariama

Reputation: 50840

It won't make it that much faster, but this will speed it up a few milliseconds: You should reuse a jquery instance instead of generating a new one (this costs some time).

Example: instead of

$(this).parent().css('background', "#346cb6 url('img/menuButtonOver.png') left top repeat-x");
$(this).parent().css('color', 'white');

use

var this_parent = $(this).parent();
this_parent.css('background', "#346cb6 url('img/menuButtonOver.png') left top repeat-x");
this_parent.css('color', 'white');

Upvotes: 1

dpq
dpq

Reputation: 9278

Lookup operations like $(this) are pretty expensive. You'd better store and address object references instead; it helped me a lot in similar circumstances.

Upvotes: 1

Related Questions