Luigi
Luigi

Reputation: 201

JQuery and Uncaught TypeError: undefined is not a function - no clues about the real pitfall

I'm having a problem writing an handler for dynamically generated elements in DOM.

My object is composed with a few attributes and some methods

function JMaze(c,r,selector) {
   this.maze = new Array();
   this.content = new Array();
   this.c = c;
   this.r = r;
   this.x = 0;
   this.y = 0;
   this.selector = selector; //need a DOM place to fit with the maze
   this.bookmark = "Home";

   //...other methods...

  this.goTo = function(event){
    console.log("HERE");
    var maze = this.maze;
    var bookmark = this.bookmark;
    var start, destination; 
    for (i = 0; i < c; i++) 
        for (j = 0; j < r; j++) 
            if (maze[i][j].id == bookmark)
                start = maze[i][j];
    for (i = 0; i < c; i++) {
        for (j = 0; j < r; j++) {
            if (maze[i][j].id == event.data.id)
                destination = maze[i][j];               
        }
    }
    var p = this.findShortestPathAndGo(start,destination);
    this.renderMaze(p);
  };

  this.showMenu = function() {
    var content = this.content;
    var div = "<div class=\'jMenu\'>";
    $.each(content, function(index, value) {        
        div += "<div menuval=\'" + value.id + "\'>" + value.id + "</div>";
    }); 
    div += "</div>";
    $( "body" ).append(div);
    $.each(content, function(index, value) {    
        $("[menuval='" + value.id + "']").on(
            "click", //event
            {id: value.id}, //data
            **this.goTo** //handler         
        );
    }); 

  };
};

When I call showMenu everything works fine, and the block near the end does what expected binding the click on DOM objects with attribute menuval with the right value. BUT when i click, and the handler is called, Chrome debug says this

enter image description here

locating the error in jquery.min.js:3.

Actually, it never gets into goTo function.

Someone around may know why?

Thanks in advance.

Upvotes: 0

Views: 127

Answers (2)

Pietro
Pietro

Reputation: 101

Answer

First, you need to keep a reference to the JMaze object if you want to use it inside the callback, like this:

var that = this;

Then, you need to ensure JMaze.goTo has the appropriate context, meaning this will be a reference to the JMaze object, you can do it by using the jQuery.proxy function, like this:

$.proxy(that.goTo, that);

So, to put it together:

this.showMenu = function() {
    var that = this;
    var content = this.content;
    var div = "<div class=\'jMenu\'>";
    $.each(content, function(index, value) {
        div += "<div menuval=\'" + value.id + "\'>" + value.id + "</div>";
    }); 
    div += "</div>";
    $( "body" ).append(div);
    $.each(content, function(index, value) {
        $("[menuval='" + value.id + "']").on(
            "click", //event
            {id: value.id}, //data
            $.proxy(that.goTo, that) //handler
        );
    }); 
};

You can see it working here. I added some basic data just to test it.

Suggestion

I highly suggest you to take advantage of jQuery's way of creating new DOM elements on the fly (docs) and rewrite the entire function like this:

this.showMenu = function () {
    var that = this;
    var div = $('<div/>').addClass('jMenu');

    $.each(this.content, function (index, value) {
        $('<div/>')
            .attr('menuval', value.id)
            .text(value.id)
            .appendTo(div)
            .on('click', { id: value.id }, $.proxy(that.goTo, that));
    });

    $('body').append(div);
};

Upvotes: 2

Barmar
Barmar

Reputation: 782158

You can't pass this.goTo as a value. Binding a method to an object only works when you actually call the function, e.g. this.goTo() , not when you just refer to it as a value.

Also, inside $.each(), this is the current element of the iteration, it's not the JMaze object.

Try:

var self = this;
$.each(content, function(index, value) {    
    $("[menuval='" + value.id + "']").on(
        "click", //event
        {id: value.id}, //data
        function() {self.goTo();} //handler         
    );
}); 

Upvotes: 0

Related Questions