susmodal
Version:
Simple library for displaying modals
181 lines (137 loc) • 5.35 kB
JavaScript
/**
* SUSModal is used for generating modal window with functionnal and style settings.
*/
class SUSModal {
/**
* Constructor.
*
* @param {*} elements
*/
constructor(elements = {}) {
elements = Object.assign({}, {
id: null,
animation: "fade",
animationDuration: 400,
width: "400px",
height: "auto",
backgroundDark: false,
finalPosition: "auto",
}, elements);
// Properties
this.id = elements.id;
this.animation = elements.animation;
this.animationDuration = elements.animationDuration;
this.width = elements.width;
this.height = elements.height;
this.finalPosition = elements.finalPosition;
this.backgroundDark = elements.backgroundDark;
// The modal and its content
this.SUSModal = document.getElementById(this.id);
this.SUSModalContent = this.SUSModal.querySelector("#susmodal-content");
// Declaration of focusables elements (for navigation with keyboard).
this.SUSModalShow = false;
this.focusElementsType = "button, input, a, textarea";
this.focusElements = Array.from(this.SUSModalContent.querySelectorAll(this.focusElementsType));
// Apply the style configuration to the modal content and the body.
this.stylize();
// For closing the modal
if(this.SUSModal.querySelectorAll(".susmodal-close-btn").length > 0) {
let closeBtnList = Array.from(this.SUSModal.querySelectorAll(".susmodal-close-btn"));
closeBtnList.forEach(item => {
item.addEventListener("click", () => {
this.hide();
});
})
}
}
/**
* Show the modal.
*/
show() {
this.SUSModal.style.display = "block";
// focus on the first element for navigation with keyboard
this.focusElements[0].focus();
this.SUSModal.removeAttribute('aria-hidden');
this.SUSModal.setAttribute('aria-modal', 'true');
// if modal has been opened at least once.
if(this.SUSModalContent.classList.contains("susmodal-content-reverse")) {
this.SUSModalContent.classList.remove("susmodal-content-reverse");
if(this.backgroundDark)
document.body.classList.remove("susmodal-body-animation-reverse")
}
// add animations.
this.SUSModalContent.classList.add("susmodal-animation-" + this.animation);
if(this.backgroundDark)
document.body.classList.add("susmodal-body-animation");
window.onkeydown = event => {
this.keydownEvent(event);
};
window.onclick = event => {
if(event.target === this.SUSModal)
this.hide();
};
}
/**
* Hide the modal.
*/
hide() {
event.preventDefault();
this.SUSModal.setAttribute('aria-hidden', 'true');
this.SUSModal.removeAttribute('aria-modal');
this.SUSModalContent.classList.remove("susmodal-content-animation");
this.SUSModalContent.classList.add("susmodal-content-reverse");
if(this.backgroundDark)
document.body.classList.add("susmodal-body-animation-reverse");
// Wait the end of the reverse animations for completely hide
setTimeout(() => {
this.SUSModal.style.display = "none";
}, this.animationDuration);
window.onkeydown = undefined;
window.onclick = undefined;
}
/**
* Used to manage the navigation with keyboard.
*
* @param {*} event
*/
catchFocus(event) {
event.preventDefault();
let indexSUSmodal = this.focusElements.findIndex(f => f === this.SUSModalContent.querySelector(":focus"));
if(event.shiftKey === true) {
indexsusmodal--;
} else {
indexSUSmodal++;
}
if(indexSUSmodal >= this.focusElements.length)
indexSUSmodal = 0;
if(indexSUSmodal < 0)
indexSUSmodal = this.focusElements.length - 1;
this.focusElements[indexSUSmodal].focus();
}
/**
* Used to manage the keydown events.
*
* @param {*} event
*/
keydownEvent(event) {
if(this.SUSModal && event.key === "Escape" || event.key === "Esc")
this.hide();
if(this.SUSModal && event.key === "Tab")
this.catchFocus(event);
}
/**
* Used to manage the style
*/
stylize() {
let SUSmodalStyleCss = "margin:"+this.finalPosition+";";
if(this.width)
SUSmodalStyleCss += "width:"+this.width+";";
if(this.height)
SUSmodalStyleCss += "height:"+this.height+";";
if(this.animationDuration)
SUSmodalStyleCss += "animation-duration:"+this.animationDuration+"ms;";
this.SUSModalContent.style.cssText = SUSmodalStyleCss;
if(this.backgroundDark)
document.body.style.cssText = "animation-duration:"+this.animationDuration+"ms;";
}
}