In this article, we are going to look at adding a small script that checks that a DIV is about to come in to the viewport and populate the DIV with a sign-up form.
I have been testing this with both MailerLite and Brevo forms. Both these forms include JavaScript callbacks that check the form has been correctly submitted.
For this reason, it is important to load everything correctly so it works.
The Simplicity Of JavaScript APIs
I am using the Intersection Observer API to do the heavy lifting here. If you are new to the Intersection Observer API, I have a starter article that explains how it works. For more detailed information on how to set up the Intersection API and the inner workings, please go check that out.
The HTML
For this implementation, it is straightforward. We only observe one element, that is an empty DIV; it actually has the text “You don't have JavaScript”, so not quite empty as show below.
<div style="min-height:300px;" id="mailerlite-placeholder">You don't have JavaScript</div>The JavaScript
To select our element, we can use:
const placeholder = document.querySelector('#mailerlite-placeholder');Then, we need to set up our new observer using the Intersection Observer constructor and pass in our function, what we want to happen, and our options.
const observer = new IntersectionObserver(handleIntersect , options);The Function
function handleIntersect(entries, observer) {
entries.forEach((entry) => {
if (entry.isIntersecting) {
// Create a container div for the form HTML (without the script yet)
const formContainer = document.createElement('div');
formContainer.innerHTML = `
<!-- Your entire MailerLite form embed code goes here -->
<style type="text/css">@import url("https://assets.mlcdn.com/fonts.css?version=1705921");</style>
<style type="text/css"></style>
<style type="text/css"></style>
<div class="ml-form-embedContainer ml-subscribe-form ml-subscribe-form-11389960" id="mlb2-11389960"></div>`;
placeholder.innerHTML = '';
placeholder.appendChild(formContainer);
// Then load the MailerLite tracking/init script if needed
const script = document.createElement('script');
script.src = 'https://groot.mailerlite.com/js/w/webforms.min.js?v2d8fb22bb5b3677f161552cd9e774127';
script.async = true;
document.body.appendChild(script); // or append to placeholder
placeholder.dataset.loaded = 'true';
observer.disconnect();
}
});
}The function first checks if the entry isIntersecting and if it is, it continues with adding the HTML form, CSS, and a link to external script(s).
The HTML and CSS are first placed into an empty div using a template literal which is then appended to the placeholder.
And the external script link is appended to the body.
We can place our function at the bottom of the JavaScript file.
The Options
let options = {
root: null, // null means use viewport
rootMargin: '0px 0px -50px 0px',
threshold: 0.1,
};The options in this case are set so that the element will be loaded just as the placeholder comes onto the screen (-50px from the bottom). This is perfect, and because of this, we do not need any sort of transition. Remember to add these before constructor.
Finally, we observe our target
observer.observe(placeholder);We pass in the placeholder as a variable, and when the element comes into the observers' sight, that is defined by the options, the form is loaded into the mailerlite-placeholder DIV.
For the full code, I have set up a branch on GitHub. Here is a link to the JavaScript we require, all wrapped in a document content loaded event listener.
To make this work: Things to Note
Things to note here are:
We need to also load the callback for the MailerLite so that the success messages can be loaded when the form is submitted. We do this globally when the script loads.
// Define the success callback globally FIRST (MailerLite needs this)
window.ml_webform_success_11389960 = function () {
var $ = ml_jQuery || jQuery;
$('.ml-subscribe-form-11389960 .row-success').show();
$('.ml-subscribe-form-11389960 .row-form').hide();
};Furthermore, we also need to attach the tracking init script. This can be done when the form is loaded so is included in the handle intersection function.
The following is the basic setup we have, all wrapped in a document event listener or Drupal behaviour (Drupal behaviour JS file is also included in the above link.)
- Select the placeholder element using querySelector().
- Register the MailerLite success callback on the global window object.
- Create a new IntersectionObserver instance with the callback function and configuration options.
- Call the observe instance method on the IntersectionObserver, passing in the placeholder element.
A few small bonuses
A few other things worth pointing out since this is a one-off:
- We can disconnect the observer from watching the target element. We can do this by using the disconnect() method on our observer.
- Before we do this, it is a good idea to add a data-attribute to the element. I called the data attributed data-loaded and set it to true.
- Now at the beginning, we can also add a return statement to check if
data.loadedis present. Add this straight after theconstdeclaration.
Now it's all set to go.
Cut 100% of fake submissions
This has cut non-serious sign-ups by 100%, which even though is quite depressing, it is also very pleasing not having to second guess if someone didn't do the two-step sign up.
Conclusion
In this article, we have learnt a practical use for the Intersection Observer API. We learnt how we can inject the code needed for a newsletter sign up form in to an empty placeholder DIV when the element comes on to the page.
Intersection Observer API is extremely simple once you understand how to use it.
The part that is sometimes difficult is to write the callback function we pass into the observer. In the case of adding a form to the page we can make use of other JavaScript fundamental concepts such as template literals and append child, as we did today.
To check out the above code in action scroll to the bottom of the page, see the newsletter sign up form there, add your email to get web development and design tips straight to your inbox.
Thanks for reading
Simon