0

I have a JavaScript function defined. Inside this function, I define variables of type jQuery elements. So, the variables refer to divs on the HTML. This function returns an object that has a single function init().

In the $(document).ready function, I call init() function.

The problem is when the script loads, the DOM is not ready and hence the variables referring to jQuery items are being set to undefined.

Later, I call the init() function inside Angular ngOnInit() to make sure things are well initialized, so nothing is happening as the variables above are undefined and they are not being re-calculated again.

Seems, when a function in JavaScript is defined, its body runs, and hence the variables are run and set to undefined as the HTML elements were not in the DOM yet.

How can I re-calculate the variables when init() runs? I cannot get my mind on this thing.

Thanks

var mQuickSidebar = function() {
var topbarAside = $('#m_quick_sidebar');

console.log('Function: ', Date.now());

var topbarAsideTabs = $('#m_quick_sidebar_tabs');    
var topbarAsideClose = $('#m_quick_sidebar_close');
var topbarAsideToggle = $('#m_quick_sidebar_toggle');
var topbarAsideContent = topbarAside.find('.m-quick-sidebar__content');

var initMessages = function() {
    var messenger = $('#m_quick_sidebar_tabs_messenger');  

    if (messenger.length === 0) {
        return;
    }

    var messengerMessages = messenger.find('.m-messenger__messages');

    var init = function() {
        var height = topbarAside.outerHeight(true) - 
            topbarAsideTabs.outerHeight(true) - 
            messenger.find('.m-messenger__form').outerHeight(true) - 120;

        // init messages scrollable content
        messengerMessages.css('height', height);
        mApp.initScroller(messengerMessages, {});
    }

    init();        

    // reinit on window resize
    mUtil.addResizeHandler(init);
}

var initSettings = function() { 
    var settings = $('#m_quick_sidebar_tabs_settings');

    if (settings.length === 0) {
        return;
    }

    // init dropdown tabbable content
    var init = function() {
        var height = mUtil.getViewPort().height - topbarAsideTabs.outerHeight(true) - 60;

        // init settings scrollable content
        settings.css('height', height);
        mApp.initScroller(settings, {});
    }

    init();

    // reinit on window resize
    mUtil.addResizeHandler(init);
}

var initLogs = function() {
    // init dropdown tabbable content
    var logs = $('#m_quick_sidebar_tabs_logs');

    if (logs.length === 0) {
        return;
    }

    var init = function() {
        var height = mUtil.getViewPort().height - topbarAsideTabs.outerHeight(true) - 60;

        // init settings scrollable content
        logs.css('height', height);
        mApp.initScroller(logs, {});
    }

    init();

    // reinit on window resize
    mUtil.addResizeHandler(init);
}

var initOffcanvasTabs = function() {
    initMessages();
    initSettings();
    initLogs();
}

var initOffcanvas = function() {
    topbarAside.mOffcanvas({
        class: 'm-quick-sidebar',
        overlay: true,  
        close: topbarAsideClose,
        toggle: topbarAsideToggle
    });   

    // run once on first time dropdown shown
    topbarAside.mOffcanvas().one('afterShow', function() {
        mApp.block(topbarAside);

        setTimeout(function() {
            mApp.unblock(topbarAside);

            topbarAsideContent.removeClass('m--hide');

            initOffcanvasTabs();
        }, 1000);                         
    });
}

return {     
    init: function() { 

        console.log('Inside Init(): ', Date.now());

        console.log($('#m_quick_sidebar')); // topbarAside is undefined here!

        if (topbarAside.length === 0) {
            return;
        }

        initOffcanvas(); 
    }
}; }();

$(document).ready(function() {
    console.log('document.ready: ', Date.now());
    mQuickSidebar.init();
});
2
  • Did you try with some timeout to make everything available on DOM? Commented Apr 12, 2018 at 19:23
  • What do you mean please? Can you illustrate more. Commented Apr 12, 2018 at 19:33

1 Answer 1

1

The problem is that you immediately invoke your function mQuickSidebar not

Seems, when a function in JavaScript is defined, its body runs

var mQuickSidebar = function() {
    var topbarAside = $('#m_quick_sidebar');

    console.log('Function: ', Date.now());

    var topbarAsideTabs = $('#m_quick_sidebar_tabs');    
    var topbarAsideClose = $('#m_quick_sidebar_close');
    var topbarAsideToggle = $('#m_quick_sidebar_toggle');
    var topbarAsideContent = topbarAside.find('.m-quick-sidebar__content');
...
}(); // <---- this runs the function immediately

So those var declarations are run and since the DOM is not ready those selectors don't find anything. I'm actually surprised that it does not throw a $ undefined error of some sort

This looks like a broken implementation of The Module Pattern

I re-wrote your code in the Module Pattern. The changes are small. Keep in mind that everything declared inside the IIFE as a var are private and cannot be accessed through mQuickSidebar. Only the init function of mQuickSidebar is public. I did not know what you needed to be public or private. If you need further clarity just ask.

As a Module

var mQuickSidebar = (function(mUtil, mApp, $) {

    console.log('Function: ', Date.now());

    var topbarAside;
    var topbarAsideTabs;
    var topbarAsideClose;
    var topbarAsideToggle;
    var topbarAsideContent;

    var initMessages = function() {
        var messenger = $('#m_quick_sidebar_tabs_messenger');  

        if (messenger.length === 0) {
            return;
        }

        var messengerMessages = messenger.find('.m-messenger__messages');

        var init = function() {
            var height = topbarAside.outerHeight(true) - 
                topbarAsideTabs.outerHeight(true) - 
                messenger.find('.m-messenger__form').outerHeight(true) - 120;

            // init messages scrollable content
            messengerMessages.css('height', height);
            mApp.initScroller(messengerMessages, {});
        };

        init();        

        // reinit on window resize
        mUtil.addResizeHandler(init);
    };

    var initSettings = function() { 
        var settings = $('#m_quick_sidebar_tabs_settings');

        if (settings.length === 0) {
            return;
        }

        // init dropdown tabbable content
        var init = function() {
            var height = mUtil.getViewPort().height - topbarAsideTabs.outerHeight(true) - 60;

            // init settings scrollable content
            settings.css('height', height);
            mApp.initScroller(settings, {});
        };

        init();

        // reinit on window resize
        mUtil.addResizeHandler(init);
    };

    var initLogs = function() {
        // init dropdown tabbable content
        var logs = $('#m_quick_sidebar_tabs_logs');

        if (logs.length === 0) {
            return;
        }

        var init = function() {
            var height = mUtil.getViewPort().height - topbarAsideTabs.outerHeight(true) - 60;

            // init settings scrollable content
            logs.css('height', height);
            mApp.initScroller(logs, {});
        };

        init();

        // reinit on window resize
        mUtil.addResizeHandler(init);
    };

    var initOffcanvasTabs = function() {
        initMessages();
        initSettings();
        initLogs();
    };

    var initOffcanvas = function() {
        topbarAside.mOffcanvas({
            class: 'm-quick-sidebar',
            overlay: true,  
            close: topbarAsideClose,
            toggle: topbarAsideToggle
        });   

        // run once on first time dropdown shown
        topbarAside.mOffcanvas().one('afterShow', function() {
            mApp.block(topbarAside);

            setTimeout(function() {
                mApp.unblock(topbarAside);

                topbarAsideContent.removeClass('m--hide');

                initOffcanvasTabs();
            }, 1000);                         
        });
    };

    return {     
        init: function() { 

            console.log('Inside Init(): ', Date.now());

            console.log($('#m_quick_sidebar')); // topbarAside is undefined here!

            topbarAside = $('#m_quick_sidebar');
            topbarAsideTabs = $('#m_quick_sidebar_tabs');    
            topbarAsideClose = $('#m_quick_sidebar_close');
            topbarAsideToggle = $('#m_quick_sidebar_toggle');
            topbarAsideContent = topbarAside.find('.m-quick-sidebar__content');

            if (topbarAside.length === 0) {
                return;
            }

            initOffcanvas(); 
        }
    }; 
})(mUtil, mApp, $);

$(document).ready(function() {
    console.log('document.ready: ', Date.now());
    mQuickSidebar.init();
});
Sign up to request clarification or add additional context in comments.

2 Comments

So I just remove the () not to execute it immediately? Or shall I implement the Module Pattern properly? Thanks
Added a re-write as a module for you.

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.