2

Below I have a simple Modal Dialog script I am working on. It uses Bootstrap Modal for the CSS side of it.

my goal was to make a simple Modal Dialog that will show and require an action from the user (ok or cancel button click). I wanted to make it flexible for use in future projects by allowing some Callback functions to be passed in and called when these click events take place. Also some other options like to show the cancel button or not. (I haven't added that portion in yet)

So what I am doing is making the function accept an Object of options/setting. I also have Default options/setting which the User options then merge into, allowing for user options to be optional and over-ride the defaults if they are present.

I know there are several ways to do this but this is the method I have seen in a few projects I have studied from JS Developers I respect.

So below is my JavaScript code, a JSFiddle page with the code, and a JSFiddle Output page to just see the result.

In the code halfway down there is a comment // PROBLEM AREA DOES NOT CALL THE CALLBACK FUNCTION

In this area I have 3 different things i have tried so far...

this.options.cancelCallback(); // This is what I wanted to work!
zPanel.dialog.confirm.options.cancelCallback(); Tried this 2nd
options.cancelCallback(); // This Works if there is a user defined callback but errors when not

So my problem in that area right now is when I call this.options.cancelCallback() which SHOULD hold the callback function, if user passed one in it should of been re-assigned to this property, if user did not, it should at least see an empty default one.

I think the this part is messed up since it is inside the onclick function..which by the way does not look like a normal click event to me, I saw it in another project and it "worked"

So the error message I get is Uncaught TypeError: Cannot call method 'okCallback' of undefined

Can someone show me how to get around this issue or improve this? Thank you.

JSFiddle http://jsfiddle.net/jasondavis/dymn5/

Full screen preview http://jsfiddle.net/jasondavis/dymn5/show/result/

var zPanel = {

    dialog: {

        confirm: function (options) {

            var i;

            // Default options
            this.options = {
                message: "",                    // String: Default Message
                cancelButton: true,             // Boolean: Show Cancel Button
                okButton: true,                 // Boolean: Show Ok Button
                cancelCallback: function () {}, // Function: Callback function when Cancel button clicked
                okCallback: function () {}      // Function: Callback function when Ok button clicked
            };
            // Merge User defined options
            for(i in options) {
                if(i in this.options) {
                    this.options[i] = options[i];
                } else {
                    throw new Error("Notice doesn't support option: " + i);
                }
            }

            var container = document.createElement('div');
            //template for modal window
            container.innerHTML += '<div class="modal-content confirm">' +
                '<div class="modal-body">' +
                '<div>' + this.options.message + '</div>' +
                '<div class="controls">' +
                '<button type="button" class="btn primary">OK</button>' +
                '<button type="button" class="btn">Cancel</button>' +
                '</div>' +
                '</div>' +
                '</div>';

            //modal window
            var modal = container.firstChild;
            container = document.createElement('div');
            container.innerHTML = '<div class="modal-backdrop fade in"></div>';
            //dark background
            var background = container.firstChild;

            //Find OK button
            var ok = modal.getElementsByTagName('button')[0];
            ok.onclick = function () {
                modal.parentNode.removeChild(modal);
                document.body.removeChild(background);

                // PROBLEM AREA DOES NOT CALL THE CALLBACK FUNCTION
                this.options.okCallback();
                //zPanel.dialog.confirm.options.okCallback();
                //options.okCallback(); // Works if there is a user defined callback
            }

            //Find Cancel button
            var cancel = modal.getElementsByTagName('button')[1];
            cancel.onclick = function () {
                modal.parentNode.removeChild(modal);
                document.body.removeChild(background);

                // PROBLEM AREA DOES NOT CALL THE CALLBACK FUNCTION
                this.options.cancelCallback();
                //zPanel.dialog.confirm.options.cancelCallback();
                //options.cancelCallback(); // Works if there is a user defined callback
            }

            document.body.appendChild(background);
            document.body.appendChild(modal);
        }

    }

}

// Create a Dialog on Click event
$('#delete_btn').click(function () {
    zPanel.dialog.confirm({
        message: 'Are you sure you want to delete action?',
        cancelCallback: function(){alert('user pressed Cancel button')},
        okCallback: function () {alert('id deleted')},
    });
});
3
  • Inside the confirm method, add this line: var self = this;. In your click handlers, use self.options.cancelCallback(); Commented Apr 13, 2013 at 0:15
  • 1
    I am a web developer of 13 years that almost sounded like you were 13 years old :) Commented Apr 13, 2013 at 0:16
  • @Ejay that's about equivalent to my JS knowledge it sometimes seems Commented Apr 13, 2013 at 0:21

2 Answers 2

6

The context of this changes inside a function, especially event handlers. This is also a common problem when using setTimeout.

this will refer to the element that the event occurred in. A way to overcome this is to store the value of this outside the handler in a new variable in the container function, then reference that variable in the handler.

So add something like this in your confirm method:

var self = this;

And in your handlers, use:

self.options.cancelCallback();

DEMO: http://jsfiddle.net/dymn5/3/

And the way to provide the passed callback with the context of the handler, you could use this:

self.options.cancelCallback.apply(this, arguments);

Such that the cancelCallback's value of this will be the element the event occurs in, and will have any arguments passed to it, like the event object.

This allows you to use this for your options:

.confirm({
    cancelCallback: function (e) {
        // `this` refers to the button
        // `e` refers to the event
    }
});
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks that's what I was thinking the problem was but had no idea how to solve it! Also for the other helpful info
1

In javascript, this always refers to the "owner" of a function.

// example 1: function without an owner
var displayA = function() { console.log(this.a); }
displayA(); // undefined

// example 2: object with a function member
var obj = {
    a: 1,
    displayA: displayA
}

obj.displayA(); // logs "1"
// note that obj.displayA is the exact same function as displayA. But when it is called, obj is the owner of the function.

In the first example, displayA has no "owner". When a function has no owner, this is automatically assumed to be the global object (window in browsers).

In the second example, the owner of displayA is obj. In this case, this will refer to obj and thus obj.a (i.e. value 1) will be displayed.

Common methods to solve this problem include:

// storing `this` in a variable, when a callback function needs a reference to this
var self = this;
window.onload = function() { 
    // I can use self instead of this!
    self.doStuff();
}

// using Function#call or Function#apply
// let's say I want to use the "obj" variable as the "this" reference in a function:
function myFunction(a, b) { this.doStuff(a, b); }
myFunction.call(obj, a, b); // after obj, pass normal function arguments
myFunction.apply(obj, [a, b]); // after obj, pass function arguments in an array

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.