Use JavaScript with CTA/Form Elements

Hello all,

We are using a CTA element holding a Hubspot form to display it on a Hubspot landing page. This works perfectly fine, but is loaded as an iFrame on the website.

As the Hubspot landing page holds our Google Tag Manager container we are looking for a way to send data from the iFrame to the parent page and look for a solution to send a postMessage call from the CTA/form element whenever the form is sucessfully submitted.

Is there any way to implement this? We cannot find options or elements to use custom JavaScript in CTA/forms, but we are aware of using global HTML/JS embeds and are wondering if this can help us here.

Best regards,
Manuel

Hi @MTrykar and welcome, we are thrilled to have you here! :orange_heart:
I’d love to ask some clarifying questions so that the Community can help you in the best way!
- Are the form and landing page on the same domain and protocol?
- Have you tested if any postMessage events are received by the parent page?
- Is the form added as a native HubSpot CTA or through an external embed/code snippet?
I’d love to put you in touch with our Top Experts: Hi @Anton, @TomM2 and @evaldas do you have suggestions to help @MTrykar, please?
Thanks so much and have a lovely weekend!
Bérangère

Hello and thank you for your answer! And I am happy to provide the missing information:

- Are the form and landing page on the same domain and protocol?

Here is an example landing page: Kuhgesundheit vorbeugend managen mit smaXtec. However, the form seems to be hosted on another domain: https://145796108.hs-sites-eu1.com/hs-web-interactive-145796108-281838878928?utk=6ad3c860f04c42f0c21e0d33f367ef5b&enableResponsiveStyles=true

- Have you tested if any postMessage events are received by the parent page?

This is exactly what we try to achieve, but could not find a way to implement the postMessage API anywhere on the CTA/Form element. Any suggestions?

- Is the form added as a native HubSpot CTA or through an external embed/code snippet? On the landing pages it is added as CTA element:

I hope this helps!

Best regards,

Manuel

Hi @MTrykar,

You might want to try the Global Form events:

https://developers.hubspot.com/docs/api-reference/global-form-events/guide

Here is a sample from the documentation that should capture a form submission, which you could then use to communicate with Google Tag Manager:

window.addEventListener("hs-form-event:on-submission:success", event => {
 console.log("User successfully submitted the form!");
});

Hope this helps!

Hello and thanks for your help! We are using this for global forms on the website, but cannot use it for Hubspot landing pages as the forms there are implemented as iFrame.

Website implementation (form tracking working): Kuhgesundheit vorbeugend managen mit smaXtec

Landing Page implementation (form tracking not working): Kuhgesundheit vorbeugend managen mit smaXtec

We look for a solution to make the second example work as well by sending data from the iFrame (CTA/Form) to the landing page.

Thank you for sharing the examples and clarifying. Yes, it appears that the global form events are not able to help here.

In this case you may want to cast a wider net with the event listener. Here is a sample that can help you latch onto the iframe popup form submission:

window.addEventListener("message", (event) => {
 if (typeof event.origin !== "string" || !event.origin.includes("hs-")) return;

 const msg = event.data;

 if (msg?.type === "hsCallsToActionCallback" && msg?.eventName === "onCallToActionFormSubmitted") {
 console.log("CTA form submitted ...", {
 ctaId: msg.id,
 formId: msg.data?.formId,
 email: msg.data?.submissionValues?.email,
 });
 return;
 }

});

For me this works on a HubSpot landing page and successfully logs submission details in the console.

The intial “if” statement is just for quick testing, to reduce the noise and filter messages coming from hubspot domains (i.e. hs-sites-eu1.com). You can adjust accordingly or simply remove it if you opt not to use any guardrails.

Hello and thank you for providing the solution. May I ask if you tested you script on our landing page Kuhgesundheit vorbeugend managen mit smaXtec and submitting a form?

For me submitting a form there does not send any messages to the console. Or did you test this in the iFrame itself? If so, my question is how I can add code to the iFrame in Hubspot?

Looks like you may have changed the form on that page? Either way, I tested the new form submission on your page and was able to capture that form submission in the console.

We may need to simplify the code a bit and add some preliminary checks to make sure it is properly running:

<script>
document.addEventListener('DOMContentLoaded', function () {

 console.log("READY");

 window.addEventListener("message", (event) => {

 const msg = event.data;

 if (msg?.type === "hsCallsToActionCallback" && msg?.eventName === "onCallToActionFormSubmitted") {
 console.log("CTA form submitted ...", {
 ctaId: msg.id,
 formId: msg.data?.formId,
 email: msg.data?.submissionValues?.email,
 });
 return;
 }

});

});

</script>

You would not add this to the iframe itself, but to the landing page, in the page settings Footer HTML:

When the landing page loads, you should see the “READY” in the console. Which means the lmessage listener should be ready to listen.

Then when you submit the form, there should be a console message logged with submission details.

Let me know the results after you try this.

Hello,

Thank you so much and this script works perfectly fine. One question: Why don’t you put in the <head> section of the landing page instead of the <body>?

And maybe you can help me with the final version of the script as I also want to track theonCallToActionReady event, but somehow the msg variable does not hold the same information as it does with the onCallToActionFormSubmitted event:

<script>
document.addEventListener('DOMContentLoaded', function () {

 window.addEventListener("message", (event) => {

 const msg = event.data;

 if (msg?.type === "hsCallsToActionCallback" && msg?.eventName === "onCallToActionReady") { 
 window.dataLayer.push({
 event: 'form_start',
 form:{
 name: msg.data?.formId,
 data: msg.data?.submissionValues
 }
 });

 return;
 }

 if (msg?.type === "hsCallsToActionCallback" && msg?.eventName === "onCallToActionFormSubmitted") { 
 window.dataLayer.push({
 event: 'form_submit',
 form:{
 name: msg.data?.formId,
 data: msg.data?.submissionValues,
 value: msg.data?.submissionValues.herdsize
 }
 });

 return;
 }

});
});
</script>

Here are the current values when loading and submitting the form on the landing page https://campaigns.smaxtec.com/de/som-paid-nord:

Best regards,
Manuel

Hi Manuel, glad to hear the form submission is working for you now.

Regarding your other questions, the reason to put this in the footer instead of <head> is to help prevent render blocking requests, which is something Google measures as a website performance metric. Since this script is not critical for page load and it will take a while for a user to submit the form, it does not need to load that soon.

As for tracking the “ready” state - two issues there:

1) I do not see “onCallToActionReady” being available in this case

2) Even if it was, you can’t pass submission values to a “ready” state because the submission has not happened on that event

What you could do is track the CTA open event with “onCallToActionViewed” and pass the ID of that event, but user visitor information will not be captured at this point.