Hi Marketo Community!
My website uses Wordpress and we embed Marketo forms 2.0 with some extra scripts to cookie the user after a form is submitted. This was working perfectly for a year, however, two weeks ago one of our embedded Marketo forms started to not submit consistently. Sometimes it submits and everything works and other times the user would be stuck on the form page with the button saying "Please Wait" grayed out. This causes problems for us because the Marketo form needs to submit so the cookies can be set for the user to continue to the next step in their trial.
We spoke with Markteo support and they don't know what the problem is so here's my cry for help. "HELLLPPPP!!!"
The script we're using with the form is provide in Marketo's Documentation (http://developers.marketo.com/javascript-api/forms/).
Here's the page we're having trouble with: https://www.scylladb.com/test-drive/ https://www.scylladb.com/test-drive/
And here's our form script...Any guidance on how to fix this is much appreciated. Thank you!
<form id="mktoForm_1114"></form>
<script>
(function() {
if (typeof(MktoForms2) === 'undefined') {
document.getElementById('mktoForm_1114').innerHTML = '<div style="padding-bottom: 20px; color: #ccc; font-style: italic">Form blocked - please disable any privacy plugins and reload the page in order to complete your registration (Privacy Badger, Adblock, etc)</div>'
}
else {
MktoForms2.loadForm("//app-ab17.marketo.com", "791-QBF-350", 1114);
MktoForms2.whenReady(function (form){
//Add an onSuccess handler
form.onSuccess(function(values, followUpUrl){
var email = jQuery('input[name=Email]').val();
var email_hash = jQuery('input[name=Email]').val() + '_3qrw56fyi30aeau';
email_hash = jQuery.md5(email_hash);
var name = jQuery('input[name=FirstName]').val() + ' ' + jQuery('input[name=LastName]').val();
var company = jQuery('input[name=Company]').val();
setCookie('email', email, 3650);
setCookie('email_hash', email_hash, 3650);
setCookie('name', name, 3650);
setCookie('company', company, 3650);
location.href = "/test-drive-thanks/";
return false;
});
});
}
})();
</script>
Solved! Go to Solution.
OK, figured it out after a debugging session.
First, realize this has nothing to do with the Forms 2.0 library. (I'm sure you'll understand that after I explain, but don't want anybody else to get the idea this is a Marketo bug.)
The direct cause, which was apparent just from the symptoms you reported, is that your custom onSuccess listener is throwing an error. When that happens, the form is stuck at "Please Wait" as you describe. It was clear that one of the functions you're calling -- and there are only a couple -- must be capable of throwing an error under some, but not all, conditions.
Looking at the md5 and setCookie functions, the functions themselves didn't show any cross-browser compatibility issues or anything that made them look specifically fragile (certainly setCookie is very straightforward, and md5 is venerable, trusted open-source code) .
But when I was able to trigger the failure after a few simulations, I found the error isn't within either of the functions, it's in finding the md5 function in the first place:
Hmm, so what have we here? The md5 function is intended to be attached as a property of the global jQuery object in your scripts.js file:
And indeed the function is often, but not always, attached as jQuery.md5 by the time the page finishes loading (which is also before the form submits, of course).
As long as it's attached, the onSuccess will not error out and you'll be fine. If it's not attached: failure.
But why isn't it always attached? Because you have a classic asynchronous race condition.
See, you've put the jQuery library <script> in your WordPress pages correctly, and it comes before the <script> that loads scripts.js (as is necessary for jQuery.md5 to be attached the first time).
But you're loading another script, the cookie consent library (https://cdn.cookielaw.org/consent/7f8998d2-7325-4c96-a530-f715928bb43e.js) and that's injecting a different copy of jQuery into the page, but doing it asynchronously:
The order of script loading is effectively random when you inject scripts from other scripts like this.
When both the 1st and 2nd versions of jQuery load, followed by scripts.js, you're fine. The 2nd jQuery overwrites the first, and md5 is attached to the 2nd one (the current window.jQuery global object)..
But when the 1st version of jQuery loads... then scripts.js... and then the 2nd version of jQuery, the 2nd jQuery overwrites the 1st to become window.jQuery and it's a fresh copy. It doesn't include the md5 property. That property was attached to the first jQuery which is overwritten. You can see the broken behavior in this timeline:
The solution, besides screaming at OneTrust because they shouldn't be blindly injecting jQuery like this, is to take the md5 function off the jQuery object. There's no reason for it to be on there as it uses no jQ functionality. It can be in its own namespace or just right on the window as window.md5. A related lesson is that jQuery should be used as little as possible. I don't use it all, having always hated it, unless there's an API that only works using jQuery wrappers.
Finally, though this is not related to the "Please Wait" problem, the way you're populating the cookies is also wrong. You should be using the Forms 2.0 API -- not jQuery methods, nor native DOM methods -- to read Marketo field values:
(function() {
if (typeof MktoForms2 === "undefined") {
document.getElementById("mktoForm_1114").innerHTML =
'<div style="padding-bottom: 20px; color: #ccc; font-style: italic">Form blocked - please disable any privacy plugins and reload the page in order to complete your registration (Privacy Badger, Adblock, etc)</div>';
} else {
MktoForms2.loadForm("//app-ab17.marketo.com", "791-QBF-350", 1114);
MktoForms2.whenReady(function(form) {
form.onSuccess(function(values, followUpUrl) {
var cookieableFields = {
email: values.Email,
email_hash: jQuery.md5(values.Email + "_3qrw56fyi30aeau"),
name: values.FirstName + " " + values.LastName,
company: values.Company
};
Object.keys(cookieableFields).forEach(function(fieldName) {
setCookie(fieldName, cookieableFields[fieldName], 3650);
});
location.href = "/test-drive-thanks/";
return false;
});
});
}
})();
Please
(a) highlight your code as JS using the Advanced Editor's syntax highlighter so it is readable
(b) link to the actual page on which you are experiencing this problem
Hi Sanford - hopefully the code is readable now and here's the page that's giving us trouble: https://www.scylladb.com/test-drive/
OK, figured it out after a debugging session.
First, realize this has nothing to do with the Forms 2.0 library. (I'm sure you'll understand that after I explain, but don't want anybody else to get the idea this is a Marketo bug.)
The direct cause, which was apparent just from the symptoms you reported, is that your custom onSuccess listener is throwing an error. When that happens, the form is stuck at "Please Wait" as you describe. It was clear that one of the functions you're calling -- and there are only a couple -- must be capable of throwing an error under some, but not all, conditions.
Looking at the md5 and setCookie functions, the functions themselves didn't show any cross-browser compatibility issues or anything that made them look specifically fragile (certainly setCookie is very straightforward, and md5 is venerable, trusted open-source code) .
But when I was able to trigger the failure after a few simulations, I found the error isn't within either of the functions, it's in finding the md5 function in the first place:
Hmm, so what have we here? The md5 function is intended to be attached as a property of the global jQuery object in your scripts.js file:
And indeed the function is often, but not always, attached as jQuery.md5 by the time the page finishes loading (which is also before the form submits, of course).
As long as it's attached, the onSuccess will not error out and you'll be fine. If it's not attached: failure.
But why isn't it always attached? Because you have a classic asynchronous race condition.
See, you've put the jQuery library <script> in your WordPress pages correctly, and it comes before the <script> that loads scripts.js (as is necessary for jQuery.md5 to be attached the first time).
But you're loading another script, the cookie consent library (https://cdn.cookielaw.org/consent/7f8998d2-7325-4c96-a530-f715928bb43e.js) and that's injecting a different copy of jQuery into the page, but doing it asynchronously:
The order of script loading is effectively random when you inject scripts from other scripts like this.
When both the 1st and 2nd versions of jQuery load, followed by scripts.js, you're fine. The 2nd jQuery overwrites the first, and md5 is attached to the 2nd one (the current window.jQuery global object)..
But when the 1st version of jQuery loads... then scripts.js... and then the 2nd version of jQuery, the 2nd jQuery overwrites the 1st to become window.jQuery and it's a fresh copy. It doesn't include the md5 property. That property was attached to the first jQuery which is overwritten. You can see the broken behavior in this timeline:
The solution, besides screaming at OneTrust because they shouldn't be blindly injecting jQuery like this, is to take the md5 function off the jQuery object. There's no reason for it to be on there as it uses no jQ functionality. It can be in its own namespace or just right on the window as window.md5. A related lesson is that jQuery should be used as little as possible. I don't use it all, having always hated it, unless there's an API that only works using jQuery wrappers.
Finally, though this is not related to the "Please Wait" problem, the way you're populating the cookies is also wrong. You should be using the Forms 2.0 API -- not jQuery methods, nor native DOM methods -- to read Marketo field values:
(function() {
if (typeof MktoForms2 === "undefined") {
document.getElementById("mktoForm_1114").innerHTML =
'<div style="padding-bottom: 20px; color: #ccc; font-style: italic">Form blocked - please disable any privacy plugins and reload the page in order to complete your registration (Privacy Badger, Adblock, etc)</div>';
} else {
MktoForms2.loadForm("//app-ab17.marketo.com", "791-QBF-350", 1114);
MktoForms2.whenReady(function(form) {
form.onSuccess(function(values, followUpUrl) {
var cookieableFields = {
email: values.Email,
email_hash: jQuery.md5(values.Email + "_3qrw56fyi30aeau"),
name: values.FirstName + " " + values.LastName,
company: values.Company
};
Object.keys(cookieableFields).forEach(function(fieldName) {
setCookie(fieldName, cookieableFields[fieldName], 3650);
});
location.href = "/test-drive-thanks/";
return false;
});
});
}
})();
You should be called the Exterminator with all the debugging you do. Brilliant.
Many thanks Sanford! It totally makes sense now.
Great, could you mark my answer as Correct?