Hi All, we're facing a very weird and inconsistent behaviour with Marketo hidden fields in regards of capturing the data.
Issue started few months back, when we implemented capturing the UTM values as cookies on our website. We created hidden fields in Marketo to track those cookie values and store in Marketo.
It was working with a hit or miss. Then we reached out to Marketo and they said, your UTM cookies set after the user cookie consent and till the time marketo form is already loaded and that's why it's not capturing the cookie values in these kind of cases.
To overcome this issue, now I check if user has accepted the cookie consent or not, as soon as consent is accepted, I am firing a event on site, which then loads the marketo form using Form API addHiddenFields method and already populating the hidden fields values by code itself like this:
Now it looks it works fine. All the cookie values I can see on in the form every time form loads on site. Then I did a lot of testing by submitting so many forms on site and then checking in Marketo if values are getting captured, and there as well I didn't see any issue and all values are getting captured fine.
Now the issue is, we're still seeing blank values for those hidden fields in marketo every month when we analyse the real user data.
Seems like Marketo is still failing to capture hidden field values each time.
Can anyone please help if they have faced these kind of issues before? This is going on for so long now and we really want to get rid of it.
Attaching here whole code which is responsibe for rendering the form on our site:
import { navigate, useLocation } from "@reach/router";
import React, { useContext, useEffect, useRef, useState } from "react";
import getPathWithQueryParam from "../../../helpers/getPathWithQueryParam";
import ResourcesContext from "src/components/helpers/ResourcesMainPageContext";
import destyleMktoForm from "src/components/helpers/destyleMktoForm";
import styleMarketoForm from "src/components/helpers/styleMarketoForm";
import getCookie from '../../../helpers/getCookieParser';
import createId from "src/components/helpers/createId.js";
/**
*
* @param {string} serial - XXX-XXX-XXX serial key unique to each form
* @param {number} id - number also unique to each form
* @param {function} callback - function to be called after all the basic styles are set for further styling
* @param {string} className - className to be added into the form element
* @param {string} followUpUrl - it contains redirect path for after success of form submission
* or extra functionality
* @callBack receives these parameters in order
* @param form the `form` object from Marketo API, which returns from whenReady() function.
* @param allInputElements -> same as above
* @param formElement -> same as above
*
* @returns {Object}
* @param formElement -> the whole form taken with the function getFormElem() from Marketo API, used to select each input individually
* @param allInputElements -> an array with all the input elements inside the form to add common styles for all of them at once
* @param formTag -> the <form> HTML tag with the correct ID to be rendered in the DOM
*/
const useMarketoForm = (
serial,
id,
callback,
className = "",
gaLabel = "Content",
isVisible=true,
followUpUrl = ""
) => {
const location = useLocation()
let formElement;
let allInputElements = [];
const formTag = <form id={`mktoForm_${id}`} ga-label={gaLabel} className={className}></form>;
const formSubmissionUniqueID = {"formSubmissionUniqueID": createId()}
const successRef = useRef();
const [hasSubmitted, setHasSubmitted] = useState(false);
const callLoadFormOnlyOneTime = useRef(0);
const callDataLayerOnlyOneTime = useRef(0);
const { otBannerDismissed } = useContext(ResourcesContext);
const addHiddenFields = formId => {
if (otBannerDismissed) {
setTimeout(() => {
const fetchedCookies = getCookie()
const validFetchedCookies = (typeof fetchedCookies !== 'undefined') && fetchedCookies.filter(obj => {
const keys = Object.keys(obj);
return keys.length > 0 && keys[0] !== "" && (keys[0].includes("utm"));
});
const utmParams = validFetchedCookies && validFetchedCookies.reduce((acc, obj) => {
const key = Object.keys(obj)[0];
const value = obj[key];
acc[key.replace(/(^|_)./g, s => s.slice(-1))] = `${value}`
return acc;
}, {});
if (utmParams) {
window.MktoForms2.getForm(formId).addHiddenFields(utmParams);
}
}, 1000);
}
}
const renderMktoForm = () => {
const headScripts = Array.from(document.querySelectorAll("script"));
const hasMarketoScript = headScripts.some((script) =>
script.src.includes("//info.accruent.com/js/forms2/js/forms2.min.js")
);
if (!hasMarketoScript) {
const marketoScript = document.createElement("script");
marketoScript.src="//info.accruent.com/js/forms2/js/forms2.min.js";
marketoScript.onload = () => {
loadForm();
};
marketoScript.async = true;
document.body.appendChild(marketoScript);
} else {
loadForm();
}
}
useEffect(() => {
renderMktoForm()
}, [isVisible, serial, otBannerDismissed])
const loadForm = () => {
// Render multiple Marketo forms on a single page
const cookies = getCookie();
if (window.MktoForms2 === undefined) {
setTimeout(() => {
loadForm();
}, 500);
}
if (
serial &&
id &&
window.MktoForms2
) {
if (callLoadFormOnlyOneTime.current === 0) {
// Setting this ref to 1 makes sure the function `loadform()` runs only once,
// so there is no duplicate form on the page
callLoadFormOnlyOneTime.current = 1;
window.MktoForms2.loadForm(
"//info.accruent.com",
`${serial}`,
`${id}`,
(form) => {
if(!form){
console.info("window.MktoForms2.loadForm was passed an empty form object")
return false;
}
// Adding new hidden field to add unique ID to each submission
form.addHiddenFields(formSubmissionUniqueID)
formElement = form.getFormElem()[0];
// set id to checkbox parent
formElement?.querySelectorAll('input[type="checkbox"]')?.forEach((element,i) => {
element?.parentNode.setAttribute("id","mktoCheckbox_"+i);
});
// takes all styles off the form
destyleMktoForm(form);
if (
window &&
window.dataLayer &&
callDataLayerOnlyOneTime.current === 0
) {
callDataLayerOnlyOneTime.current = 1;
window.dataLayer.push({
event: "gaTriggerEvent",
gaCategory: "CTA Engagement",
gaAction: "Form Load",
gaLabel: gaLabel,
});
form.onSuccess(function(values, marketoFollowUpUrl) {
window.dataLayer.push({
event: "gaTriggerEvent",
gaCategory: "CTA Engagement",
gaAction: "Form Submission",
gaLabel: gaLabel,
});
if(!(id == calendlyFormId)){
setHasSubmitted(true);
if(followUpUrl){
navigate(getPathWithQueryParam(location, followUpUrl, cookies))
}
else if (marketoFollowUpUrl) {
window.location.href = marketoFollowUpUrl;
} else if (successRef.current) {
successRef.current.scrollIntoView({
behavior: "smooth",
block: "center",
inline: "nearest",
});
}
}
return false;
});
}
styleMarketoForm(formElement, allInputElements);
// CALL BACK
if (callback) {
callback(form, allInputElements, formElement, formTag);
}
}
);
} else {
addHiddenFields(id)
}
}
};
if (serial && id) {
if (hasSubmitted) {
return { formElement, allInputElements, formTag };
} else return { formElement, allInputElements, formTag };
} else return { formElement: null, allInputElements: null, formTag: null };
};
export default useMarketoForm;
Solved! Go to Solution.
The code you’ve posted is server-side code, with imports we can’t possibly reproduce. Please put up a demo page running your code so we can take a look at it live.
From first glance I can say setTimeout is never the way to solve race conditions and is almost certainly the core problem with your code.