Skip to content

Instantly share code, notes, and snippets.

@TimBHowe
Last active April 15, 2024 12:44
Show Gist options
  • Save TimBHowe/93767add564332085b2eb76fe40ac9d5 to your computer and use it in GitHub Desktop.
Save TimBHowe/93767add564332085b2eb76fe40ac9d5 to your computer and use it in GitHub Desktop.
Mailchimp Popup Tracking
<script id="mcjs">
!function(c,h,i,m,p){m = c.createElement(h), p = c.getElementsByTagName(h)[0], m.async = 1, m.src = i, p.parentNode.insertBefore(m, p)}(document,"script","https://chimpstatic.com/mcjs-connected/js/users/XXXXX/XXXXX.js");
window.dataLayer = window.dataLayer || [];
var mailchimpObserver = new MutationObserver(function(mutations_list) {
mutations_list.forEach(function (mutation) {
mutation.addedNodes.forEach(function (added_node) {
if (added_node.id == 'PopupSignupForm_0') {
window.dataLayer.push({ event: 'mailchimp_popup_shown' });
document.querySelector('#PopupSignupForm_0 div.mc-modal iframe').contentDocument.querySelector('form').addEventListener('submit', function (event) {
window.dataLayer.push({ event: 'mailchimp_popup_submit' });
});
mailchimpObserver.disconnect();
}
});
});
});
mailchimpObserver.observe(document.querySelector("body"), {subtree: false, childList: true });
</script>
@tberndt-ewolff
Copy link

tberndt-ewolff commented Apr 12, 2024

Hi @TimBHowe! Your code works well, but I wanted some additional functionalities for our tracking setup:

  1. Push the ..._shown event only if the modal was actually visible.
  2. Only observe the MailChimp modal container (not the whole body).
  3. Get the specific MailChimp form ID (f_id) and attach it to the dataLayer event.
  4. Easier debugging

Therefore I needed to create some additional functionality. Maybe this comment is helpful for other people who need to handle these "pesky" dynamic MailChimp forms.

Example dataLayer event (result):

{
    "event": "form_view",
    "formType": "mailchimp_modal",
    "formId": "123456",
    ...
}

The code below should be ready for copy&paste, if anyone has the same challenge.

    (() => {
    // IIFE

    /**
    * Waits for the MailChimp modal container to be available in the DOM and then executes the provided callback function.
    * @param {Function} callbackFunction - The function to be executed when the MailChimp modal container is found.
    */
    const waitForMailChimpModalContainer = (callbackFunction) => {
      const intervalDuration = 500;
      const timeoutDuration = 5000;
  
      /**
      * Clears the active interval and timeout timers.
      */
      const clearActiveTimer = () => {
        clearInterval(intervalId);
        clearTimeout(timeoutId);
      };
  
      /**
      * Clears timers and logs a warning message when the MailChimp modal container is not
      * found within the specified timeout.
      */
      const mailChimpModalContainerNotFound = () => {
        clearActiveTimer();
        console.warn("MailChimp modal container not found within the specified timeout. Disable form tracking?");
      };
  
      /**
      * Checks if the MailChimp modal container exists in the DOM.
      */
      const checkIfMailChimpContainerExists = () => {
        try {
          const mailChimpContainer = document.getElementById("PopupSignupForm_0");
          if (mailChimpContainer && mailChimpContainer.offsetParent !== null) {
            clearActiveTimer();
            callbackFunction();
          }
        } catch (error) {
          console.error("Error while checking for MailChimp modal container:", error);
        }
      };
  
      const intervalId = setInterval(checkIfMailChimpContainerExists, intervalDuration);
      const timeoutId = setTimeout(mailChimpModalContainerNotFound, timeoutDuration);
    };
  
    /**
    * Extracts the form ID from the form action URL.
    * @param {string} formAction - The form action URL.
    * @returns {string|null} - The extracted form ID or null if an error occurs.
    */
    const extractFormId = (formAction) => {
      try {
        const urlParams = new URLSearchParams(formAction.split("?")[1]);
        return urlParams.get("f_id");
      } catch (error) {
        console.error("Error while extracting MailChimp modal form ID:", error);
        return null;
      }
    };
  
    /**
    * Handles the form view event for the MailChimp modal form.
    * @param {HTMLFormElement} mailChimpModalForm - The MailChimp modal form element.
    */
    const handleFormView = (mailChimpModalForm) => {
      try {
        const formId = extractFormId(mailChimpModalForm.action);
        if (formId) {
          window.dataLayer = window.dataLayer || [];
          window.dataLayer.push({ event: "form_view", formType: "mailchimp_modal", formId: formId });
        }
      } catch (error) {
        console.error("Error while handling MailChimp modal form view:", error);
      }
    };
  
    /**
    * Handles the form submit event for the MailChimp modal form.
    * @param {HTMLFormElement} mailChimpModalForm - The MailChimp modal form element.
    */
    const handleFormSubmit = (mailChimpModalForm) => {
      try {
        const formId = extractFormId(mailChimpModalForm.action);
        if (formId) {
          window.dataLayer = window.dataLayer || [];
          window.dataLayer.push({ event: "form_submit", formType: "mailchimp_modal", formId: formId });
        }
      } catch (error) {
        console.error("Error while handling MailChimp modal form submit:", error);
      }
    };
  
    /**
    * Callback function for the MutationObserver to handle the visibility of the MailChimp modal.
    * @param {MutationRecord[]} mutationList - The list of mutations observed.
    * @param {MutationObserver} observer - The MutationObserver instance.
    * @returns {boolean} - Returns true if the MailChimp modal is visible and the form is found.
    */
    const onMailChimpModalVisible = (mutationList, observer) => {
      for (const mutation of mutationList) {
        if (
          mutation.type === "attributes" &&
          mutation.target?.className === "mc-modal" &&
          mutation.target?.style?.cssText.indexOf("display: block") > -1
        ) {
          try {
            const mailChimpModalForm = document.querySelector("#PopupSignupForm_0 .mc-modal iFrame")?.contentDocument?.querySelector("form");
            if (mailChimpModalForm) {
              handleFormView(mailChimpModalForm);
              mailChimpModalForm.addEventListener("submit", () => handleFormSubmit(mailChimpModalForm));
              observer.disconnect();
              return true;
            }
          } catch (error) {
            console.error("Error while handling Mailchimp modal visibility:", error);
          }
        }
      }
    };
  
    /**
    * Attaches a MutationObserver to observe the visibility of the MailChimp modal.
    */
    const attachMailChimpObserver = () => {
      try {
        const visibilityConfig = { attributes: true, subtree: true, attributeFilter: ["style"] };
        const mailChimpVisibilityObserver = new MutationObserver(onMailChimpModalVisible);
        mailChimpVisibilityObserver.observe(document.querySelector("#PopupSignupForm_0 .mc-modal"), visibilityConfig);
      } catch (error) {
        console.error("Error while attaching MailChimp modal observer:", error);
      }
    };
  
    waitForMailChimpModalContainer(attachMailChimpObserver);
  
  })();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment