Skip navigation

Do It

2 Posts authored by: freejung

I've been struggling with form spam recently, and found a solution that I like and thought would be worth posting about. I tried the "honeypot" technique described in this post, but I found that some of the bots attacking my forms were sophisticated enough to detect that the honeypot field is hidden, even if I used CSS or JavaScript to hide the field. So I tried a modified version of this technique which seems to be working well.

Step 1: create a "honeypot" field

  • Create a new text field in your form (not a hidden field, we'll hide it with JS later).
  • The field label should be a question with an answer that would be obvious to any human, such as "what color is the sky?" If you're not confident that it's obvious enough, you can put something in the field instructions that says "just say blue." This is necessary so that the form will work with JavaScript disabled.
  • Set up your form processing steps to run conditionally only if the value of this field is "blue" or whatever the correct answer is. That way submissions with the wrong answer will not be added to the database.
  • You might want to add a form processing step to send you a notification email if the form is filled out with the wrong answer so that you can monitor the spam submissions and make sure it's working well. You can always turn that off later.


Step 2: hide the field using JavaScript


  • Don't use CSS to hide the field, because you want it to be visible if JavaScript is disabled so that the submitter can correctly answer the question.
  • Use JavaScript to set the "display" property of the field (or preferably, one of its parent elements) to "none." See sample jQuery code below.
  • If you're using the default Eloqua 10 form layout, the easiest way to do this is to hide the div with id "formElementX" where X is the number of the order that the field appears on the form, starting from 0.


Step 3: populate the hidden field using a JavaScript event


  • The idea here is to use JavaScript to fill in the correct answer to the hidden field when the user takes an action that any human would have to take in order to fill out the form.
  • I settled on using the "focus" event on all form elements because even if the user is using their browser to auto-fill the fields, they will still have to place focus on some form element in order to submit the form.
  • If you use some other event, make sure that it's an event that any human user must trigger in order to submit the form, but not one that is automatically triggered when the page loads (such as window load) because some bots may be smart enough to execute it.


Example Code


This example uses jQuery, so you'll need to make sure you're loading the jQuery library on the page. If you're using an externally hosted landing page and you're already using a different version of jQuery, remove the first line in the code. You can replace the first line with whatever version of jQuery you prefer to use. This code also assumes you are using the default Eloqua 10 form code, but it could easily be adapted to whatever form layout you're using.

Insert this code into the <head> section of your document if you're using an external form, or into the JS field in the Tools bar if you're using an Eloqua 10 landing page.


<script type="text/javascript" src=""></script>
<script type="text/javascript">


Be sure to replace "formElement7" with the id of your honeypot field's parent div (remember the fields are labeled starting from 0). Replace "sky" with the HTML name of your honeypot field, and "blue" with the correct answer to your obvious question.


So far this is working well for me - I'll post updates if I find that there are problems or if I want to recommend modifications to the technique.

Update August 5 2014: fixed bug that was causing jquery.validate to fail in some situations. Fixed bug causing problems when the channel field field is not specified - now you can just set channelField=0 and it will ignore the channel tracking feature. Also added a version number (previous versions were not numbered) - the current version is 1.5.

Update May 5 2014: fixed critical bug in prepop which caused the callback function to fail when the visitor has no cookie.

Update July 31, 2013: Critical update with two crucial bug fixes: fixed the script comments to give correct instructions for the theseFields array, and added a parameter visitorEmailField because apparently different instances of eloqua use different field names for the visitor data lookup email address. I also added a line to the instructions that shows how to set it up so that if the visitor is new (has no Eloqua cookie) and they fill in their email address, the whole script runs again, populates the form according to the new email address, and runs progressive profiling accordingly.

To find out what your visitorEmailField should be set to, you'll have to look at the data lookup scripts for the cookie data lookup and see what the field names are. It should be something like V_Email_Address or V_ElqEmailAddress.

Update July 25, 2013: I have posted a new version which fixes several critical bugs in the previous version. Please get the latest version below if you're still using any of the older versions.

Update July 19, 2013: this script has been updated to use the new Eloqua tracking scripts. I have added support for radio buttons and for using any field (not just the first field) for the email address. Also note that skip rules are now indexed by field name, not by number.


----------------------begin original post-----------------------


First post.


I'd like to share with you a script I call Progress Pro - a script that uses jquery and data lookups to implement advanced progressive profiling for Eloqua 10.


You can download the script by clicking on the attachment at the bottom of this post.


Progressive profiling is a technique whereby visitors are asked different questions each time they fill out a form.  The idea is to start by asking a few simple questions, for example email address and name, and gradually learn more about the visitor as they perform subsequent registrations. This reduces the barrier to entry into your marketing campaign, while still allowing you to collect detailed information on your most interested prospects.


The Problem


Eloqua 10 has a cloud component for progressive profiling. If you want to implement progressive profiling on an Eloqua-hosted landing page, I suggest you start with that. See this post:


However, this solution only works for forms hosted on Eloqua landing pages. It also has some limitations, although it is generally pretty good. But if you want to do progressive profiling on your own forms, you will need to implement some sort of custom solution.


The desired solution would work for visitors from any channel, use only one form, and enable advanced dynamic skip rules while still allowing easy implementation for simple use cases. It would dynamically pre-populate the form based on browser cookie or email address and ask questions conditionally according to the prospect's previous answers. Incidentally, it would also be useful to track the incoming channel and store it in a contact field. Progress Pro implements such a solution.




Eloqua has an amazingly awesome feature called Data Lookups. I won't go into the details, as the feature is well documented. This feature allows you to access data from your Eloqua database using javascript. Once you have data on the visitor, you can use javascript to manipulate the form accordingly. The methodology is:


  •     Create a long form, beginning with email address, containing all of the questions you want to ask in the entire profiling process. Set the email address field to prefill, but don't make any of the fields required.
  •     Create two data lookups: one for looking up the email address by tracking cookie, and one for looking up the rest of your data by email address. These lookups are performed by Progress Pro using jquery.getScript to load a script with appropriate parameters from Eloqua.
  •     If the visitor comes in from an email, their email address will be prefilled. Progress Pro uses this to perform the lookup by email address and prefill the form with data accordingly.
  •     If the vistor comes from another channel and has an Eloqua tracking cookie, Progress Pro performs a data lookup by cookie to get the email address, then performs another lookup by email address to get the rest of the data and prefill the form.
  •     If neither of these is the case, the visitor will begin by filling in the email address field. Progress Pro attaches a jquery.change handler to the email field, and performs the lookup by email address once it has been filled in. If the visitor is not in the database, the rest of the form obviously stays empty.
  •     Progress Pro then hides some of the questions on the form according to parameters you specify. In the basic setup, you simply specify a number of fields at the top of the form that should always be shown, and the number of unanswered questions you want to ask. Each time the visitor returns, they will be asked a new set of unanswered questions until they have completed the entire form.
  •     For more advanced use cases, you can specify an array of conditional skip rules; e.g. if the prospect indicates interest in a particular product in the answer to one of the questions, skip all questions having to do with other products. These rules are implemented dynamically - as soon as the prospect selects an answer to the question on which the rule depends, the form immediately alters accordingly.
  •     Progress Pro uses jquery.validate to validate the form. This is necessary because only visible fields can be required, or the form will always fail validation. You specify an initial set of validation rules, which are modified dynamically according to which fields are shown.
  •     To track the incoming channel, you can use an optional URL parameter called "ch" - your form needs to contain an hidden field called "Channel History" which will be populated with whatever channel you specify in the URL.




To use this script, first upload it to your website (or wherever else you want to host it). Then, in the <head> section of your landing page, add javascript code similar to the following:


//---------begin code-------------

<script type="text/javascript" src=""></script>

<script type="text/javascript" src=""></script>

<script type="text/javascript" src=""></script>

<script type="text/javascript" src=""></script>

//-----------end code--------------


This is to load the necessary script files. Obviously replace "mydomain" with your domain, and replace "assets/js" with the path to your copy of Progress Pro.


You should also make sure you have installed the Eloqua visitor tracking code, which you can find in Eloqua under Setup->Website->Tracking.


Then you set up a document.ready function to call the function prePop and prepopulate the form. You will need to specify which field to use for the email address. As a callback from the prePop function, optionally call addChannel() to record the incoming channel in the Channel History field, and call the function progressiveProfile to hide fields as appropriate. Be sure to call the latter two functions as callbacks from prePop or they will not work.


In the most basic usage, just specify the number of unanswered questions to ask and the number of fixed questions to always show at the top. For this example we will set both to 3. For basic usage, just specify the skip rules as an empty array.


You will need to provide the data lookup keys for your data lookup by cookie and by email. These will be generated by Eloqua when you set up the data lookups. You will also need to provide an array of form fields (indexed starting at 0) with the names of the database fields corresponding to each question. These names can be found in the fields setup in Eloqua. Also provide a set of jquery.validate validation rules (see In this example, all fields are required.


//------------begin code--------------------------

<script type="text/javascript">

  $(document).ready(function() {


  var elqDLKey_Cookie = escape('###');

  var elqDLKey_Email = escape('###');

  var theseFields = ['C_EmailAddress', 'C_FirstName', 'C_LastName', 'C_Company','C_Primary_Interest1', 'C_BusPhone', 'C_Website1', 'C_Lead_Role1', 'C_Function1', 'C_Title', 'C_Company_Type1', 'C_Project_Time_Frame1'];

  var popFields = ['C_FirstName', 'C_LastName', 'C_Company','C_Primary_Interest1', 'C_BusPhone', 'C_Website1', 'C_Lead_Role1', 'C_Function1', 'C_Title', 'C_Company_Type1', 'C_Project_Time_Frame1'];

  var openQuestions = 4;

  var fixedQuestions = 1;

  var thisForm = 'form21';

  var channelField = 0;

  var emailField = 'C_EmailAddress';

  var visitorEmailField = 'V_ElqEmailAddress';

  var myValidationRules = { rules: {C_FirstName: {required: true}, C_LastName: {required: true},

      C_Title: {required: true}, C_Company: {required: true}, C_Website1: {required: true},

      C_Primary_Interest1: {required: true},C_Project_Time_Frame1: {required: true},C_Lead_Role1: {required: true},C_Function1: {required: true},C_Company_Type1: {required: true},

      C_BusPhone: { required: true, phoneUS: true }, C_EmailAddress: { required: true, email: true } } };

  var mySkipRules = {};

  prePop(thisForm, theseFields, elqDLKey_Cookie, elqDLKey_Email, emailField, visitorEmailField, function(){

      progressiveProfile(openQuestions, fixedQuestions, thisForm, theseFields, popFields, elqDLKey_Cookie, elqDLKey_Email, emailField, channelField, myValidationRules, mySkipRules);





//--------------end code-------------


For advanced usage, the skip rules are specified as an array. You set which field (by name) is to be hidden or shown, the field value the rule depends on, an operator, and a condition to match the "depends" field against. In other words, the rule states something like "hide this field if this other field contains 'California'" or perhaps "show this field if this other field equals 'yes'". You can specify multiple skip rules for each field. As a simple example, suppose we want to hide field 'C_HR_When1' if field 'C_Product_Family1' has the value "yes." The skip rule for this looks like:


//---------begin code-----------

var mySkipRules = {'C_HR_When1': {1: {action: 'hide', depends: 'C_Product_Family1', operator: 'eq', condition: 'yes'}}};

/----------end code ---------------

The possible actions are "hide" and "show" where show takes priority over hide if the rules contradict. The possible operators are "eq" for equals, "neq" for not equal to, "contains" and "always" - always means the action will always be taken regardless of the values of other fields.


Et voila! We have progressive profiling that works for all visitors, uses a single form, and supports both simple cases and more advanced conditional rules.


I hope you find this useful. If you have any questions, comments or problems with this, please don't hesitate to contact me:


[EDITOR'S NOTE: As this is custom code, the Eloqua support team will not be able to provide support or help you troubleshoot this code if you implement it. We recommend that you take Eli up on his offer and add a comment to this message and/or email him directly if you run into questions.]

Filter Blog

By date: By tag: