2

I've been trying for days to figure this out. I have read many questions on SO as well as googled it many different ways and read/attempted everything I found. Nothing I have found so far has worked for me and I have rewritten my code a million times it seems trying out different methods for doing this.

I feel like there is some super obvious thing I am missing here, and maybe just need a push in the right direction. If I'm going about this completely wrong and need to restructure everything, I can do that too.

Basically what I am working with is a front end "controller" for lack of a better word, that initializes some variables, sets up some event listeners and responds to user actions.

I don't care if I use jQuery or pure JavaScript, I just want it to work, even if I have to re-write the whole thing. My goal is speed and performance under heavy load. Another option I was considering was node.js but I have limited experience with that, so was hoping to figure it out with jQuery.

When the page loads, I do not get an error, but when I click one of the items that I have set an event listener on, I get the error... TypeError: Cannot Read Property 'apply' of undefined. And it refers to the corresponding line that starts with var scope = this ? function(e)...

The purpose of that line is to have the this keyword refer to the controller object so I can call object methods from within the event handler method. Though it seems it might not be working as I intended.

I tried to just use .on to set up the click and change handlers, but I was having problems with scope there as well. Any help, again, is much appreciated.

(function ($) {
    $(function () {  //document ready

        function Controller(authId, authKey) {

            this.user.id = authId;
            this.user.key = authKey;

            this.init();
        };


        Controller.prototype = {

            eventChange: [ "amt", "multi" ],
            eventClick: [ "double", "half", "high", "low" ],

            event: { refresh: ['amt', 'multi'], update: ['double', 'half'], process: ['high', 'low'] },

            user: { id: '', key: '', name: '', balance: '' },

            init: function () {

                this.initEvents();
           },
            initEventz: function() {

                for (var i = 0; i < this.eventChange.length; i += 1) {

                    var ele = document.getElementById(this.eventChange[i]);

                    var scope = this ? function(e) { this.handleEvent.apply(this, ["change"]); } : this.handleEvent;

                    if(document.addEventListener) {
                        ele.addEventListener("change", scope, false);
                    } else if(document.attachEvent) {
                        ele.attachEvent("onchange",  scope);
                    }
                }

                for (var i = 0; i < this.eventClick.length; i += 1) {

                    var ele = document.getElementById(this.eventClick[i]);

                    var scope = this ? function(e) { this.handleEvent.apply(this, ["click"]); } : this.handleEvent;

                    if(document.addEventListener) {
                        ele.addEventListener("click", scope, false);
                    } else if(document.attachEvent) {
                        ele.attachEvent("onclick",  scope);
                    }  
                }
           },
           handleEvent: function (e) {

              var eventId = e.target.id;

              for (var event in this) {
                    if (this.hasOwnProperty(event)) {

                           console.log(event);
                    }
              }
          }
        };
       var Controller = new Controller($("#auth").val(), $("#key").val());


    }); //end document ready

})(jQuery);
halfer
  • 19,824
  • 17
  • 99
  • 186
Tech Savant
  • 3,686
  • 1
  • 19
  • 39

1 Answers1

1

You are losing the reference to this.

You can solve that with this code:

var scope = this ? function(e) { this.handleEvent.apply(this, ["click"]); }.bind(this) : this.handleEvent;

but if you want that the handler have access to the element within his scope with the reference of this you should write this:

var scope = this ? function(e) { this.handleEvent.apply(ele, ["click"]); }.bind(this) : this.handleEvent;

or this

var that = this;

var scope = this ? function(e) { that.handleEvent.apply(ele, ["click"]); } : this.handleEvent;

I have seen other mistake. Because if this is undefined then scope is going to be this.handleEvent but this is going to raise an error because undefined can't have the handleEvent property.

gabrielperales
  • 6,935
  • 1
  • 20
  • 19
  • OK, I used your first solution, by using bind(this), but now when it gets to the handleEvent method, I get the error that property 'id' is undefined when trying to access e.target.id. Any ideas? – Tech Savant Jan 04 '16 at 00:08
  • Can I see your code running on codepen or somewhere ? I'm trying fix it but without the example is more difficult. I going to keep working on it :). I'm making changes on my answer. – gabrielperales Jan 04 '16 at 00:12
  • Yes, the thing is that when you use apply, the first argument is going to be "the context", and the second argument is an array of arguments that you want to pass to the function, and you are passing "click" as the argument of handleEvent, and you are trying to read the property `target.id` of the string "click" – gabrielperales Jan 04 '16 at 00:19
  • try this `var scope = this ? function(e) { this.handleEvent.call(this, e); }.bind(this) : this.handleEvent;` – gabrielperales Jan 04 '16 at 00:23
  • This makes e.target.id set correctly in the handleEvent method. However, when I do `for (var event in this)` event is undefined. Any ideas? I will mark correct and award bounty. But if you can help me figure out this event thing, would be greatly appreciated. FYI, it will be 2 days until i can award bounty, but I will. Please make comment here if you don't get bounty in a couple days and remind me. – Tech Savant Jan 04 '16 at 00:28
  • This shows me the mouseevent. I'm trying to acess the event property of the controller object. I will add it to my question, as I don't think it's in there. But basically `event: { refresh: ['amt', 'multi'], update: ['double', 'half'], process: ['high'], ['low'] }` and I want to loop through refresh, update, and process, and then check each array if e.target.id exists. – Tech Savant Jan 04 '16 at 00:48
  • Maybe you can't use "event" as an object property because it's a keyword? – Tech Savant Jan 04 '16 at 00:54
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/99637/discussion-between-notorious-pet0-and-gabrielperales). – Tech Savant Jan 04 '16 at 00:55
  • try with [this](https://jsfiddle.net/5w5ksL9j/2/). I have to leave. Tomorrow I will keep helping you if I can :-P – gabrielperales Jan 04 '16 at 01:07
  • I got it working. Thanks for all your help. Remind me in a couple days if you don't get your bounty. – Tech Savant Jan 04 '16 at 02:24