JS to open window

That’s actually not real windows but regular HTML elements that are absolutely (or sometimes fixed) positioned on the page; a basic example might look like this:

<a href="#" data-open-modal>click here</a>

<div class="modal-overlay">
  <div class="modal-container">
    <div class="modal-header">
      Lorem ipsum
      <a href="#" class="modal-close">&times;</a>
    </div>
    <div class="modal-content">
      Lorem ipsum dolor sit amet, consectetur adipisicing elit. Voluptatem repellat, modi totam. Recusandae a culpa dolor, placeat veritatis sed qui dignissimos, non optio provident voluptatem, sunt dolores. Perspiciatis, esse natus.
    </div>
  </div>
</div>

And the CSS to make it look like a window:

.modal-overlay {
  display: flex;
  justify-content: center;
  align-items: center;
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  background-color: rgba(0, 0, 0, .5);
}

.modal-container {
  flex-basis: 50%;
  padding: 1rem;
  background-color: #fff;
  border-radius: 3px;
}

.modal-header {
  display: flex;
  font-weight: bold;
}

.modal-close {
  margin-left: auto;
  color: inherit;
  text-decoration: none;
  margin-top: -.5rem;
  font-size: 2rem;
}

Of course it should be hidden initially though… so mutatis mutandis:

.modal-overlay {
  display: none;
  /* ... */
}

.modal-overlay.active {
  display: flex;
}

You then need some JS to show it when clicking a certain link, and hide it when clicking the close button or the overlay:

var openModal = document.querySelector('[data-open-modal]')
var closeModal = document.querySelector('.modal-close')
var modalOverlay = document.querySelector('.modal-overlay')

var handleOpenModal = function (event) {
  event.preventDefault()
  modalOverlay.classList.add('active')
}

var handleCloseModal = function (event) {
  if (event.target !== event.currentTarget) {
    // Only hide the modal when clicking elements the handler
    // got actually attached to, not others from which the
    // event bubbles up to the current target (e.g. from the 
    // modal content)
    return;
  }

  event.preventDefault()
  modalOverlay.classList.remove('active')
}

openModal.addEventListener('click', handleOpenModal)
closeModal.addEventListener('click', handleCloseModal)
modalOverlay.addEventListener('click', handleCloseModal)

The animation part can be done with a CSS transition: opacity (say); however transitions won’t work when simultaneously changing the display property, and conversely we have to wait until the fading out animation finished until we can change it back to display: none. The solution is splitting that in two different classes (again mutatis mutandis):

.modal-overlay {
  display: none;
  opacity: 0;
  transition: opacity .2s ease;
}

.modal-overlay.active {
  display: flex;
}

.modal-overlay.visible {
  opacity: 1;
}

And the JS needs to be adjusted to add / remove the corresponding classes at the appropriate times:

var handleShowModal = function (event) {
  modalOverlay.classList.add('visible')
}

var handleHideModal = function (event) {
  modalOverlay.classList.remove('active')
}

var handleOpenModal = function (event) {
  event.preventDefault()
  modalOverlay.classList.add('active')
  
  // Wait a tick to kickoff the animation
  window.setTimeout(handleShowModal)
}

var handleCloseModal = function (event) {
  if (event.target !== event.currentTarget) {
    return;
  }

  event.preventDefault()
  modalOverlay.classList.remove('visible')
  
  // Hide the modal when the fading out is finished
  modalOverlay.addEventListener(
    'transitionend',
    handleHideModal, 
    { once: true, passive: true }
  )
}

Here’s a fiddle putting it all together.

4 Likes