We have a lot of clickable CTA elements on our website (sidebar graphics, download buttons, hyperlinks, etc) that contain a Marketo form id via the CTA element's data-attribute. When a CTA element is clicked, the form id is retrieved from the data-attribute, and then using the JavaScript API loadForm method we render the form inside of a panel that slides up from the bottom of the page.
This functionality has worked well for months, until recently when we discovered an issue that if the user rapidly clicks the CTA multiple times, the form children elements (input fields, labels, submit buttons, etc) are getting duplicated. The problem seems to be that if the forms script is aborted before it has a chance to render the form, when executed again it appends the form elements from the previous call to the current form object, causing duplicates of the form's elements to get loaded.
I've tried all the typical methods I would think would fix the issue (disabling the element that gets clicked, setting flag variables, timeouts, etc.) to ensure the loadForm script isn't being called multiple times, but maybe I should be looking at using a different method of retrieving/rendering the form?
I've created a codepen displaying the issue here: https://codepen.io/jonbenwaa/pen/aWapvL
It may take a few tries to get it to occur, but it will happen if you rapidly click on the of the sidebar images fast enough. Also I attached a screenshot of it occurring as well so you can see what I'm talking about. Any help is appreciated.
Solved! Go to Solution.
Thanks! I was actually looking over your pens before making this post, Sanford Whiteman 2 (FigureOne)
It's actually Sanford Whiteman for the future . (That's one of my other accounts.)
Before I start rewriting my actual code to restructure the Marketo methods, I wanted to ask you a few things on your pen-
- Is the a#doClix element you added in the HTML along with the click listener (JS line 48) just an example of triggering the getMktoForm method?
- Lastly, what is going on with the array you're iterating through in the jQuery click event? I know I'm a little behind on my ES6 syntax, but I don't get what's going on with a 1-5 loop inside the click event.
Woopsy, I just deleted that stuff. That was debug code. Nothing to worry about!
- is there logic in the loadForm method that looks to see if there is a form id already on the page? Is that why clearing it in whenReady helps resolve this issue
Yep. It's in the internal XHR callback from loadForm. So you can't intercept it directly, but if there's no form with the matching ID it becomes a no-op.
Thanks man. On it as soon as I get done with some other stuff.
Yep, Forms 2.0 isn't fully evented for a deployment like this, and you have a race condition. But you can vastly reduce the real-world cases by clearing the form id whenReady, as shown here:
MktoForms2 :: <form> singleton
Your code also had an inevitable memory leak warning (not necessarily an actual memory leak, depending on the browser) because you were constantly adding to the event stack. No need to do that: you should pull the whenReady out of the instantiation routine, as in my Pen. Also a lot less wear-and-tear on the DOM that way, FWIW.
Thanks! I was actually looking over your pens before making this post, Sanford Whiteman 2 (FigureOne), so that's awesome that you're the one responding.
Before I start rewriting my actual code to restructure the Marketo methods, I wanted to ask you a few things on your pen-
Regardless, I appreciate your help and have enough to get started on the real code.
Thanks again
Thanks! I was actually looking over your pens before making this post, Sanford Whiteman 2 (FigureOne)
It's actually Sanford Whiteman for the future . (That's one of my other accounts.)
Before I start rewriting my actual code to restructure the Marketo methods, I wanted to ask you a few things on your pen-
- Is the a#doClix element you added in the HTML along with the click listener (JS line 48) just an example of triggering the getMktoForm method?
- Lastly, what is going on with the array you're iterating through in the jQuery click event? I know I'm a little behind on my ES6 syntax, but I don't get what's going on with a 1-5 loop inside the click event.
Woopsy, I just deleted that stuff. That was debug code. Nothing to worry about!
- is there logic in the loadForm method that looks to see if there is a form id already on the page? Is that why clearing it in whenReady helps resolve this issue
Yep. It's in the internal XHR callback from loadForm. So you can't intercept it directly, but if there's no form with the matching ID it becomes a no-op.
Actually scratch the second question, I see in the API reference that
"If there is already a form that is ready at the time this function is called, the passed callback will be called immediately."
So after seeing your solution, I'm assuming this looks for a form element with an existing id- so clearing the id forces it to reinitialize it?
Geez you're fast lol. But thanks for the clarification!
So after seeing your solution, I'm assuming this looks for a form element with an existing id- so clearing the id forces it to reinitialize it?
Actually, your first instinct was correct, which is that it relies on undoc'd behavior, not anything in the published event model.