1

I have a panel widget with a button. Clicking the button should execute some global actions related to all such widgets and after that execute some local actions related to this widget instance only. Global actions are binded in a separate javascript file by CSS class like this:

var App = function ()
{
    var handleWidgetButton = function ()
    {
        $('.widgetBtn').on('click', function (e)
        {
            // do smth global
        });

        return {
            init: function ()
            {
                handleWidgetButton();
            }
        };
    }
}();

jQuery(document).ready(function() 
{
    App.init();
});

And in the html file local script is like this:

$("#widgetBtn1234").click(function (e)
{
    // do smth local
});

Currently local script is executed first and global only after while I want it to be the opposite. I tried to wrap local one also with document.ready and have it run after global but that doesn't seem to change the execution order. Is there any decent way to arrange global and local jQuery bindings to the same element?

2
  • Personally I wouldn't write code where the order of different event handlers matter. If the order is important, that means the code must be related to each other, and should probably be being triggered by one handler. Independent event handlers suggests independent code. Commented Jan 7, 2016 at 13:02
  • Totally agree. I tried to generalize the question, however the actual problem is a bit tricky. Global code suppose to change the size of the panel and local one suppose to re-arrange the items inside etc. So technically local code should react on different event - div resize but since there is no such thing (easily done at least) I thought I could chain those behind the same button click. Commented Jan 7, 2016 at 13:25

3 Answers 3

1

The problem you're having comes from using jQuery's .ready() function to initialize App, while you seem to have no such wrapper in your local code. Try the following instead:

var App = function ()
{
    var handleWidgetButton = function ()
    {
        $('.widgetBtn').on('click', function (e)
        {
            // do smth global
        });

        return {
            init: function ()
            {
                handleWidgetButton();
            }
        };
    }
}();

$(function() 
{
    App.init();
});

Then in your local JS:

$(function() {
    $("#widgetBtn1234").click(function (e)
    {
        // do smth local
    });
});

Note that $(function(){}) can be used as shorthand for $(document).ready(function(){});. Also, make sure your JS file is located before your local JS, as javascript runs sequentially.

Alternatively, you can use setTimeout() to ensure everything's loaded properly:

(function executeOnReady() {
    setTimeout(function() {
        // Set App.isInitialized = true in your App.init() function
        if (App.isInitialized) runLocalJs();
        // App.init() hasn't been called yet, so re-run this function
        else executeOnReady();
    }, 500);
})();
function runLocalJs() {
    $("#widgetBtn1234").click(function (e)
    {
        // do smth local
    });
};
Sign up to request clarification or add additional context in comments.

3 Comments

yes, I mentioned I tried wrapping local to .ready too already but weird thing, they seem to be run properly sequentially but click event is still handled in the opposite order. I added console.log printouts in both global and local .ready inits and they are printed in the right order global first then local. But similar printouts on click come wrong, local first. That got me thinking that is there something in jQuery that would prefer direct div reference by id to the more common one like by class reference, no matter the binding order.
OK, sorry it was my mistake eventually. Global code was binding to upper element with path to the widget button and that seems to matter to the order jQuery is executing. $(".widgetBtn").click(function (e) apparently has precedence over $('.widget').on('click', '.widgetBtn', function (e) even if the latter is binded first. Didn't think that this would matter.
@ollit my solution would work regardless of the order and also provides consequential run after the asyncronous global handler (I guess it's not the case, ok). Anyway, just as an addition, I also would refactor the App declaration so it would be an instance of the App class, to be able to use return this and make it more maintainable. And also, regarding the answer - why just not to implement onload method for App class and use it with one line App.onload = onAppLoad; instead of rechecking init flag every 500 ms? And, btw, runLocalJs is more like initLocalClickHandlers, right?
0

How about this instead:

var widget = $("#widgetBtn1234").get(0);//get the vanilla dom element
var globalHandler = widget.onclick; //save old click handler

// clobber the old handler with a new handler, that calls the old handler when it's done
widget.onclick = function(e){
  //do smth global by calling stored handler
  globalHandler(e);

  //afterward do smth local   
};

There might be a more jqueryish way to write this, but I hope the concept works for you.

-------VVVV----keeping old answer for posterity----VVVV--------

Why not something like this?

    var App = function ()
{
    var handleWidgetButton = function ()
    {
        $('.widgetBtn').on('click', function (e)
        {
            // do smth global
            if(this.id === 'widgetBtn1234'){
               //do specific things for this one
            }
        });

        return {
            init: function ()
            {
                handleWidgetButton();
            }
        };
    }
}();

Please excuse any syntax errors I might have made as I haven't actually tested this code.

4 Comments

This is exactly what I try to avoid. Global code suppose to be common and shared among different projects, kind of a library. I was thinking about something like this but other way around, calling global code from local file. But not sure how to deal with jQuery bindings in global then.
Ah, I think I understand your reasoning for keeping things separate now, I think I have a different idea for a solution.
I Edited the answer with a different method.
Thanks, actually idea of storing event handlers seems interesting. I found similar solutions here. Might be helpful for some tricky cases.
0

Check out my simple JQ extension I created on jsbin.

http://jsbin.com/telofesevo/edit?js,console,output

It allows to call consequentially all defined personal click handlers after a global one, handle missed handlers case if necessary and easily reset all personal handlers.

Comments

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.