Exit Intent Popup Script and Tutorial

November 28th, 2014
Twitter Facebook Google+

An exit intent popup is an HTML window that shows dynamically when a visitor goes to close your website. Visitor mouse movements are tracked, and when their cursor moves outside the upper page boundary, the popup is triggered. This popup can be anything, but will usually consist of a call to action (sign up to a newsletter, claim a discount, etc). Exit intent popups are used to hopefully retain visitors that would otherwise be lost.

After browsing the web for awhile, it became apparent there wasn't a freely available exit intent popup script that's built for today's browsers. The few I came across were using older technology that no longer works. The code and guide on how to use them was pretty unclear as well. I decided to build one for use on my own SaaS application, and I'm making the source code for it available here.

UPDATE Nov 21st, 2015: All future updates to this script will be available to download from the buttons below and in the GitHub repository. Continually modifying this tutorial as the code changes on GitHub is pretty time consuming, so the actual tutorial for the popup script below will remain as is from this date forward. To get the latest version of this script in full, use the Download buttons below, or visit the GitHub repository.

Demo View Source Download (9.7kb) Download Minified (5.2kb) GitHub

Features

Usage

The script is written in vanilla JavaScript, so no other libraries like jQuery are needed with it. Simply include it and initialize it from within the head element on your page.

<script type="text/javascript" src="bioep.min.js"></script>

<script type="text/javascript">
    bioEp.init({
        html: '',
        css: ''
    });
</script>

An exit popup will now be created with the default settings. With no HTML or CSS set in the init options, it will just be a blank box. Below is the default structure of the popup that's created and added to your page, which consists of a background, the popup itself, and a close button.

<div id="bio_ep_bg"></div>
<div id="bio_ep">
    <div id="bio_ep_close">X</div>
    <!-- Your HTML goes here -->
</div>

The close button is absolutely positioned, so it will not affect the layout of your content inside the popup. For the popup to actually show content, you must add in your own HTML and CSS.

Adding in HTML and CSS

There are two methods for adding HTML and CSS to the popup.

Method One

The first is a pure JavaScript solution, where you set the html and css option in the bioEp.init function call.

bioEp.init({
    html: '<div id="content">Your HTML goes here</div>',
    css: '#content {font-size: 20px;}'
});

Method Two

The second method allows you to add HTML and CSS on the page itself, without using JavaScript. You can simply call the init function without the html and css options, then add in your popup styles within the head element and your HTML within a

tag as the last child of the body element.

<!DOCTYPE html>
<html>
    <head>
        <script type="text/javascript" src="bioep.min.js"></script>

        <script type="text/javascript">
            bioEp.init();
        </script>

        <style type="text/css">
            // Popup CSS goes here
        </style>
    </head>

    <body>
        <div id="bio_ep">
            // Popup HTML goes here
        </div>
    </body>
</html>

The script will automatically recognize the bio_ep element and use it as the main popup. Base styles to create the background and handle the popup are added, as well as your own custom styles. Using this method, any HTML added via method one will be ignored, but CSS will still be applied from both methods.

Templates

These templates are provided for you to use and to help illustrate how you would go about creating your own custom popup using this script. Since it's easier to implement, the first method of adding HTML and CSS is used.

To use these templates on your own site, follow the usage instructions above and replace the bioEp.init() call with the template. Then, in the html of the template, replace #YOURURLHERE with the URL you want to redirect users to when they click the popup.

Template 1

Using HTML and CSS to create the popup.

See the Pen vEOora by Conner Hewitt (@beekerio) on CodePen.

bioEp.init({
	html: '<div id="bio_ep_content">' +
		'<img src="http://beeker.io/images/posts/2/tag.png" alt="Claim your discount!" />' +
		'<span>HOLD ON!</span>' +
		'<span>Click the button below to get a special discount</span>' +
		'<span>This offer will NOT show again!</span>' +
		'<a href="#YOURURLHERE" class="bio_btn">CLAIM YOUR DISCOUNT</a>' +
		'</div>',
	css: '#bio_ep {width: 400px; height: 300px; color: #333; background-color: #fafafa; text-align: center;}' +
		'#bio_ep_content {padding: 24px 0 0 0; font-family: "Titillium Web";}' +
		'#bio_ep_content span:nth-child(2) {display: block; color: #f21b1b; font-size: 32px; font-weight: 600;}' +
		'#bio_ep_content span:nth-child(3) {display: block; font-size: 16px;}' +
		'#bio_ep_content span:nth-child(4) {display: block; margin: -5px 0 0 0; font-size: 16px; font-weight: 600;}' +
		'.bio_btn {display: inline-block; margin: 18px 0 0 0; padding: 7px; color: #fff; font-size: 14px; font-weight: 600; background-color: #70bb39; border: 1px solid #47ad0b; cursor: pointer; -webkit-appearance: none; -moz-appearance: none; border-radius: 0; text-decoration: none;}',
	fonts: ['//fonts.googleapis.com/css?family=Titillium+Web:300,400,600'],
	cookieExp: 0
});

Template 2

Using a premade image.

See the Pen ByNXVE by Conner Hewitt (@beekerio) on CodePen.

bioEp.init({
	width: 394,
	height: 298,
	html: '<a href="#YOURURLHERE" title="Claim your discount!"><img src="http://beeker.io/images/posts/2/template2.png" alt="Claim your discount!" /></a>',
	cookieExp: 0
});

Options

All options must be added to the init function as an object.

Name Type Default Description
width integer 400 The width of the popup. This can be overridden by adding your own CSS for the #bio_ep element.
height integer 220 The height of the popup. This can be overridden by adding your own CSS for the #bio_ep element.
html string blank The HTML code to be placed within the popup. HTML can be added through this function or on the page itself within a element.
css string blank The CSS styles for the popup. CSS can be added through this function or on the page itself.
fonts array null An array containing URLs that link to font stylesheets. Google Fonts was the main idea behind this feature.
delay integer 5 The time, in seconds, until the popup activates and begins watching for exit intent. If showOnDelay is set to true, this will be the time until the popup shows.
showOnDelay boolean false If true, the popup will show after the delay option time. If false, popup will show when a visitor moves their cursor above the document window, showing exit intent.
cookieExp integer 30 The number of days to set the cookie for. A cookie is used to track if the popup has already been shown to a specific visitor. If the popup has been shown, it will not show again until the cookie expires. A value of 0 will always show the popup.

For example, here's how to create a simple popup the will automatically show after 10 seconds.

bioEp.init({
    html: '<div id="content">A simple popup</div>',
    css: '#content {font-size: 20px;}',
    delay: 10,
    showOnDelay: true
});

Tutorial

Now we'll cover how to create the exit intent popup in JavaScript. The entire script is coded in vanilla JavaScript, which means other libraries like jQuery do not need to be included along with it. Using pure JavaScript also ensures our footprint will be small.

So, let's get started with the basics. Here is the gutted version of our script.

window.bioEp = {
	// Private variables
	bgEl: {},
	popupEl: {},
	closeBtnEl: {},
	shown: false,
	overflowDefault: "visible",
	transformDefault: "",
	
	// Popup options
	width: 400,
	height: 220,
	html: "",
	css: "",
	fonts: [],
	delay: 5,
	showOnDelay: false,
	cookieExp: 30,
	
	// Handle creating, reading, and deleting cookies
	cookieManager: {
		// Create a cookie
		create: function() {},
		
		// Get the value of a cookie
		get: function() {},
		
		// Delete a cookie
		erase: function() {}
	},
	
	// Check for cookie
	checkCookie: function() {},
	
	// Add font stylesheets and CSS for the popup
	addCSS: function() {},
	
	// Add the popup to the page
	addPopup: function() {},
	
	// Show the popup
	showPopup: function() {},
	
	// Hide the popup
	hidePopup: function() {},
	
	// Handle scaling the popup
	scalePopup: function() {},
	
	// Load event listeners for the popup
	loadEvents: function() {},
	
	// Set user defined options for the popup
	setOptions: function() {},

	// Ensure the DOM has loaded
	domReady: function() {}
	
	// Initialize
	init: function() {}
}

Our entire script is defined as a JavaScript object. In JavaScript, there's no true capability for creating classes. Instead, we must use an object directly as the class, or use a function which exports an object as the class. When using the first method, only one instance of the object (or class) can be and is created. In the second, multiple instances of the class can be instantiated using the new keyword. An article on phpied.com goes over this more thoroughly.

The very first part of our script,

window.bioEp = {

creates a new object named bioEp and attaches it to the window object (bioEp stands for beeker.io Exit Popup, just in case that's bothering the heck out of you). If you've played around with JavaScript before, you'll be familiar with the window object. Essentially, everything in JavaScript is an object, including window. These objects are created by the browser when loading a page, which is referred to as the DOM or Document Object Model. JavaScript interacts with the page via the DOM.

We use the period in window.bioEp to create the bioEp property in the window object, and we signify we're making it an object by using the open curly bracket {. To access or create properties of objects in JavaScript, you must use a period.

Below this, we define our private variables and options.

	// Private variables
	bgEl: {},
	popupEl: {},
	closeBtnEl: {},
	shown: false,
	overflowDefault: "visible",
	transformDefault: "",
	
	// Popup options
	width: 400,
	height: 220,
	html: "",
	css: "",
	fonts: [],
	delay: 5,
	showOnDelay: false,
	cookieExp: 30,

These variables are technically just properties of our object. To assign something to the property of an object, we use a colon : rather than an equals = symbol.

Kinda like how there's no true classes in JavaScript, there's no real way to create private variables, especially when using object literal notation. While you can define "private" variables and methods inside the constructor when using the function method to create a class, this still works differently than the standard of other languages. We're simply labeling these variables as private because only our bioEp functions will be using them.

We also set the default options for the popup here, which are used in various parts of the script. Later in the code, via the setOptions function, we'll check for any user defined options and assign them accordingly.

The rest of the code contains the various functions we'll use to create and handle the popup. Just like we can assign values and strings to the property of an object, we can also assign functions.

Let's start with the main function, init.

The init Function

	// Initialize
	init: function(opts) {
		// Once the DOM has fully loaded
		this.domReady(function() {
			// Handle options
			if(typeof opts !== 'undefined')
				bioEp.setOptions(opts);
				
			// Handle the cookie
			if(bioEp.checkCookie()) return;
			
			// Add the CSS
			bioEp.addCSS();
			
			// Add the popup
			bioEp.addPopup();
			
			// Load events
			setTimeout(function() { 
				bioEp.loadEvents();

				if(bioEp.showOnDelay)
					bioEp.showPopup();
			}, bioEp.delay * 1000);
		});
	}

This is the main function of our bioEp object. All of the other main functions in the script used to create the popup, add the CSS to the page, etc are called from within this function. It's essentially the main control for our entire script. If this function is never called, our script will never run and the popup will never show or even be placed on the page.

First, we use our domReady function to ensure that our code is only executed once the DOM has loaded.

this.domReady(function() {

Functions in our script must have access to the body element of the page. If we ran our script without waiting for the DOM to fully load, and our script is in the head element, it will run before the body element is even placed on the page. Details on the domReady function will be explained in a new post very soon (I'll link to it here).

Inside our anonymous function is where we actually run our script. We first handle any options set by the user, if there are any, via the setOptions function. Next, we check for a cookie that's set by the script to determine if the popup should show for this visitor or not. If the cookie is found and set to true, we exit out of the init function entirely by calling return.

The next two functions add the CSS and popup HTML to the DOM, which we'll cover a bit later. Finally, we use another anonymous function inside a setTimeout call to handle loading the event listeners we need to track mouse movement for exit intent and clicks on the close button of the popup. This function also checks if the popup should show based on the delay time rather than exit intent. You can see we use the delay option of our object to set the time, in seconds, for the timeout call.

The setOption Function

	// Set user defined options for the popup
	setOptions: function(opts) {
		this.width = (typeof opts.width === 'undefined') ? this.width : opts.width;
		this.height = (typeof opts.height === 'undefined') ? this.height : opts.height;
		this.html = (typeof opts.html === 'undefined') ? this.html : opts.html;
		this.css = (typeof opts.css === 'undefined') ? this.css : opts.css;
		this.fonts = (typeof opts.fonts === 'undefined') ? this.fonts : opts.fonts;
		this.delay = (typeof opts.delay === 'undefined') ? this.delay : opts.delay;
		this.showOnDelay = (typeof opts.showOnDelay === 'undefined') ? this.showOnDelay : opts.showOnDelay;
		this.cookieExp = (typeof opts.cookieExp === 'undefined') ? this.cookieExp : opts.cookieExp;
	}

We pass the options object to this function as the opts argument. For each option, we use a single line if statement known as a ternary operator to check if the option is undefined. If it's undefined, we'll keep the default setting, otherwise we'll change it to the user specified value. There's actually a few ways of setting options like this, but we're checking for undefined for a specific reason.

One popular method is to use the 'or' logical operator, ||. If we were to use this method to handle options, it would look like this.

this.width = opts.width || this.width;

The problem with this method is that if the width option for instance is set to 0, as the user defines, it will be ignored and use the default width instead. The reason for this is because 0 is "falsy", so it will return false and try to use the next value after the or operator, which is our default width. Our default width is "truthy" since it contains a value greater than 0, so it will be used instead.

The loadEvents Function

	// Load event listeners for the popup
	loadEvents: function() {
		// Track mouse movements
		document.addEventListener("mousemove", function(e) {
			// Get current scroll position
			var scroll = window.pageYOffset || document.documentElement.scrollTop;
			
			if((e.pageY - scroll) < 7)
				bioEp.showPopup();
		});
		
		// Handle the popup close button
		this.closeBtnEl.addEventListener("click", function() {
			bioEp.hidePopup();
		});
		
		// Handle window resizing
		window.addEventListener("resize", function() {
			bioEp.scalePopup();
		});
	}

Event listeners allow us to watch for certain actions and changes on the page. The first event listener, which we attach to the document object (since we want to track this event across the entire page), is mousemove. Whenever the user moves their mouse on the page, our anonymous function will be called. Inside this function, we check for exit intent by seeing if the visitor's mouse is less than 7 pixels from the top of the document (the Y axis scroll position is taken into account in this calculation as well). If it is, this shows exit intent, so we call the function to show the popup.

You can also see we have an argument for the anonymous function, e. When the event is triggered and our anonymous function is called, an event object is passed to our anonymous function as the first argument. We use the data in this object to get the mouse position on the page.

For handling the close button, we assign an event listener to the click event, attached to the close button element. If the close button is clicked, we call the function to hide the popup.

The last event listener we assign is to the window itself, and we check for any viewport dimension changes (the viewable area within the browser) by listening for the resize event. If the window is resized, we run the function to scale the popup.

The scalePopup Function

	// Handle scaling the popup
	scalePopup: function() {
		var margins = { width: 40, height: 40 };
		var popupSize = { width: bioEp.popupEl.offsetWidth, height: bioEp.popupEl.offsetHeight };
		var windowSize = { width: window.innerWidth, height: window.innerHeight };
		var newSize = { width: 0, height: 0 };
		var aspectRatio = popupSize.width / popupSize.height;
		
		// First go by width, if the popup is larger than the window, scale it
		if(popupSize.width > (windowSize.width - margins.width)) {
			newSize.width = windowSize.width - margins.width;
			newSize.height = newSize.width / aspectRatio;
			
			// If the height is still too big, scale again
			if(newSize.height > (windowSize.height - margins.height)) {
				newSize.height = windowSize.height - margins.height;
				newSize.width = newSize.height * aspectRatio;
			}
		}
		
		// If width is fine, check for height
		if(newSize.height === 0) {
			if(popupSize.height > (windowSize.height - margins.height)) {
				newSize.height = windowSize.height - margins.height;
				newSize.width = newSize.height * aspectRatio;
			}
		}
		
		// Set the scale amount
		var scaleTo = newSize.width / popupSize.width;
		
		// If the scale ratio is 0 or is going to enlarge (over 1) set it to 1
		if(scaleTo <= 0 || scaleTo > 1) scaleTo = 1;
		
		// Save current transform style
		if(this.transformDefault === "") 
			this.transformDefault = window.getComputedStyle(this.popupEl, null).getPropertyValue("transform");
			
		// Apply the scale transformation
		this.popupEl.style.transform = this.transformDefault + " scale(" + scaleTo + ")";
	}

Easily the heaviest function code wise is the scalePopup function. We use it to automatically scale the size of the popup so it will fit within the browser's viewable area if the window is resized. CSS3 spec offers a handy function within the transform property named scale which allows us to do this. It's actually a really cool function, and everything inside of the popup, all text, images, etc, is scaled to size.

We first set all of the size information we'll need. The margins that we set are taken into account with the window size, so when the popup is scaled, there will be space between it and the window. The popupSize and windowSize variables store the popup size and viewable window size in pixels, and the newSize variable will store the size the popup should be scaled to, also in pixels. In order to keep the new width and height proportional, we also get the aspect ratio of the popup, stored in the aspectRatio variable.

Now we get into actually scaling the popup. First we check the width of the popup vs the width of the viewable window and margin. If the popup is larger, we scale the width down to the proper size in pixels, then apply the proportional height according to the aspect ratio. If the height is still too big after the scale based on width, then we scale it again based on the height.

If we checked the width of the popup vs the window and it fit, we must next check the height and make sure that fits as well. If the height doesn't fit, we scale it.

The scale function of the transform property takes a multiplier value, meaning if we set it to 1, the size won't change at all, and if we set it to 0.5, the size would be halved. We get this scale value and store it in scaleTo by dividing the new size of the popup by the original size. We also check to make sure that if it's going to be essentially invisible (scaled to 0 or less), or enlarge (scaled above 1), we set it to it's original size by setting the scale to 1.

Before we modify the transform value of our popup to add in our scale function, we first want to make sure we don't overwrite any existing transform values and functions that the user may have added. We use the getComputedStyle function, which returns the true styles of the popup element after all the stylesheets and everything have been applied to it. It returns a CSSStyleDeclaration object containing all of the CSS properties for the element and a method for getting those properties. We use one of those methods, named getPropertyValue, to pull the value of the transform property and store in our private variable.

Finally, we modify the transform property of the element and attach our scale function to it.

The hidePopup Function

	// Hide the popup
	hidePopup: function() {
		this.bgEl.style.display = "none";
		this.popupEl.style.display = "none";
		
		// Set body overflow back to default to show scrollbars
		document.body.style.overflow = this.overflowDefault;
	}

We use this function to hide the popup if the user clicks on the close button. It's very straight forward - to hide it, we just set the display CSS property to none for both the background and the popup.

In the showPopup function, we change the overflow style of the body so the scrollbars are hidden. This prevents the user from scrolling the window while the popup is shown. With this last line, we're setting the body overflow back to it's default value so the visitor can scroll again.

The showPopup Function

	// Show the popup
	showPopup: function() {
		if(this.shown) return;
		
		this.bgEl.style.display = "block";
		this.popupEl.style.display = "block";
		
		// Handle scaling
		this.scalePopup();
		
		// Save body overflow value and hide scrollbars
		this.overflowDefault = document.body.style.overflow;
		document.body.style.overflow = "hidden";
		
		this.shown = true;
	}

This function will show the popup to the user. We first check to see that the popup hasn't already been shown to the visitor by checking our private shown. If we didn't check this, then the popup would be triggered every time they showed exit intent, even after they clicked the close button.

The next two lines show the background and popup by setting their display styles to "block". The actual elements for the background and the popup are already on the page via the addPopup function, which we'll cover next, but they're hidden by default. This simply changes it so they show.

Remember how we used the default overflow value for the body element in the hidePopup function above? We set it here, before we make any changes to it. Then we change it to hidden so the scrollbars are removed. When the hidePopup function is called, the scrollbars will be shown again.

Lastly, we set our private shown variable to true.

The addPopup Function

	// Add the popup to the page
	addPopup: function() {
		// Add the background div
		this.bgEl = document.createElement("div");
		this.bgEl.id = "bio_ep_bg";
		document.body.appendChild(this.bgEl);
		
		// Add the popup
		if(document.getElementById("bio_ep"))
			this.popupEl = document.getElementById("bio_ep");
		else {
			this.popupEl = document.createElement("div");
			this.popupEl.id = "bio_ep";
			this.popupEl.innerHTML = this.html;
			document.body.appendChild(this.popupEl);
		}
		
		// Add the close button
		this.closeBtnEl = document.createElement("div");
		this.closeBtnEl.id = "bio_ep_close";
		this.closeBtnEl.appendChild(document.createTextNode("X"));
		this.popupEl.insertBefore(this.closeBtnEl, this.popupEl.firstChild);
	}

Before we can ever show the popup, it first has to exist on the page. We use this function to do that via the DOM.

We add the background element as the last child of the body element. Next, we perform a check. If an element with bio_ep as its id already exists on the page, we tell our script to use this as the popup element. If it doesn't already exist, only then do we create it and add it ourselves. This enables the script to accept HTML code for the popup via the init options or via code that's already on the page.

Another important thing to note here is that we use the innerHTML property to set the HTML for the popup. If we didn't use this method, we wouldn't be able to cleanly accept user specified HTML via the init options.

Finally we add the close button. We add it as the first child element of the popup so that when we set its CSS position property to absolute, it will not affect the rest of the popup content and we can position it easily.

The addCSS Function

	// Add font stylesheets and CSS for the popup
	addCSS: function() {
		// Add font stylesheets
		for(var i = 0; i < this.fonts.length; i++) {
			var font = document.createElement("link");
			font.href = this.fonts[i];
			font.type = "text/css";
			font.rel = "stylesheet";
			document.head.appendChild(font);
		}
		
		// Base CSS styles for the popup
		var css = document.createTextNode(
			"#bio_ep_bg {display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: #000; opacity: 0.3; z-index: 10001;}" +
			"#bio_ep {display: none; position: fixed; width: " + this.width + "px; height: " + this.height + "px; font-family: 'Titillium Web', sans-serif; font-size: 16px; left: 50%; top: 50%; transform: translateX(-50%) translateY(-50%); background-color: #fff; box-shadow: 0px 1px 4px 0 rgba(0,0,0,0.5); z-index: 10002;}" +
			"#bio_ep_close {position: absolute; left: 100%; margin: -8px 0 0 -12px; width: 20px; height: 20px; color: #fff; font-size: 12px; font-weight: bold; text-align: center; border-radius: 50%; background-color: #5c5c5c; cursor: pointer;}" +
			this.css
		);
		
		// Create the style element
		var style = document.createElement("style");
		style.type = "text/css";
		style.appendChild(css);
		
		// Insert it before other existing style
		// elements so user CSS isn't overwritten
		document.head.insertBefore(style, document.getElementsByTagName("style")[0]);
	}

Here is where we add the font stylesheets, as well as our base popup and user specified styles.

Since the fonts option is an array, we loop through it to get the value (the URL for the font stylesheet) of each element in the array. We create a new link element for each font, and add it to the head element of the page.

Next, we create a text node that contains all the CSS code for our popup. We specifically include the user's CSS code at the end so it will take precedence over our default styles.

We then create a style element with our CSS text node added to it as the child, and add it to the head element. We don't just append it as a child to the head element though, like we did with the font stylesheets. Instead, we make sure our style element is inserted before any other style elements in head by using insertBefore. Since the order of styles as they appear on the page defines precedence, we want any styles that are already on the page from the user to take priority over the default styles. By adding our default styles before all the other style elements on the page, we can be sure of the precedence. If there are no style elements in the head when we go to add ours, it will just be appended as the last child of the head element by default.

The checkCookie Function

	// Handle the bioep_shown cookie
	// If present and true, return true
	// If not present or false, create and return false
	checkCookie: function() {
		// Handle cookie reset
		if(this.cookieExp <= 0) {
			this.cookieManager.erase("bioep_shown");
			return false;
		}
		
		// If cookie is set to true
		if(this.cookieManager.get("bioep_shown") == "true")
			return true;
			
		// Otherwise, create the cookie and return false
		this.cookieManager.create("bioep_shown", "true", this.cookieExp);
		
		return false;
	}

Cookies are created by the browser and are used to store information about a web page via simple name value pairs (i.e. you name a cookie and set its value). So people don't get annoyed from seeing the exit popup every time they visit a page, we use a cookie named bioep_shown to check if this visitor has already seen the popup on a prior visit.

We use our cookieManager object which is explained in the next section (an object within an object, neat.jpg) to handle creating, getting, and deleting cookies. First, we check the value of our cookieExp option. We use 0 or less to signify that no cookie should be placed, and if there is one, delete it. This will make the popup show for every visitor session.

If the cookie already exists and its value is set to true, we simply return true to let our script know that the popup should not display.

If the cookie doesn't exist, we create it with the defined expiration value and return false.

The cookieManager Object

	// Object for handling cookies, taken from QuirksMode
	// http://www.quirksmode.org/js/cookies.html
	cookieManager: {
		// Create a cookie
		create: function(name, value, days) {
			var expires = "";
			
			if(days) {
				var date = new Date();
				date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
				expires = "; expires=" + date.toGMTString();
			}
			
			document.cookie = name + "=" + value + expires + "; path=/";
		},
		
		// Get the value of a cookie
		get: function(name) {
			var nameEQ = name + "=";
			var ca = document.cookie.split(";");
			
			for(var i = 0; i < ca.length; i++) {
				var c = ca[i];
				while (c.charAt(0) == " ") c = c.substring(1, c.length);
				if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length);
			}
			
			return null;
		},
		
		// Delete a cookie
		erase: function(name) {
			this.create(name, "", -1);
		}
	}

To keep our code more organized and object oriented, we create the functions we use to handle cookies as their own object named cookieManager. The functions used here are actually taken from the wonderful QuirksMode website, where a line by line explanation is given for each. Since they explain it much better than I can, I highly recommend you visit the site.

The Complete Script

That's it! Our script is now complete and ready for use.

window.bioEp = {
	// Private variables
	bgEl: {},
	popupEl: {},
	closeBtnEl: {},
	shown: false,
	overflowDefault: "visible",
	transformDefault: "",
	
	// Popup options
	width: 400,
	height: 220,
	html: "",
	css: "",
	fonts: [],
	delay: 5,
	showOnDelay: false,
	cookieExp: 30,
	
	// Object for handling cookies, taken from QuirksMode
	// http://www.quirksmode.org/js/cookies.html
	cookieManager: {
		// Create a cookie
		create: function(name, value, days) {
			var expires = "";
			
			if(days) {
				var date = new Date();
				date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
				expires = "; expires=" + date.toGMTString();
			}
			
			document.cookie = name + "=" + value + expires + "; path=/";
		},
		
		// Get the value of a cookie
		get: function(name) {
			var nameEQ = name + "=";
			var ca = document.cookie.split(";");
			
			for(var i = 0; i < ca.length; i++) {
				var c = ca[i];
				while (c.charAt(0) == " ") c = c.substring(1, c.length);
				if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length);
			}
			
			return null;
		},
		
		// Delete a cookie
		erase: function(name) {
			this.create(name, "", -1);
		}
	},
	
	// Handle the bioep_shown cookie
	// If present and true, return true
	// If not present or false, create and return false
	checkCookie: function() {
		// Handle cookie reset
		if(this.cookieExp <= 0) {
			this.cookieManager.erase("bioep_shown");
			return false;
		}
		
		// If cookie is set to true
		if(this.cookieManager.get("bioep_shown") == "true")
			return true;
			
		// Otherwise, create the cookie and return false
		this.cookieManager.create("bioep_shown", "true", this.cookieExp);
		
		return false;
	},
	
	// Add font stylesheets and CSS for the popup
	addCSS: function() {
		// Add font stylesheets
		for(var i = 0; i < this.fonts.length; i++) {
			var font = document.createElement("link");
			font.href = this.fonts[i];
			font.type = "text/css";
			font.rel = "stylesheet";
			document.head.appendChild(font);
		}
		
		// Base CSS styles for the popup
		var css = document.createTextNode(
			"#bio_ep_bg {display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: #000; opacity: 0.3; z-index: 10001;}" +
			"#bio_ep {display: none; position: fixed; width: " + this.width + "px; height: " + this.height + "px; font-family: 'Titillium Web', sans-serif; font-size: 16px; left: 50%; top: 50%; transform: translateX(-50%) translateY(-50%); background-color: #fff; box-shadow: 0px 1px 4px 0 rgba(0,0,0,0.5); z-index: 10002;}" +
			"#bio_ep_close {position: absolute; left: 100%; margin: -8px 0 0 -12px; width: 20px; height: 20px; color: #fff; font-size: 12px; font-weight: bold; text-align: center; border-radius: 50%; background-color: #5c5c5c; cursor: pointer;}" +
			this.css
		);
		
		// Create the style element
		var style = document.createElement("style");
		style.type = "text/css";
		style.appendChild(css);
		
		// Insert it before other existing style
		// elements so user CSS isn't overwritten
		document.head.insertBefore(style, document.getElementsByTagName("style")[0]);
	},
	
	// Add the popup to the page
	addPopup: function() {
		// Add the background div
		this.bgEl = document.createElement("div");
		this.bgEl.id = "bio_ep_bg";
		document.body.appendChild(this.bgEl);
		
		// Add the popup
		if(document.getElementById("bio_ep"))
			this.popupEl = document.getElementById("bio_ep");
		else {
			this.popupEl = document.createElement("div");
			this.popupEl.id = "bio_ep";
			this.popupEl.innerHTML = this.html;
			document.body.appendChild(this.popupEl);
		}
		
		// Add the close button
		this.closeBtnEl = document.createElement("div");
		this.closeBtnEl.id = "bio_ep_close";
		this.closeBtnEl.appendChild(document.createTextNode("X"));
		this.popupEl.insertBefore(this.closeBtnEl, this.popupEl.firstChild);
	},
	
	// Show the popup
	showPopup: function() {
		if(this.shown) return;
		
		this.bgEl.style.display = "block";
		this.popupEl.style.display = "block";
		
		// Handle scaling
		this.scalePopup();
		
		// Save body overflow value and hide scrollbars
		this.overflowDefault = document.body.style.overflow;
		document.body.style.overflow = "hidden";
		
		this.shown = true;
	},
	
	// Hide the popup
	hidePopup: function() {
		this.bgEl.style.display = "none";
		this.popupEl.style.display = "none";
		
		// Set body overflow back to default to show scrollbars
		document.body.style.overflow = this.overflowDefault;
	},
	
	// Handle scaling the popup
	scalePopup: function() {
		var margins = { width: 40, height: 40 };
		var popupSize = { width: bioEp.popupEl.offsetWidth, height: bioEp.popupEl.offsetHeight };
		var windowSize = { width: window.innerWidth, height: window.innerHeight };
		var newSize = { width: 0, height: 0 };
		var aspectRatio = popupSize.width / popupSize.height;
		
		// First go by width, if the popup is larger than the window, scale it
		if(popupSize.width > (windowSize.width - margins.width)) {
			newSize.width = windowSize.width - margins.width;
			newSize.height = newSize.width / aspectRatio;
			
			// If the height is still too big, scale again
			if(newSize.height > (windowSize.height - margins.height)) {
				newSize.height = windowSize.height - margins.height;
				newSize.width = newSize.height * aspectRatio;
			}
		}
		
		// If width is fine, check for height
		if(newSize.height === 0) {
			if(popupSize.height > (windowSize.height - margins.height)) {
				newSize.height = windowSize.height - margins.height;
				newSize.width = newSize.height * aspectRatio;
			}
		}
		
		// Set the scale amount
		var scaleTo = newSize.width / popupSize.width;
		
		// If the scale ratio is 0 or is going to enlarge (over 1) set it to 1
		if(scaleTo <= 0 || scaleTo > 1) scaleTo = 1;
		
		// Save current transform style
		if(this.transformDefault === "") 
			this.transformDefault = window.getComputedStyle(this.popupEl, null).getPropertyValue("transform");
			
		// Apply the scale transformation
		this.popupEl.style.transform = this.transformDefault + " scale(" + scaleTo + ")";
	},
	
	// Load event listeners for the popup
	loadEvents: function() {
		// Track mouse movements
		document.addEventListener("mousemove", function(e) {
			// Get current scroll position
			var scroll = window.pageYOffset || document.documentElement.scrollTop;
			
			if((e.pageY - scroll) < 7)
				bioEp.showPopup();
		});
		
		// Handle the popup close button
		this.closeBtnEl.addEventListener("click", function() {
			bioEp.hidePopup();
		});
		
		// Handle window resizing
		window.addEventListener("resize", function() {
			bioEp.scalePopup();
		});
	},
	
	// Set user defined options for the popup
	setOptions: function(opts) {
		this.width = (typeof opts.width === 'undefined') ? this.width : opts.width;
		this.height = (typeof opts.height === 'undefined') ? this.height : opts.height;
		this.html = (typeof opts.html === 'undefined') ? this.html : opts.html;
		this.css = (typeof opts.css === 'undefined') ? this.css : opts.css;
		this.fonts = (typeof opts.fonts === 'undefined') ? this.fonts : opts.fonts;
		this.delay = (typeof opts.delay === 'undefined') ? this.delay : opts.delay;
		this.showOnDelay = (typeof opts.showOnDelay === 'undefined') ? this.showOnDelay : opts.showOnDelay;
		this.cookieExp = (typeof opts.cookieExp === 'undefined') ? this.cookieExp : opts.cookieExp;
	},
	
	// Ensure the DOM has loaded
	domReady: function(callback) {
		(document.readyState === "interactive" || document.readyState === "complete") ? callback() : document.addEventListener("DOMContentLoaded", callback);
	},
	
	// Initialize
	init: function(opts) {
		// Once the DOM has fully loaded
		this.domReady(function() {
			// Handle options
			if(typeof opts !== 'undefined')
				bioEp.setOptions(opts);
				
			// Handle the cookie
			if(bioEp.checkCookie()) return;
			
			// Add the CSS
			bioEp.addCSS();
			
			// Add the popup
			bioEp.addPopup();
			
			// Load events
			setTimeout(function() { 
				bioEp.loadEvents();

				if(bioEp.showOnDelay)
					bioEp.showPopup();
			}, bioEp.delay * 1000);
		});
	}
}

Comments

Avatar
Ben on Jan 8th, 2015
Great script! Thank you so much for sharing it. Any suggestion on how I would tweak it so that if I click anywhere in the grey #bio_ep_bg layer it would close the pop over? The button is small and I think people might miss it if they all of a sudden decide to stay.
replypermalink
Avatar
beeker on Jan 13th, 2015
Thanks, and no problem. To get it to close when the background is clicked, you'll just have to add an event listener for it. In the loadEvents function of the script, add this piece of code just below where the close button is handled:

// Close popup when background is clicked
this.bgEl.addEventListener("click", function() {
	bioEp.hidePopup();
});
replypermalink
Avatar
Kris on Jan 8th, 2015
Simple, elegant and works great. Awesome!
replypermalink
Avatar
Bryan on Jan 13th, 2015
Is there anyway to load content in an iframe with this?
replypermalink
Avatar
beeker on Jan 13th, 2015
Yes, you'd just have to add it in the html and css options, using either of the two methods (either in the JavaScript init call or in HTML on the page itself, see the 'Adding in HTML and CSS' section at the top).

For example you could do this:

bioEp.init({
	html: '<iframe id="popup_iframe" src="http://nytimes.com" scrolling="no"></iframe>',
	css: '#popup_iframe {width: 100%; height: 100%;}',
	cookieExp: 0
});
replypermalink
Avatar
Bryan on Jan 13th, 2015
Yes that worked great, thanks for the quick reply!
replypermalink
Avatar
Ben on Jan 14th, 2015
Thank you, your modification works great! Awesome script!
replypermalink
Avatar
manoj on Feb 2nd, 2015
can i add a other java script fun inside init like onclick move to some url instedof embedding the url inside
replypermalink
Avatar
beeker on Feb 19th, 2015
Definitely, just add the HTML and CSS you need in the init() function, then below that, add the onclick handler you want for the element inside the popup.
replypermalink
Avatar
Robert on Feb 8th, 2015
Thanks for this script!
I noticed that the cookie is always set, even if the mouse never leaves this window. As a result the popup only ever show while on the first page. If the user navigates around on the website and e.g. leaves window on 3rd page, the cookie already exists and there is no popup. How can I change this?
replypermalink
Avatar
beeker on Feb 19th, 2015
No problem, and you'll just have to set the cookieExp option to 0 inside the init() call. This will make it so the popup shows on every page load.
replypermalink
Avatar
modhappy on Feb 25th, 2015
How can I prevent the users of Internet explorer from getting an error and showing the hidden div ?
replypermalink
Avatar
beeker on Mar 16th, 2015
You could do a conditional check for the IE version and decide whether or not to call the init() function based on it. It should be working in the latest version of IE though.
replypermalink
Avatar
Matt on Mar 11th, 2015
Hi, This script is great! thanks very much for the detailed post explaining it aswell, its nice to know what is going on behind the scenes.
I wondered, is there a way to make the popup display on the click of a button aswell as on exitIntent?
Cheers, Matt
replypermalink
Avatar
beeker on Mar 16th, 2015
Thank you, and definitely, you'll just have to add an event listener on the element that listens for the click, then in that listener, call bioEp.showPopup();
replypermalink
Avatar
Bobbie on Apr 16th, 2015
Beeker, this is a AWESOME script. I am pretty dumb at this stuff. I would be willing to pay you to install this on my wordpress site. Would you be interested in that?
replypermalink
Avatar
beeker on Apr 19th, 2015
Thank you, and while I appreciate the offer, I just don't have the time at the moment to install and set it up.

Basically, you would just need to upload the bioep.min.js script to your WordPress site, then use either the wp_enqueue_script() WordPress function to add it in, or simply modify the header.php file of your theme and link to within the head tag.

This is explained in more detail here https://codex.wordpress.org/Using_Javascript
replypermalink
Avatar
Leo on Apr 19th, 2015
Is posible to show more popups? One when visitor want to leave and one after 5 seconds?
Thanks for answer.
replypermalink
Avatar
beeker on Apr 19th, 2015
With the current code, that unfortunately isn't possible. It would be fairly straight forward to modify though and have it work that way.
replypermalink
Avatar
Rick on Apr 19th, 2015
I'd like to control a form within the popup by jQuery. How to bind for example the change event? Though I have assigned the event to a form element it perhaps gets overwritten or removed when you apply the popup html to the DOM tree.
replypermalink
Avatar
beeker on May 10th, 2015
If you're using the HTML method of building the popup (where the HTML for the popup is already on the page within the bio_ep div), then it should work from what I can see. The HTML is only modified when the popup has to be created.
replypermalink
Avatar
Mohammad on Apr 20th, 2015
After searching a lot for this online and failed , I finally found your solution and it is working as instructed with your templates, But when i try to add my html codes nothing is showing . The popup doesn't work .



Here is the code I am trying to add :

<div id="contact_form_pop_up">

<div id="er_pop_header">
<img src="" alt="LOGO" /> 
</div>

<div id="er_pop_text">
<p>Get Started with a <strong>Free 14-Day Trial.</strong></p>
<p>Enjoy unlimited access to all features.</p>
</div>

<div id="er_pop_container">
<form method="post" id="er_loaderform" action="">
<input type="text" class="er_span7" title="Email" id="er_pop_input" name="name" placeholder="EMAIL ADDRESS" />
<input type="text" class="er_span7" title="Email" id="er_pop_password" name="name" placeholder="CREATE PASSWORD" />
<button type="submit" class="er_btn-o" id="er_pop_submit" type="submit" name="submit" value="SIGN UP NOW" >SIGN UP NOW</button>

</div>

<div id="er_pop_footer">
<p>By clicking "sign up now" I agree to </p>
<a href=">Terms of Service</a> and <a href="">Privacy Policy.</a>
<p id="er_pop_login">Already have an <a href=""> Login</a></p>
</div>

</form>


</div>


Could you please help me out with a solution..

replypermalink
Avatar
beeker on May 10th, 2015
Most likely it's because of the cookieExp parameter. By default, it's set to 30 days, so once you see the popup once it won't show again for another 30 days unless you clear your cookies. You can have it show every time by changing this parameter to 0.
replypermalink
Avatar
Akoma on Apr 23rd, 2015
Hey Beeker, thanks very much for the script. How can I get the pop up to show every time my visitor is trying to leave the site, if after they have closed it out once?
replypermalink
Avatar
beeker on May 10th, 2015
You'll have to go into the source code and change one thing.
In the showPopup function, you'll see the very first line is: if(this.shown) return;
Simply delete that entire line and it should show the popup every time a visitor goes to leave the site.
replypermalink
Avatar
Kevin on Apr 28th, 2015
This is great! How do I create two buttons next to each other using html and css, one on left that says yes and points to the URL and one on right that says no, the no button becomes the close function. Thanks in advance!
replypermalink
Avatar
beeker on May 10th, 2015
This is pretty simple using HTML and CSS, to build the buttons I would suggest just searching for a tutorial on Google. To have one act as the close function, just attach a click event handler to the button, and in that handler have it call the bioEp.hidePopup(); function.
replypermalink
Avatar
dreamerdad on Apr 29th, 2015
I tried method one but one of my colleagues who has been testing it says the popup does not appear in Chrome when he moves his mouse over to the URL address bar as if to close the window. It does appear in IE for him. He's using a Windows 8 machine. He said he is not noticing any errors in the console. Any idea what the problem might be?
replypermalink
Avatar
beeker on May 10th, 2015
I would suggest first making sure the cookieExp parameter is set to 0. By default, it's set to 30, which means it will show once and not show again for 30 days after that.
replypermalink
Avatar
Hugh on May 14th, 2015
Ok thanks! I also tried with the method 2 above and that seemed to help.
replypermalink
Avatar
beeker on May 16th, 2015
The tutorial and script has just been updated today to fix an issue with the DOMContentLoaded event not firing - huge thank you to Hugh for finding it!
replypermalink
Avatar
Bu on Jun 2nd, 2015
I know this isn't a question, but...please don't add this to your site. I found this page after Googling why I kept getting these popups when I tried to navigate away from sites (I guess I just reached critical mass in my awareness of this code being on many different sites).

I had just had a really good experience on a site which offered excellent products and had an attractive, functional interface. My good feelings about the company /utterly/ evaporated when I got a popup as I tried to leave. It might not be logical. I know that, rationally, it only requires a few seconds to get rid of such a popup. But, it just erodes goodwill. Instead of feeling like a respected potential customer, whose experience of their website is taken into consideration (which was conveyed through clear, useable design) I immediately feel like a number on a spreadsheet as presented at a weekly staff meeting by someone whose MBA's ink is still drying.

I know business, particularly big business, is about numbers. I know that. But, goodwill and loyalty are often undervalued until it's far too late.
replypermalink
Avatar
beeker on Jun 4th, 2015
Very good point, and a lot of the time it is just about the numbers. If you're running an offer and get a high bounce rate, but implementing an exit popup turns some of those would be wasted visits into conversions, then it's worth it. I think trying to make it look as professional as possible goes a long way as well. I do agree though that it can be off putting, and everyone should decide whether or not it makes sense to them, ideally through testing and actual data.
replypermalink
Avatar
Webdesign Dortmund on Jun 3rd, 2015
Thank you so much for this script. Amazing. It works without any problems.
replypermalink
Avatar
beeker on Jun 4th, 2015
No problem, you're very welcome.
replypermalink
Avatar
Maria-Christina Rus on Jun 4th, 2015
Hi, many thanks for your tutorial!! I have one question: I would like to put two buttons, one "yes" with a link and the other, "no" that closes the pop up. Any clue on how to do this?
replypermalink
Avatar
beeker on Jun 4th, 2015
You would want to create both of those buttons using HTML and CSS, and use that to initialize the bioEp popup. The "yes" button can just be a simple anchor element, and for the "no" button, it can be whatever you choose, just use the click event to call the bioEp.hidePopup() function.
replypermalink
Avatar
Maria-Christina Rus on Jun 8th, 2015
Ok, I am going to try to see how it works (I am not a Javascript expert) Thanks!!
replypermalink
Avatar
Chris Hayes on Jun 6th, 2015
Good stuff, this is a really useful script! Pretty simple too :) I like it.

Thanks for taking the time to build it out and share with everyone.

Cheers!
replypermalink
Avatar
beeker on Jun 6th, 2015
No problem, thank you for the comment!
replypermalink
Avatar
Ankit on Jun 25th, 2015
Hi
Many thanks for the script it is working fine on all browser except below IE11 like IE10,9 ..
is there any way how can I fix this issue so popup will also appear on IE 10and 9 also

Thanks in advance !!!
replypermalink
Avatar
beeker on Jul 11th, 2015
Some of the properties used in the script may not be supported below IE11, however I just tried it out in IE9 emulation mode and it seems to be working. Are you still having issues with it in IE?
replypermalink
Avatar
al on Jul 8th, 2015
it took like 10 times to get it to work, well its good but too slow... u gotta move your mouse super slow out of the window for it to pop up, is there a way to make it instant?
replypermalink
Avatar
beeker on Jul 11th, 2015
I'll probably be updating the script here soon with a better method for detecting exit intent.
replypermalink
Avatar
rixy on Jul 12th, 2015
Hi, thank you so much for this tutorial! It was exactly what I was looking for. I was just wondering if there was a way to be able to click anywhere and not just the little X to exit the pop-up. Thanks!
replypermalink
Avatar
beeker on Nov 21st, 2015
No problem, and yeah I'll actually be updating the script and I'll add this as an option. Look for it soon!
replypermalink
Avatar
Lemary on Jul 16th, 2015
Hi. I'm using your code for my website. The popup is HTML and located at the bottom of the page.
First time, it will show popup.
The next time, there is no popup. But the popup in HTML is visible after the footer.
I'm not sure why this happen. Can you please check?
replypermalink
Avatar
beeker on Nov 21st, 2015
I think this is a bug when using the second method to create the popup. I'm going to look into this and fix in an update.
replypermalink
Avatar
Jason on Jul 16th, 2015
Beeker, I need some help. I ahve a exit pop up script on my website, it wrks great and I love it. I cannot figure out how to add a script to it so it only shows once to a customer not EVERY time the try to leave. I also want it to only show on a specific page (checkout page if the decide not to complete order). I am willing to pay some one to help me with this. I use WP with woocommerce wesite is customstickershop.us can you take a quick look and help me please
replypermalink
Avatar
beeker on Nov 21st, 2015
You'll need to set the cookieExp option higher than 0. When it's set to 0, no cookie is stored (the cookie tracks if a visitor has seen the popup), so it will show every time. If you set it to 30 for example, it won't show again for that user for 30 days.
replypermalink
Avatar
Jason on Jul 16th, 2015
I'm having issues with the exit pop in Safari. When the exit pop is activated, the image is shifted towards the right and down, and not in the center, as it is displayed in Chrome. The same issue occurs with your Template examples above. Any idea why this is or how to work around?
replypermalink
Avatar
beeker on Nov 21st, 2015
I'll look into this and add a fix in an update if needed, thank you!
replypermalink
Avatar
jay on Aug 3rd, 2015
Hi I am waiting to get your IE10 solution as I tried couple of ways as it did not work as expected.
It would be really appreciated.
Thank you
replypermalink
Avatar
beeker on Nov 21st, 2015
IE10 should be fully supported now.
replypermalink
Avatar
Jeff on Aug 17th, 2015
Great Script! Works pretty great out of the box. Any way to make the pop up not show up for 7 days after a user clicks the close button?
replypermalink
Avatar
beeker on Nov 21st, 2015
Thanks, and sure, just set the cookieExp option to 7.
replypermalink
Avatar
Eric on Aug 26th, 2015
Love this script. I'm a total beginner in javascript. Is there any way to add an image link on the pop up that closes the pop up? Example: "Click either of these two buttons: EXTERNAL URL... CLOSE THIS POP UP"
replypermalink
Avatar
beeker on Nov 21st, 2015
Check out the code for the close button and where it's referenced in the different parts of the script - the easiest way will be mimicking that with your own HTML element within the popup.
replypermalink
Avatar
Maria-Christina Rus on Aug 28th, 2015
Hi there,

Sorry it's me again :) For any strange reason, the pop is positionned outside the window: http://i.imgur.com/aC7ODOj.png

I have tried to position it with CSS with position: relative and position: absolute but it doesn't work... any clue?

Thanks a lot again!
replypermalink
Avatar
beeker on Nov 21st, 2015
Most likely a styling issue caused by other CSS declarations on that page. Could you comment here the URL of that site?
replypermalink
Avatar
omar on Sep 4th, 2015
can i take off the little close button.
replypermalink
Avatar
beeker on Nov 21st, 2015
Sure, in the addPopup function, just remove this section:

		// Add the close button
		this.closeBtnEl = document.createElement("div");
		this.closeBtnEl.id = "bio_ep_close";
		this.closeBtnEl.appendChild(document.createTextNode("X"));
		this.popupEl.insertBefore(this.closeBtnEl, this.popupEl.firstChild);


And remove the event listener for it in the loadEvents function.
replypermalink
Avatar
Leo on Sep 6th, 2015
Hi great script - thanks a bunch! I have 2 questions:

1) If i add script file AND CSS code to HEADER (such as in method two) but do not call popup in body (div id="bio_ep"), blank popup fires.
How do I make it NOT show unless I call
??

2) Popup does not always fire on exit intent (mouse off browser screen), even if I wait 5-10+ seconds. I often have to reload the page to get it to fire.

I set delay: 0, and cookieExp: 0,

AND

if(bioEp.showOnDelay)
bioEp.showPopup();
}, bioEp.delay * 10);

but there is still delay in firing or it doesn't fire at all (about 30-50% of the time)...

How do I make sure it always fires on exit intent and delay is minimal.

Thank you very much!

replypermalink
Avatar
beeker on Nov 22nd, 2015
No problem, to answer your questions:

1. You'll have to remove the bioEp.init() call. If this function is called, the popup will show.

2. I'm going to update the script with improved exit intent detection. This should hopefully fix these issues.
replypermalink
Avatar
Leo on Nov 25th, 2015
Hi, thanks for reply. Have you had a chance to improve the EXIT intent detection?

PS - I now primarily use your plugin as LOAD popup with delay - it works great. Would love to also use it for EXIT intent

Thanks for great work

PSS - you should add a Donate link [/hint]
replypermalink
Avatar
beeker on Nov 25th, 2015
Yes, the script has been updated with improved exit intent detection. You can download the newest version using the download buttons above, or going to the GitHub page. Thanks for the suggestion! :)
replypermalink
Avatar
sultanmahmud on Sep 10th, 2015
On safari browser popup is not centered. How to fix it?
replypermalink
Avatar
beeker on Nov 22nd, 2015
Going to look into this and will update the script if there's a fix needed, thanks!
replypermalink
Avatar
sultanmahmud on Sep 10th, 2015
It will be really cool if outside of the popup box click hides the popup.
replypermalink
Avatar
beeker on Nov 22nd, 2015
Adding this as an option very soon!
replypermalink
Avatar
James B on Sep 12th, 2015
Superb tutorial man! This must have taken some time but it works really well and was easy to follow the way you broke it down into chunks. Look forward to seeing your new method for detecting exit intent because I'd like to figure out how to use it to get an aweber lightbox pop up to show when people exit. I've been trying with this bit but no luck yet :( loadEvents: function() {
// Track mouse movements
document.addEventListener("mousemove", function(e) {
// Get current scroll position
var scroll = window.pageYOffset || document.documentElement.scrollTop;

if((e.pageY - scroll) < 7)
bioEp.showPopup();
});

Thanks again anyway. Have a good one!
replypermalink
Avatar
beeker on Nov 22nd, 2015
Thanks, and for sure, I'll be updating the script soon with much better exit intent detection.
replypermalink
Avatar
Zoran on Sep 13th, 2015
Hello,
Script is excellent, and thank you very much for sharing. I tried to use the method #2, which is in my case much better to use, but there is a small problem. I have an autoresponder form and I put an image above that. Now, when I open a main website, that image (only image, without form) will show up (in the lower part of the content) for about 1 or 2 seconds and escape after that. Actually it looks funny, the offer come for 1 second and escape after that. Maybe visitor will search that through whole website, and if he want to leave, finally the magic offer is here again :).

However, is there any solution, that the image will stay hidden as should?
replypermalink
Avatar
beeker on Nov 22nd, 2015
Most likely a bug, I'll look into it and code in a fix in the next version if needed.
replypermalink
Avatar
taras on Sep 22nd, 2015
Thanks for this script, it's brilliant. I have one issue which I think I'll be able to fix myself, but might be something to think about:

If the mouse is above the page (for example, resting on the address bar or browser tabs) while the user is reading the page, then they move their mouse *down* into the window, the pop-over appears. In this case, they're most likely to be about to interact with the page, rather than trying to exit.

I guess the solution would be to add some code to check whether the mouse has previously been moving below the trigger margin ( y > 7 ) before showing the pop up.
replypermalink
Avatar
beeker on Nov 22nd, 2015
Thanks, and yes you're 100% right. I'm going to update the script with better exit intent detection.
replypermalink
Avatar
taras on Sep 22nd, 2015
In order to make sure the popup isn't triggered by someone moving their mouse into the window from the top after 5 seconds, I altered the loadEvents function to include these lines:

if((e.pageY - scroll) >= 7 )
wasLower = 1;
if((e.pageY - scroll) < 7 && wasLower )
bioEp.showPopup();
replypermalink
Avatar
Stacy on Oct 8th, 2015
You are a LIFESAVER! Thank you so much for providing such a great, easy script!
replypermalink
Avatar
oriol on Oct 26th, 2015
Nice script! good work beeker. Can I implement it on the footer instead of head?. Im updating an e-commerce and I need to display the pop-up on every page, not just in homepage. Im runing joomla, and my idea is to include it to the footer which can be injected in every page.

Thankyou
replypermalink
Avatar
beeker on Nov 22nd, 2015
Yes, you should be able to include the script in the footer. Either call bioEp.init() when the DOM is ready, or just have it after the script include.
replypermalink
Avatar
Philipp on Oct 28th, 2015
Hi. Thanks for the script. I would like to use the possibility to create different kinds of exit intent. I have seen that there is no way to use different cookie names for different popup. Do you have any idea how I can set my own cookie name during init the popup?
replypermalink
Avatar
beeker on Nov 22nd, 2015
You'd have to modify the script and just add in a new option, such as cookieName, and use that variable in the cookie functions.
replypermalink
Avatar
beeker on Nov 22nd, 2015
Thinking about it again, you may have to change the structure of the script so instead of using object literal notation (binding the bioEp object to the window), you make the bioEp object its own function, and prototype all of its methods out (then use new bioEp(); to create each new popup with its own options and cookie name).



Here's a great tutorial on how to define JavaScript classes using a function.
replypermalink
Avatar
libor on Nov 6th, 2015
hello I like the script, but I have this problem:

if the cookie is set to 0, so everything goes ok

However, if I set a cookie for any time greater than 0, the popup window the next time you reload the page does not remain hidden and the whole under footer stretched the width of the page :-(
replypermalink
Avatar
beeker on Nov 22nd, 2015
Will be looking into this and providing a fix.
replypermalink
Avatar
Nabeel on Nov 9th, 2015
Sir you're awesome! I've been searching for exit intent popup without javascript and this is exactly what I need! :)
replypermalink
Avatar
Craig on Nov 12th, 2015
Hi Beeker, thanks for this great script. It works straight away!
One thing I noticed is when I use HTML and CSS on the page itself and not through Javascript and I set a cookie for anything 1 or above, it shows the bio_ep div on the page instead of not showing at all. Once the cookie is cleared it loads in the popup as it should. Refresh again and it will load on the page again. The only way around this is to show the popup every single time. Is there a way around this as I would prefer a day or two before the popup shows again.
Thank you!
replypermalink
Avatar
beeker on Nov 22nd, 2015
This will be fixed in the new version.
replypermalink
Avatar
Peter on Nov 19th, 2015
What's the license behind your popup system?
I actually rather like it, not really the exit intent part I'm interested, as I think you should have used mouseleave, it's more accurate.

But the simple modal system with inline css. I would like to sell a product with your modal, is it possible?
replypermalink
Avatar
beeker on Nov 22nd, 2015
Sure, no problem.
replypermalink
Avatar
Jakkrit on Nov 20th, 2015
Thank you very much for this exit intent popup script. It is very nice, very small and very easy to install on any website. I love it so much. Although i have found problem with method one but after i have change to use method two it work fine properties.
replypermalink
Avatar
Konrad on Nov 20th, 2015
great plugin, thanks :)
replypermalink
Avatar
beeker on Nov 21st, 2015
Catching up on comments now, sorry everyone!
replypermalink
Avatar
Nelson Martinez on Dec 4th, 2015
Beeker, excellent script!!!

I will like to know how to change something with the Overflow. My popup is a little large and in displays of 15 inches or less the visitor cannot see the half part of my popup....

My issue is, that if try to change the Overflow from "Hidden" (which is the value you assigned when showing up the popup) to any other thing, the Close button stop working :(

Cheers, and hope to read you soon.
replypermalink
Avatar
beeker on Feb 22nd, 2016
Is this happening on mobile/tablets?
replypermalink
Avatar
arif on Dec 5th, 2015
This is a great script. Thanks for the work. Is it possible to modify the script so that the popup doesn't show when someone is reaching for the scroll bar? I want the popup to show only when someone is trying to reach the close button.
replypermalink
Avatar
beeker on Feb 22nd, 2016
Just implemented a fix for this, can check the new code.
replypermalink
Avatar
Bogdan on Dec 28th, 2015
Can we set a delay on the close button so that it will show after X amount of time?
replypermalink
Avatar
beeker on Feb 22nd, 2016
You could add a setTimeout() function in the event that looks for the mouseout on document (check the loadEvents function). Just have this setTimeout() function call the bioEp.hidePopup() function.
replypermalink
Avatar
Uri on Dec 28th, 2015
Hello,
Is it possible to call the body div from another file that does not load with site template? or iFrame as I use that would not load till called?
replypermalink
Avatar
beeker on Feb 22nd, 2016
I'm not sure, the easiest fix for that would probably to just have all the HTML and CSS in the init() call in JavaScript, if you're able to control that on every page.
replypermalink
Avatar
Ezekiel on Jan 7th, 2016
That's really great ! Is it possible to open a popunder with another website with your script ? This could be a really good improvment. Thanks !
replypermalink
Avatar
beeker on Feb 22nd, 2016
For sure, it depends on how you want to do it though. If you want it to happen on a click of the popup window, you can simply place an anchor link in the popup (or make the entire popup a link) and have the target="_blank" so it opens a new window. Could also do it via a JavaScript window.open() call on click.
replypermalink
Avatar
Sam on Jan 18th, 2016
How can I add on window close functionality in this script.
replypermalink
Avatar
beeker on Feb 22nd, 2016
If you want to do something when the entire window is closed, you'll have to hook the window.onunload event. Know though that depending on the browser and what you do in the handler for onunload, it may not finish executing before the window closes (AJAX calls are an example). If you want to do something when just the popup is closed, add it in to the hidePopup() function.
replypermalink
Avatar
Patryk on Jan 30th, 2016
Thank you very much for this script.
replypermalink
Avatar
beeker on Feb 22nd, 2016
You're very welcome :)
replypermalink
Avatar
Andre on Feb 2nd, 2016
Popup also shows when I move mouse to scroll bar. This should not happen. It happens in latest Firefox/Chrome. How do I make it so it only shows when mouse is moved towards top (implying exit)? :)
replypermalink
Avatar
beeker on Feb 22nd, 2016
Just implemented a fix for this, new code is live.
replypermalink
Avatar
Van Van Horn on Feb 21st, 2016
Two questions:

1) I'd like to show the popup after a delay *OR* on exit intent, whichever comes first. Is that possible?

2) Is it possible to use this for more than one popup? Specifically, I'd like to popup a different form for current subscribers every three or four months, which would be based on a different cookie expiring.

I'm pretty impressed so far, I have it up and running and I had to learn about setting cookies first.

Van
replypermalink
Avatar
beeker on Feb 22nd, 2016
1. That isn't possible right now but that's a good idea for a feature, I'll try and add this in soon.

2. The code would have to be modified quite a bit to make that possible (allow bioEp to be instantiated via new keyword, and use prototyping vs object literal notation). I'll think this over.

Thank you for the comments and I hope the script helps!
replypermalink