DOM Recipes: Creating exit intent popups

Join the AI Workshop to learn more about AI and how it can be applied to web development. Next cohort February 1st, 2026

The AI-first Web Development BOOTCAMP cohort starts February 24th, 2026. 10 weeks of intensive training and hands-on projects.


You know those popups that appear when you try to close a browser window? They somehow know you’re trying to close it, like if they can read your mind.

In reality it’s a pretty simple concept: you have to listen to a specific DOM event.

Here’s the HTML:

<!doctype html>
<head>
  <title>Popup page</title>
</head>
<body>
  <div id="popup">
    <h3>Popup!</h3>
  </div>
</body>

Add this CSS:

body {
  font-family: system-ui;
  background-color: #f6d198;
}

#popup {
  position: fixed;
  width: 100%;
  visibility: hidden;
  z-index: 10002;
  top: 0;
  opacity: 0;
  transform: scale(0.5);
  transition: transform 0.2s, opacity 0.2s, visibility 0s 0.2s;
  position: relative;
  margin: 0 auto;
  text-align: center;
  box-shadow: 0 1px 10px rgba(0, 0, 0, 0.5);
  width: 60%;
  background: #862a5c;
  padding-bottom: 100px;
  padding-top: 50px;
  color: #fff;
  font-size: 2rem;
}

And this JavaScript:

const show = () => {
  const element = document.querySelector("#popup")
  element.style.visibility = "visible"
  element.style.opacity = "1"
  element.style.transform = "scale(1)"
  element.style.transition = "0.4s, opacity 0.4s"
}

document.addEventListener("DOMContentLoaded", () => {
  document.addEventListener("mouseout", (event) => {
    if (!event.toElement && !event.relatedTarget) {
      setTimeout(() => {
        show()
      }, 1000)
    }
  })
})

This listens on the mouseout event and waits 1 second before showing the popup.

Closing the popup

You can close the popup if the user presses the esc key:

const hide = () => {
  const element = document.querySelector("#popup")
  element.style.visibility = "hidden"
  element.style.opacity = "0"
  element.style.transform = "scale(0.5)"
  element.style.transition = "0.2s, opacity 0.2s, visibility 0s 0.2s"
}

document.addEventListener("DOMContentLoaded", () => {
  document.onkeydown = event => {
    event = event || window.event
    if (event.keyCode === 27) {
      hide()
    }
  }
})

And if the user clicks outside the modal:

let eventListener

//inside show()
eventListener = document.addEventListener("click", function (clickEvent) {
  let el = clickEvent.target
  let inPopup = false
  if (el.id === 'popup') {
    inPopup = true
  }
  while (el = el.parentNode) { 
    if (el.id == "popup") {
      inPopup = true
    }
  }
  if (!inPopup) hide()
})

//inside hide()
if (eventListener) {
  document.removeEventListener(eventListener)
}

Show only once with cookies

Store the fact we showed the modal in a cookie, so we only show it once to every person:

let showed = false;

const show = () => {
  if (showed) return;

  if (
    document.cookie.split(";").filter((item) => {
      return item.includes("popup=");
    }).length
  ) {
    return;
  } else {
    console.log(
      document.cookie.split(";").filter((item) => {
        return item.includes("popup=")
      }).length
    )
    console.log(document.cookie.split(";"))
  }

  document.cookie = "popup=true;path=/;max-age=15768000"
  showed = true

  //...the remaining part of show()

Lessons in this unit:

0: Introduction
1: Hiding and showing elements
2: Check if element is descendant
3: Change colors/styles dynamically
4: DOM timing and when elements are ready
5: stopPropagation vs preventDefault
6: How to detect adblockers
7: ▶︎ Creating exit intent popups