DEV Community

Cover image for Automating AJAX Forms
Tommy
Tommy

Posted on

Automating AJAX Forms

While Autofill can autofill the majority of forms on the internet, there are more and more forms these days that it has a hard time with. These modern forms either use non-standard HTML form tags (e.g., <div> instead of <select>) or render forms dynamically via AJAX. Today, we're going to go over how to automate filling out a multi-page form that is powered by AJAX.

Since Autofill is typically triggered on page load, when a form uses the magic of AJAX to "load" the next page without actually loading the web page, Autofill is never triggered again. This is the reason why you might see Autofill working only on the first page and not subsequent pages. In order to properly autofill AJAX forms, we need to use Autofill's powerful JavaScript rules. For this tutorial, we will use Fundrise as a case study.

First, go to the Fundrise link above and enter any email address and password to create a dummy account. The multi-page form should start at the page below, and this is where we'll begin our automation:

Form page 1 screenshot

For the purpose of this tutorial, let's create a new profile called "Fundrise":

Manage Profiles

In this new profile, create a JavaScript rule (select Type = "JavaScript") and enter this into the Value field:

const fields = [
  { name: '[data-test="Family or Friend"]' },
  { name: '[data-test="TAXABLE"]' },
  { name: '[data-test="Income"]' },
  { name: '[data-test="$10K - $25K"]' },
  { name: '[data-test="3-5 years"]' },
  { name: '[data-test="I am more hands-on"]' },
  { name: '[data-test="first-name"]', value: 'First' },
  { name: '[data-test="last-name"]', value: 'Last' },
  { name: '[data-test="agreements"]' },
  { name: '[data-test="address-form-address1"]', value: '321 Test Dr' },
  { name: '[data-test="address-form-city"]', value: 'Beverly Hills' },
  { name: '[data-test="address-form-state"]', value: 'CA' },
  { name: '[data-test="address-form-zip"]', value: '90210' },
  { name: '[data-test="phone-number"]', value: '(310) 555-1212' },
  { name: '[data-test="date-input"]', value: '04/01/1984' },
  { name: '[data-test="amount-input"]', value: '10' },
  { name: '[data-test="other-amount-input"]', value: '10' },
  { name: '[data-test="manually-enter-account"]' },
  { name: '[data-test="new-bank-name"]', value: 'Big Bank' },
  { name: '[data-test="new-bank-routing"]', value: '123456789' },
  { name: '[data-test="new-bank-acct-1"]', value: '1234567890' },
  { name: '[data-test="new-bank-acct-2"]', value: '1234567890' },
  { name: '[data-test="acknowledgment-0"]' },
  { name: '[data-test="acknowledgment-1"]' },
  { name: '[data-test="acknowledgment-2"]' },
  { name: '[data-test="acknowledgment-3"]' },
  { name: '[data-test="acknowledgment-4"]' },
  { name: '[data-test="acknowledgment-5"]' },
  { name: '[data-test="acknowledgment-6"]' },
];

setInterval(() => {
  for (let i = 0, field; i < fields.length; ++i) {
    field = document.querySelector(fields[i].name);
    if (field) {
      if (fields[i].value) {
        field.value = fields[i].value;
        field.dispatchEvent(new Event('input'));
        field.dispatchEvent(new Event('change'));
      } else if (!field.checked) {
        field.click();
      }
    }
  }
}, 500);
Enter fullscreen mode Exit fullscreen mode

Whoa, that's quite a handful! Don't worry, I'm going to break it down for you. It's not so intimidating once you break it down into smaller chunks. Let's dive in...

const fields = [ ... ];
Enter fullscreen mode Exit fullscreen mode

Here we are defining all of the form fields that we want to automate. The [ ... ] contains a list of items, with each item having one or both of these properties:

  • name - the CSS selector that we will use to identify the fields.
  • value - the text that will be entered in input boxes.

As you'll soon see below, we will assume items that only contain the name property to be checkbox/radio elements that should be clicked on, and items that also contain the value property are input boxes that should be autofilled.

NOTE: Throughout this tutorial I will use the terms "field" and "element" to refer to the same thing — the form's <input> tag.

setInterval(() => { ... }, 500);
Enter fullscreen mode Exit fullscreen mode

We are going to execute everything inside { ... } every 500 milliseconds (half a second). Since the page never reloads as you move through the steps, what we are essentially doing is polling the page to detect when the form elements we want to fill out or click are added to the page. As with most things in JavaScript, there are other ways of doing this, such as using Mutation Observers, but for our needs good ol' setInterval() works fine.

for (let i = 0, field; i < fields.length; ++i) { ... }
Enter fullscreen mode Exit fullscreen mode

This loops through each item in the fields list.

field = document.querySelector(fields[i].name);
Enter fullscreen mode Exit fullscreen mode

Here we are querying the document for the form element we want to automate by using the CSS selector stored in the name property, and saving the first matching element to a variable called field for easier reference in the future.

if (field) { ... }
Enter fullscreen mode Exit fullscreen mode

Only proceed if the element actually exists in the document at the time of the polling. If not, then continue to the next item in the fields list.

if (fields[i].value) { ... }
Enter fullscreen mode Exit fullscreen mode

If the form element does exist, and the fields list item contains the value property, then assume the element is an input box that should be autofilled.

field.value = fields[i].value;
Enter fullscreen mode Exit fullscreen mode

Autofill the input box with the text stored in the value property.

field.dispatchEvent(new Event('input'));
field.dispatchEvent(new Event('change'));
Enter fullscreen mode Exit fullscreen mode

This is required on some forms to simulate a human manually entering the text into the input box. Not having these two lines can make the code break or result in unexpected behaviors.

else if (!field.checked) { ... }
Enter fullscreen mode Exit fullscreen mode

If the fields list item does not contain the value property, then we'll assume it's either a checkbox or radio element that needs to be auto-clicked. The !field.checked condition means we only click it if it's not already checked/ticked; if we leave out this condition, then Autofill would repeatedly click on this element every half second — not good.

field.click();
Enter fullscreen mode Exit fullscreen mode

Click on the checkbox or radio element.

That's pretty much it. I hope it was straight-forward enough. If you wanted even more automation, then there's nothing stopping you from adding a few lines of code to make it also automatically click the Continue button for you. I left that out because there were some sensitive fields that should not be automated by Autofill, such as the Social Security number field.

One thing that may not be obvious is how we came up with the CSS selectors for the form fields. To do this, right-click on a field and select "Inspect"; then look for the <input> tag closest to the highlighted code. Below I have inspected the "Family or Friend" radio element from our example:

Image description

For this part you'll have to look at all of the form elements you want to automate and see which attributes are common among them. In our case, the data-test attribute was one of the attributes found in all of the fields.

As you proceed through the Fundrise form, it should be autofilled for you even though each page was rendered dynamically via AJAX. Autofill + JavaScript rules can be used like this to open up a world of automation that can significantly reduce your menial workload. Happy autofilling! ⚡

Top comments (0)