Marketo Form Event Listener: Track Valid Submissions
Marketo forms need a listener that waits for validation to pass. Here is the code, and why the small delay in it matters.

Marketo forms need a slightly different approach. There is no clean success event to listen for, so the listener watches the form for validation errors and only reports a submission once it is satisfied there are none.
This post explains how that works and why the small delay in the code matters.
How this listener works
It does three things:
Polls the page every 500 milliseconds looking for forms whose ID starts with mktoForm_.
When it finds them, it stops polling and attaches a submit listener to each.
On submit, it waits 100 milliseconds, then checks whether any element has the mktoInvalid class. If none does, it pushes the event.
The polling exists because Marketo forms load asynchronously. The form often does not exist when your listener first runs, so waiting for it is the only reliable approach.
The listener code
This is the Marketo form listener from DumbData.
The full listener is in the copy block at the bottom of this page, with a copy button. It is reproduced exactly as DumbData publish it, so you can copy it straight from here or from their page.
Why the 100 millisecond delay matters
Marketo applies its validation classes after the submit event fires, not before.
If the listener checked immediately, the mktoInvalid classes would not be there yet and every submission would look valid, including the failed ones. The short delay lets validation finish first.
It is a small detail and it is the whole reason this listener is accurate. Do not remove it.
What it does and does not guarantee
It tells you the form passed client side validation. It does not confirm Marketo accepted the submission on its side.
In practice these are almost always the same. If you are reconciling against Marketo’s own numbers and find a small gap, this is where it comes from.
Installing it in GTM
Create a Custom HTML tag and paste the listener.
Trigger on the pages with Marketo forms.
Save and Preview.
Because it polls, it is safe to fire on page load even if the form appears later.
Creating the trigger and variable
Custom Event trigger, event name marketo_form_submission. Save as CE Marketo Submission.
Data Layer Variable named formID. Call it DLV Marketo Form ID.
The form ID comes through as the full element ID, such as mktoForm_1234, which is enough to tell your forms apart in reporting.
Sending it to GA4
Create a GA4 Event tag.
Event name: generate_lead
Trigger: CE Marketo Submission.
Sending conversions to Google Ads
Create a conversion action in Google Ads and note the ID and label.
Add a Google Ads Conversion Tracking tag with those values.
Trigger: CE Marketo Submission.
Meta and everything else
Same trigger, different tag. A Meta Pixel tag with the Lead event, Mixpanel or Piwik PRO from their GTM templates, or a LinkedIn or Microsoft conversion tag.
Testing
Open GTM Preview on a page with a Marketo form.
Submit with a required field empty and confirm nothing fires.
Submit properly and confirm marketo_form_submission fires once.
Check formID is populated.
Confirm the lead arrived in Marketo.
The invalid submission test is the one that proves the validation check is working.
Common issues
Nothing fires. The form ID may not start with mktoForm_, which happens with some custom embeds. Check the actual ID in developer tools.
Fires on invalid submissions. The 100 millisecond delay was removed, or Marketo is applying a different invalid class in your instance.
Polling never stops. The listener clears its interval once it finds forms. If there is no Marketo form on the page it keeps polling, which is harmless but worth limiting the tag to the right pages.
Privacy considerations
Never send names, emails, phone numbers or free text into GA4.
Hash anything you send to an ad platform for matching.
Gate the tags behind marketing consent: Consent Mode v2 in GTM
Final thoughts
Marketo is one of the fiddlier platforms and this listener handles the fiddly parts well. Leave the delay alone, limit the tag to pages that have forms, and test the invalid case.
Listener from DumbData. Their full library is worth a bookmark.
Need this built properly?
The 100 millisecond delay is the whole trick. Leave it in. I set up and repair tracking for ecommerce stores and lead generation businesses, so the numbers in your ad accounts match the money in your bank account.
Have a look at what I do, or book a call and we can go through your setup together.
<script type="text/javascript">
var pollFormElementsIntervalId;
function pollFormElements() {
var a = document.querySelectorAll("form[id^='mktoForm_']");
if (a instanceof NodeList) {
clearInterval(pollFormElementsIntervalId);
for (var b = 0; b < a.length; b++) attachEventListener(a[b])
}
}
function attachEventListener(a) {
a.addEventListener("submit", function(b) {
setTimeout(function() {
var c = 0 === a.querySelectorAll(".mktoInvalid").length;
c && window.dataLayer.push({
event: "marketo_form_submission",
formID: a.id
})
}, 100)
})
}
pollFormElementsIntervalId = setInterval(pollFormElements, 500);
</script>


















