I'm trying to create a vanilla JavaScript Modal that has the capability of being customized by the User, when instantiating it from the HTML file (or JS file). However, when it comes to dealing with the close() function to close the modal, instead of closing ONE modal at a time, using its close button, the close button of the FIRST modal closes ALL modals of the page. I'm not sure what I'm doing wrong...
I've researched other similar vanilla JavaScript, customizable modal libraries, but most of them use either jQuery, some framework, or include a lot of complications that I am not familiar with (I'm still a beginner). I've researched on GitHub, CodePen, Google, and on here; but I have yet to find a solution that satisfies what I need.
Since the code is quite long, I'd suggest you go directly to my CodePen account, where you can have the full code.
https://codepen.io/jdriviere/pen/zYOyJvv?editors=0011
But here is my close() function:
Modal.prototype.close = function() {
let modal = document.getElementById(this.options.id);
let modalBody = modal.children[0];
// Delete elements from Modal Body
for (let i = 0; i < modalBody.children.length; i++) {
modalBody.removeChild(modalBody.children[i]);
} // End of LOOP
// Delete Modal Body from Modal
modal.removeChild(modalBody);
// Delete Modal from DOM
modal.style.display = 'none';
document.body.removeChild(modal);
return this;
};
I would expect the code to close ONE modal at a time, and preferably the modal that has the proper ID (which should be either assigned by the User or by default have a "NoID" ID). Instead, if I close subsequent modals, it closes them; but if I close the FIRST one, it closes ALL of them. Also, is there a way to init() the Modal functionalities as soon as you create the modal instance (I hate manually initiating them)? If so, please include your solution here too, if not much asked.
Been at it for quite some time now. Your help would be greatly appreciated.
Thank you. :)
You have couple of mistakes in your code:
n/a
for the modal that does not have id
property in their options
object. Using such id
will break the query selector when you use jQuery.init()
function twice and in each call for init()
the closeBtn
is selecting both the close buttons of two modals and assigning the click event handler to each of them twice. That was the reason when you clicked on one button the click event for another button was executing itself. So, what you can do is, only associate a click function once only to that close button of the modal for which the init()
function was called. I used let closeBtn = document.querySelector('#'+this.options.id + ' .modal-close');
to select that particular close button inside that init()
function. Overall your JS code will look like:
/**
* Blueprint function (class) that describes a Modal object.
* @param {Object} options Object parameter containing elements that describe the Modal.
* @returns {Object} options Returns options from current modal object.
*/
function Modal(options) {
// If constructor params is available
if (options) {
this.options = options;
} else {
this.options = {};
} // End of IF-ELSE
// Add to options object
if (options.id) {
// Check type of ID entry
if (typeof options.id === 'number') {
this.options.id = options.id.toString();
} else {
this.options.id = options.id;
} // End of IF-ELSE
} else if (options.id === undefined) {
this.options.id = 'NA';
} // End of IF-ELSE
if (options.name) {
this.options.name = options.name;
} // End of IF
if (options.closable) {
this.options.closable = options.closable;
} // End of IF
return this;
};
// Prototypes
/**
* Displays some information concerning the current Modal object.
* @returns {Object} this Returns current modal object.
*/
Modal.prototype.open = function() {
let demo = document.getElementById('demo');
return this;
};
/**
* Creates an instance of a Modal object with the specified object elements.
* @returns {Object} this Returns current Modal object.
*/
Modal.prototype.create = function() {
// Create Modal Element
let modal = document.createElement('div');
let modalBody = document.createElement('div');
// Create Modal
!modal.classList.contains('modal') ?
modal.classList.add('modal') :
modal.classList.add('');
modal.id = this.options.id || 'noID';
// Create modal body element
!modalBody.classList.contains('modal-body') ?
modalBody.classList.add('modal-body') :
modalBody.classList.add('');document.querySelector('#' + this.options.id + ' .modal-close');
modal.appendChild(modalBody);
// Adding modal sub-elements
if (this.options.title) {
let modalTitle = document.createElement('h2');
!modalTitle.classList.contains('modal-title') ?
modalTitle.classList.add('modal-title') :
modalTitle.classList.add('');
modalTitle.textContent = this.options.title;
modalBody.appendChild(modalTitle);
console.log('Added title!');
} // End of IF
if (this.options.subtitle) {
let modalSubtitle = document.createElement('h4');
!modalSubtitle.classList.contains('modal-subtitle') ?
modalSubtitle.classList.add('modal-subtitle') :
modalSubtitle.classList.add('');
modalSubtitle.textContent = this.options.subtitle;
modalBody.appendChild(modalSubtitle);
console.log('Added subtitle!');
} // End of IF
if (this.options.content) {
let modalContent = document.createElement('p');
!modalContent.classList.contains('modal-content') ?
modalContent.classList.add('modal-content') :
modalContent.classList.add('');
modalContent.textContent = this.options.content;
modalBody.appendChild(modalContent);
console.log('Added contents!');
} // End of IF
if (this.options.closable) {
let modalClose = document.createElement('span');
!modalClose.classList.contains('modal-close') ?
modalClose.classList.add('modal-close') :
modalClose.classList.add('');
modalClose.innerHTML = '×';
modalBody.appendChild(modalClose);
console.log('Close button added!');
} // End of IF
document.body.appendChild(modal);
console.log('Modal created with ID', modal.id);
return this;
};
/**
* Closes the current Modal object.
* @returns {Object} this Returns current Modal object.
*/
Modal.prototype.close = function() {
let modal = document.getElementById(this.options.id);
let modalBody = modal.children[0];
// Delete elements from Modal Body
for (let i = 0; i < modalBody.children.length; i++) {
modalBody.removeChild(modalBody.children[i]);
} // End of LOOP
// Delete Modal Body from Modal
modal.removeChild(modalBody);
// Delete Modal from DOM
modal.style.display = 'none';
document.body.removeChild(modal);
return this;
};
/**
* Initializes the inner functions of the modal, such as the closing capacity.
* @returns {Object} this Returns current Modal object.
*/
Modal.prototype.init = function(e) {
// let closeBtnAll = document.querySelectorAll('.modal-close');
let closeBtn = document.querySelector('#'+this.options.id + ' .modal-close');
// Assign close() function to all close buttons
closeBtn.addEventListener('click', () => {
if (this.options.closable) {
this.close();
}
})
// Press ESC to close ALL modals
return this;
};
// Create a Modal object
let modal1 = new Modal({
id: 'post1',
name: 'modal',
title: 'First Post',
subtitle: 'I contain all the elements',
content: 'This is awesome!',
closable: true
});
let modal2 = new Modal({
title: 'Second Post',
subtitle: 'Trying new things',
content: 'Hehehehehe',
closable: true
});
modal1.open();
modal1.create();
modal1.init();
modal2.open();
modal2.create();
modal2.init();
Just replace the above JS code in your codepen and try. It will work.