0

I'm trying to figure out what's the best way to bind event handlers from within object literal.

Imagine the following object:

MySystem.ValidationTop = {

    topClose : function() {

        "use strict";

        $(document).on('click', '#topCloseTrigger', function(event) {

            // some action goes here

        });

    },

    topExecute : function() {

        "use strict";

        $(document).on('click', '#topExecuteTrigger', function(event) {

            // some action goes here

        });

    },

    topWarningTemplate : function(thisWarning) {

        "use strict";

        // warning is wrapped and styled here

    },

    addTopWarning : function(thisWarning) {

        "use strict";

        if (typeof thisWarning !== 'undefined') {

            this.addTop($(this.topWarningTemplate(thisWarning)));
            this.topClose();

        }

    },

    topConfirmationTemplate : function(thisConfirmation) {

        "use strict";

        // confirmation is wrapped and styled here

    },

    addTopConfirmation : function(thisConfirmation) {

        "use strict";

        if (typeof thisConfirmation !== 'undefined') {

            this.addTop($(this.topConfirmationTemplate(thisConfirmation)));
            this.topClose();

        }

    }

};

The problem here is that the method addTopWarning and addTopConfirmation can be called multiple times from other objects and this will effectively also call the topClose and topExecute multiple times and create duplicate bindings to the same objects. I know I could convert it to the instance object, which initialises all of the bindings when the instance of the object is created, but again - with multiple instances of the same object the bindings will occur several times as well.

I'd prefer to keep it as object literal as this specific object serves more as a helper than object that needs to be instantiated.

I can't figure out how to approach it so if someone could advice please - it would be very much appreciated.

Spencer Mark
  • 5,263
  • 9
  • 29
  • 58
  • I might have misunderstood but, what is preventing you from checking if the event handler has [already been instantiated](http://stackoverflow.com/questions/1236067/test-if-event-handler-is-bound-to-an-element-in-jquery) or just having a flag or counter to make sure it only happens once unless it gets triggered (and reset the flag from the handler). – mechalynx Jul 22 '14 at 22:29
  • ^ or checking if the given DOM element has already given JS object assigned to it – eithed Jul 22 '14 at 22:30
  • Any example please? I've heard jQuery doesn't allow you to check for binding using .data('events') any more? – Spencer Mark Jul 22 '14 at 22:32
  • why not add an additional optional parameter to those functions (i.e., addTopWarning, etc) that will accept a function to be called. You could then pass topClose to it when you want it called, and leave it off otherwise. (You might need to pass your context with the function, too.) – fgshepard Jul 22 '14 at 22:32
  • @fgshepard - But this will again create multiple bindings every time that the method is called. – Spencer Mark Jul 22 '14 at 22:33
  • hmm. you might consider creating a registry of event bindings that you can check before binding any more. along those lines, you could create a custom bind method that would check the registry for you whenever it is called. – fgshepard Jul 22 '14 at 22:54
  • Why do you need those as methods at all? Just bind them once, in a single-ton-like manner, when instantiating that object literal. They're using event delegation anyway, so why bother? – Bergi Jul 22 '14 at 23:14
  • Or, if you expect only one box to be open at one time, and it will get closed before the next one is added, just use [`.one`](http://api.jquery.com/one/) – Bergi Jul 22 '14 at 23:16

3 Answers3

2

With jQuery's event namespaces you can do this:

$(document)
      .off('click.myValidation')
      .on('click.myValidation', '#topCloseTrigger', function(event) {
        // some action goes here
      });

the .off() above will ensure that only one handler is set for that event.

c-smile
  • 26,734
  • 7
  • 59
  • 86
0

Use something to check if the function has already been run.

Here is an example with a closure

function  validationTop(/* some args */){
    var trigger = {'conf':true, 'warn':true};

    return {
        topConfirmation: function(){
            if(trigger['conf']){
                //do some stuff
                trigger['conf'] = false;
            }
        }

        //rest of your object
    }   
}
Gabs00
  • 1,869
  • 1
  • 13
  • 12
  • Yes - but that's not quite object literal - and again, if there are multiple instances of the same object - it will be binded multiple times as each instance will check for the binding internally - isn't that right? – Spencer Mark Jul 22 '14 at 22:53
  • @SpencerMark The result is an object in object literal syntax, you would make the object with the function, and each object will have its own copy of trigger. – Gabs00 Jul 22 '14 at 22:55
  • but wouldn't that bind event to the same object once for every instance? – Spencer Mark Jul 22 '14 at 22:59
0

To stop it from binding agian

MySystem.ValidationTop = {
  topClosed : 0,
  topExecuted : 0,
  topClose: function(){
    "use strict";
    if(!this.topClosed){
      this.topClosed = 1;
      $(document).on('click', '#topCloseTrigger', function(event) {
        // some action goes here
      });
    }
  }
}

repeat

Across multiple instaces:

var topClosed = 0, topExecuted = 0;
MySystem.ValidationTop = {
  topClose: function(){
    "use strict";
    if(topClosed){
      topClosed = 1;
      $(document).on('click', '#topCloseTrigger', function(event) {
        // some action goes here
      });
    }
  }
}

If you need multiple instances of an Object:

if(!Object.create){
  Object.create = function(o){
    function F(){}
    F.prototype = o;
    return new F;
  }
}

Now just use var whatever = Object.create(MySystem.ValidationTop);

StackSlave
  • 10,613
  • 2
  • 18
  • 35
  • So just to clarify - if I make it a prototype of an object it will always only execute the binding once regardless of how many times the object will be instantiated? – Spencer Mark Jul 22 '14 at 22:51
  • No, I'm using the `topClosed` property to stop that. If you create a new instance, the way you wrote it you will bind the click Event again. You could always use a variable scoped outside of the Object for that. It would work like a static variable. I'll show example. – StackSlave Jul 22 '14 at 23:04