Reputation: 490
I'm creating a javascript function which creates a modal. Here's the function:
function createModal(options) {
var self = this;
modalHeaderText = options.header;
modalBodyText = options.body;
$modal = $('<div />').addClass('modal').appendTo('body');
$modalOverlay = $('<div />').addClass('modal-overlay').appendTo($modal);
$modalContainer = $('<div />').addClass('modal-container').appendTo($modal);
$modalHeader = $('<div />').addClass('modal-header').addClass(options.headerClass).html(modalHeaderText).appendTo($modalContainer);
$modalBody = $('<div />').addClass('modal-body').addClass(options.bodyClass).html(modalBodyText).appendTo($modalContainer);
if (options.buttons) {
$modalFooter = $('<div />').addClass('modal-footer').appendTo($modalContainer);
$.each(options.buttons, function(name, buttonOptions) {
$modalButton = $('<button />').addClass(buttonOptions.class).html(name).appendTo($modalFooter);
if(buttonOptions.callback) {
$modalButton.on('click', function() {
buttonOptions.callback();
});
} else {
$modalButton.on('click', function(e) {
$modal.remove();
});
};
});
};
$modal.addClass('active');
if (options.closeOnOverlayClick == true) {
$modalOverlay.on('click', function(e) {
$modal.remove();
});
};
};
This works fine, but I want to be able to call the function within the same function, like this:
$('#modal').on('click', function(e){
e.preventDefault();
createModal({
header : 'Enter your name',
body : '<input type="text" class="name" />',
buttons : {
'OK' : {
class : 'btn btn-success',
callback : function() {
var name = self.$modalBody.find('.name').val();
if (!name) {
createModal({
header : 'Error',
body : 'You must provide a name',
buttons : {
'OK' : {
class : 'btn'
}
}
});
} else {
alert(name);
};
},
},
'Close' : {
class : 'btn btn-error'
}
}
});
});
What I want is the following: when someone clicks the button with ID "modal" (hence "#modal"), a modal is opened with a input. When the OK-button is pressed, it checks if the input ('name') has a value. If so, the value is shown in an alert. If not, a new modal is openend (over the current modal) with the text 'You must provide a name'.
If I enter a name, it works. The name is shown in an alert, and also the close button works. But if I do not enter a name, and the second modal is shown, all the variables in the function are overwritten.
How can I preserve the variables/elements from the first modal so that, after the second modal is shown (and cleared), the buttons from the first modal still work.
I've created a JSFiddle here: https://jsfiddle.net/6pq7ce0a/2/
You can test it like this:
1) click on 'open modal' 2) enter a name 3) click on 'ok' 4) the name is shown in an alert ==> this works
The problem is here:
1) click on 'open modal' 2) do NOT enter a name 3) click on 'ok' 4) a new modal is shown 5) click on 'ok' in the new (error) modal 6) the buttons from the first modal (with the input field) don't work anymore
Thanks in advance!
If I change the function to the function below, the first modal does not work at all.
function createModal(options) {
var self = this;
var modalHeaderText = options.header;
var modalBodyText = options.body;
var $modal = $('<div />').addClass('modal').appendTo('body');
var $modalOverlay = $('<div />').addClass('modal-overlay').appendTo($modal);
var $modalContainer = $('<div />').addClass('modal-container').appendTo($modal);
var $modalHeader = $('<div />').addClass('modal-header').addClass(options.headerClass).html(modalHeaderText).appendTo($modalContainer);
var $modalBody = $('<div />').addClass('modal-body').addClass(options.bodyClass).html(modalBodyText).appendTo($modalContainer);
if (options.buttons) {
var $modalFooter = $('<div />').addClass('modal-footer').appendTo($modalContainer);
$.each(options.buttons, function(name, buttonOptions) {
var $modalButton = $('<button />').addClass(buttonOptions.class).html(name).appendTo($modalFooter);
if(buttonOptions.callback) {
$modalButton.on('click', function() {
buttonOptions.callback();
});
} else {
$modalButton.on('click', function(e) {
$modal.remove();
});
};
});
};
$modal.addClass('active');
if (options.closeOnOverlayClick == true) {
$modalOverlay.on('click', function(e) {
$modal.remove();
});
};
};
The problem is here:
var name = self.$modalBody.find('.name').val();
$modalBody is not defined if I add 'var' to all the elements.
Upvotes: 0
Views: 65
Reputation: 331
Simply not using var
in front of $modal
variable causing it to be stored in window scope. When the next next $modal is closed, the variable is referencing to an already removed element, so nothing happens on first modal's Close
button click.
Upvotes: 0
Reputation: 309
So in addition to the comments above regarding not declaring var
you also are storing a reference to window
in the self variable. To avoid all of that I went down the road in this fiddle: https://jsfiddle.net/10fanzw6/1/.
Quick explanation.
this
to self
as this
is windowself
object as well as a local var (for better readability)self
var back to any button callback giving you access to any part of the modal you may need.For posterity, including the updated function here:
function createModal(options) {
var self = {};
var modalHeaderText = options.header;
var modalBodyText = options.body;
var $modal = self.$modal = $('<div />').addClass('modal').appendTo('body');
var $modalOverlay = self.$modalOverlay = $('<div />').addClass('modal-overlay').appendTo($modal);
var $modalContainer = self.$modalContainer = $('<div />').addClass('modal-container').appendTo(self.$modal);
self.$modalHeader = $('<div />').addClass('modal-header').addClass(options.headerClass).html(modalHeaderText).appendTo($modalContainer);
self.$modalBody = $('<div />').addClass('modal-body').addClass(options.bodyClass).html(modalBodyText).appendTo($modalContainer);
if (options.buttons) {
var $modalFooter = self.$modalFooter = $('<div />').addClass('modal-footer').appendTo($modalContainer);
$.each(options.buttons, function(name, buttonOptions) {
var $modalButton = $('<button />').addClass(buttonOptions.class).html(name).appendTo($modalFooter);
if (buttonOptions.callback) {
$modalButton.on('click', function() {
buttonOptions.callback(self);
});
} else {
$modalButton.on('click', function(e) {
$modal.remove();
});
};
});
};
$modal.addClass('active');
if (options.closeOnOverlayClick == true) {
$modalOverlay.on('click', function(e) {
$modal.remove();
});
};
};
$('#modal').on('click', function(e) {
e.preventDefault();
createModal({
header: 'Enter your name',
body: '<input type="text" class="name" />',
buttons: {
'OK': {
class: 'btn btn-success',
callback: function(modal) {
var name = modal.$modalBody.find('.name').val();
if (!name) {
createModal({
header: 'Error',
body: 'You must provide a name',
buttons: {
'OK': {
class: 'btn'
}
}
});
} else {
alert(name);
};
},
},
'Close': {
class: 'btn btn-error'
}
}
});
});
Upvotes: 3