Get Our Newsletter

Internet
Science
with Kirk M.
(@saricden)

Responsive Modals in JavaScript & CSS

If you'd prefer, there is a video version of this tutorial available:

Today we’re going to learn how to create custom modals for our web projects. Modals are an excellent tool for your UI/UX toolkit.

Step 1: Build out your Markup

To start, let’s write out some HTML.

<button data-openmodal="cool-modal1">Click Me!</button>

<div data-modal="cool-modal1">
  <div>
    <header>
      <span>Cool Modal 1!</span>
      <button>Close</button>
    </header>
    <p>
      Hello from our modal!
    </p>
  </div>
</div>

Pretty slick right? The data-openmodal attribute is going to be used to specify which modal to open when that element is clicked. The data-modal attribute is a unique identifier for our modal.

Step 2: Style Style Style

Normally in my tutorials, I focus on function over form. In this one however, I’m going to give you some CSS to make your modal look pretty as well as work.

:root {
  font-size: 10px;
  font-family: sans-serif;
}

body.noscroll {
  overflow-y: hidden;
}

[data-modal] {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.75);
  padding: 2rem;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  overflow-y: scroll;
  
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.5s, visibility 0.5s;
}

[data-modal].open {
  opacity: 1;
  visibility: visible;
}

[data-modal] > div {
  width: 100%;
  max-width: 50rem;
  padding: 2rem;
  box-sizing: border-box;
  border-radius: 2rem;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
  background-color: #FFF;

  transform: scale(0);
  transition: transform 0.5s;
}

[data-modal].open > div {
  transform: scale(1);
}

[data-modal] > div header {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
}

[data-modal] > div header button {
  font-size: 1.25rem;
  color: blue;
  border: 0;
  background-color: transparent;
  outline: none;
}

[data-modal] > div header span {
  font-size: 2.5rem;
}

[data-modal] > div p {
  font-size: 1.8rem;
}

The important takeaways of the above code are:

We’re adding a class to the <body> that we will later use to disable scrolling on the page.

We’re setting the modal wrapper to postition: fixed so that it “sticks” to the screen, no matter where the user has scrolled. We’re also setting overflow-y: scroll so that if the modal has a lot of content and spills off-screen (particularly on mobile devices), the user will be able to scroll the modal.

We’re adding transition properties to animate the modal opening and closing. These fallback gracefully on old browsers.

And

We’re toggling the visibility and opacity properties on the wrapping <div> so that it will be hidden when the modal is closed, but won’t prevent the user from clicking other elements on the page.

Now, onwards!

Step 3: A Dab of JavaScript Magic

This is where the rubber meets the pavement, so to speak. We’re now going to write the script that will be responsible for selecting the correct elements and binding events to them that toggle classes and trigger our transitions specified above.

// Grab our trigger buttons (or links or whatever you apply the class to)
var modalBtns = document.querySelectorAll('[data-openmodal]');

// Grab our divs designated as modals
var modals = document.querySelectorAll('[data-modal]');

console.log(modalBtns);

// Loop through our buttons
for (var b in modalBtns) {
  // Make sure we're looking at a DOM element
  if (modalBtns[b] instanceof Element || modalBtns[b] instanceof HTMLDocument) {
    // Bind click events to each of them
    modalBtns[b].addEventListener('click', function(e) {
      // Prevent the default operation (opening a link for example)
      e.preventDefault();

      // When clicked, read the modal it's referencing
      var targetModalName = e.target.getAttribute('data-openmodal');
      
      // Fetch the element it's referring to
      var targetModal = document.querySelector('[data-modal="'+targetModalName+'"]');

      // Add the open class to that modal
      targetModal.classList.add('open');

      // Disable scrolling on the body
      document.body.classList.add('noscroll');
    });
  }
}

// Loop through our modals
for (var m in modals) {
  // Make sure we're looking at a DOM element
  if (modals[m] instanceof Element || modals[m] instanceof HTMLDocument) {
    // Bind click events to each modal's close button
    modals[m].querySelector('header button').addEventListener('click', function(e) {
      // Remove the open class from the parent container
      e.target.parentNode.parentNode.parentNode.classList.remove('open');

      // Re-enable scrolling on the body
      document.body.classList.remove('noscroll');
    });
  }
}

And there ye have it. Throw all this together on a page and you should have a working, fully pretty modal to play with.

A neat aside about the process above is that once the JavaScript and CSS are implemented, you can reuse the data-openmodal and data-modal attributes to make as many modals as you’d like on the page. They will automatically have the correct events and styles applied.

Thanks for reading my post, if you found any of this useful, please consider sharing with a friend or collegue.

As always, happy hacking!

Love Kirk M. (@saricden)


Header Image courtesy of Irvan Smith via Unsplash

Start typing to find blog posts...