🚀 how to Create a Modal Component without Javascript Framework
Published: 2023-09-18 12:04:44

create a Modal component with plain vanilla JavaScript (Without React or Vue Framework), you can follow these steps:

## Create an HTML file and include the following HTML structure for the modal:

```html
<div class="vanilla-modal">
  <div class="modal">
    <div class="modal-inner">
      <div id="modal-content"></div>
    </div>
  </div>
</div>
```
## Create a CSS file and add the provided styles for the modal.

```css
.modal{
    display: block;
    position: fixed;
    content: "";
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(0, 0, 0, 0.6);
    z-index: -1;
    opacity: 0;
    transition: opacity 0.2s, z-index 0s 0.2s;
    text-align: center;
    white-space: nowrap;
    -webkit-overflow-scrolling: touch;
}

 .modal > * {
    display: inline-block;
    white-space: normal;
    vertical-align: middle;
    text-align: left;
}

 .modal:before {
    display: inline-block;
    overflow: hidden;
    width: 0;
    height: 100%;
    vertical-align: middle;
    content: "";
}

.modal-visible .modal {
    z-index: 9999;
    opacity: 1;
    transition: opacity 0.2s;
}

.modal-inner {
    position: relative;
    overflow: hidden;
    max-width: 90%;
    max-height: 90%;
    background: #fff;
    z-index: -1;
    opacity: 0;
    transform: scale(0);
    transition: opacity 0.2s, transform 0.2s, z-index 0s 0.2s;
    min-width: 500px;
    border-radius: 6px;
}
.modal-visible .modal-inner {
    z-index: 100;
    opacity: 1;
    transform: scale(1);
    transition: opacity 0.2s, transform 0.2s;
}

#modal-content{
    padding: 50px 70px;
}
```

## Create Vanilla Plain JavaScript Component

This code allows you to initialize the modal, open it, and close it. The modal will listen for clicks outside the modal, clicks on the close button, pressing the Escape key, and handling tab navigation inside the modal.

```javascript
function outsideClick(e) {
  if (e.target.closest(".modal-inner")) {
    return;
  }
  const modalVisible = document.querySelector(".modal-visible");
  if (modalVisible) {
    closeModal();
  }
}
function escKey(e) {
  if (e.keyCode == 27) {
    closeModal();
  }
}

function closeClick(e) {
  if (e.target.classList.contains("closeModal")) {
    closeModal();
  }
}
function trapTabKey(e) {
  const vanillaModal = document.querySelector(".vanilla-modal");
  const FOCUSABLE_ELEMENTS = [
    "a[href]",
    "area[href]",
    'input:not([disabled]):not([type="hidden"]):not([aria-hidden])',
    "select:not([disabled]):not([aria-hidden])",
    "textarea:not([disabled]):not([aria-hidden])",
    "button:not([disabled]):not([aria-hidden])",
    "iframe",
    "object",
    "embed",
    "[contenteditable]",
    '[tabindex]:not([tabindex^="-"])',
  ];

  const nodes = vanillaModal.querySelectorAll(FOCUSABLE_ELEMENTS);
  let focusableNodes = Array(...nodes);

  if (focusableNodes.length === 0) return;

  focusableNodes = focusableNodes.filter((node) => {
    return node.offsetParent !== null;
  });

  // if disableFocus is true
  if (!vanillaModal.contains(document.activeElement)) {
    focusableNodes[0].focus();
  } else {
    const focusedItemIndex = focusableNodes.indexOf(document.activeElement);

    if (e.shiftKey && focusedItemIndex === 0) {
      focusableNodes[focusableNodes.length - 1].focus();
      e.preventDefault();
    }

    if (
      !e.shiftKey &&
      focusableNodes.length > 0 &&
      focusedItemIndex === focusableNodes.length - 1
    ) {
      focusableNodes[0].focus();
      e.preventDefault();
    }
  }
}

function closeModal() {
  const vanillaModal = document.querySelector(".vanilla-modal");
  if (vanillaModal) {
    vanillaModal.classList.remove("modal-visible");
    document.getElementById("modal-content").innerHTML = "";
    document.getElementById("modal-content").style = "";
  }

  document.removeEventListener("keydown", escKey);
  document.removeEventListener("click", outsideClick, true);
  document.removeEventListener("click", closeClick);
  document.removeEventListener("keydown", trapTabKey);
}

const modal = {
  init: function () {
    const prerendredModal = document.createElement("div");
    prerendredModal.classList.add("vanilla-modal");
    const htmlModal = `         
       <div class="modal">
       <div class="modal-inner"
       ><div id="modal-content"></div></div></div>`;
    prerendredModal.innerHTML = htmlModal;
    document.body.appendChild(prerendredModal);
  },
  open: function (idContent, option = { default: null }) {
    let vanillaModal = document.querySelector(".vanilla-modal");
    if (!vanillaModal) {
      console.log("there is no vanilla modal class");
      modal.init();
      vanillaModal = document.querySelector(".vanilla-modal");
    }

    const content = document.getElementById(idContent);
    let currentModalContent = content.cloneNode(true);
    currentModalContent.classList.add("current-modal");
    currentModalContent.style = "";
    document.getElementById("modal-content").appendChild(currentModalContent);

    if (!option.default) {
      if (option.width && option.height) {
        document.getElementById("modal-content").style.width = option.width;
        document.getElementById("modal-content").style.height = option.height;
      }
    }
    vanillaModal.classList.add("modal-visible");
    document.addEventListener("click", outsideClick, true);
    document.addEventListener("keydown", escKey);
    document.addEventListener("keydown", trapTabKey);
    document
      .getElementById("modal-content")
      .addEventListener("click", closeClick);
  },

  close: function () {
    closeModal();
  },
};

// for webpack es6 use uncomment the next line
// export default modal;
```
1. The `outsideClick` function is responsible for handling clicks outside the modal. If the click target is not inside the modal, it triggers the `closeModal` function to close the modal.

2. The `escKey` function checks if the Escape key was pressed (keyCode 27), and if so, it calls the `closeModal` function.

3. The `closeClick` function listens for a click event on the close button within the modal. If the click target has the class "closeModal", it triggers the `closeModal` function.

4. The `trapTabKey` function is used to trap keyboard focus within the modal. It first retrieves all the focusable elements inside the modal based on the provided array `FOCUSABLE_ELEMENTS`. Then it filters out any elements that are not currently visible. 

   - If there are no focusable elements, it simply returns.
   
   - If the modal does not currently contain focus (meaning no element inside the modal is currently focused), it sets the focus on the first focusable element.
   
   - If the modal currently contains focus, it handles the tab navigation logic: 
     - If the Shift key is pressed along with Tab, it moves focus to the last focusable element and prevents the default tab behavior.
     - If only the Tab key is pressed, and the currently focused element is the last focusable element, it moves focus back to the first element and prevents the default tab behavior.

5. The `closeModal` function is responsible for closing the modal. It finds the modal element and removes the classes and content related to the visibility of the modal. It also removes the event listeners for keydown, clicks outside the modal, and clicks on the close button.

6. The `modal` object has three methods:
   - `init()`: This method is responsible for initializing the modal. It creates the HTML structure of the modal and appends it to the document body.
   - `open(idContent, option)`: This method is used to open the modal. It takes two parameters:
     - `idContent` (required): The ID of the element that contains the content to be shown in the modal.
     - `option` (optional): An object that allows specifying additional options. Currently, it supports setting the width and height of the modal by providing the `width` and `height` properties in the `option` object.
   - `close()`: This method simply calls the `closeModal` function to close the modal.

To use the `modal` object, you can call the `open` method with the ID of the content you want to show in the modal. For example:

```javascript
modal.open("your-modal-content-id");
```

You can also provide optional options to adjust the width and height of the modal:

```javascript
modal.open("your-modal-content-id", { width: "500px", height: "300px" });
```

To close the modal, simply call the `close` method:

```javascript
modal.close();
```



With these steps in place, you can now use the modal object to open and close the modal:


// Opening the modal
modal.open("your-modal-content-id");

// Closing the modal
modal.close();

Make sure to replace "your-modal-content-id" with the actual ID of your modal content.


You can modify the provided code as per your requirement or style the modal further based on your design preferences.

**Github Repository Link :**
[https://github.com/idurar/vanilla-js-modal](https://github.com/idurar/vanilla-js-modal)




```
And don't forget to star our  Open Source ERP / CRM  🤩 repo in github 🚀 ?  !
```
Github Repo :  [https://github.com/idurar/erp-crm](https://github.com/idurar/erp-crm) 

[![Open Source ERP / CRM ](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5q8hkcx90gfqlgz3qban.png)](https://github.com/idurar/erp-crm)

Idurar is a modern and open-source ERP/CRM system based on Node.js React.js that offers features such as sales management, customer management, and invoicing.