Build a Smart Autofill Chrome Extension with JavaScript

Filling out forms again and again can be a pain—especially during testing. Whether you're QA-ing a web app or filling mock data into online forms, an autofill extension can save you hours.

In this tutorial, we'll walk through creating a simple yet powerful Chrome Extension that autofills online forms with one click using JavaScript. We'll even throw in some randomized data generation to make it dynamic and realistic.

🔧 What We’re Building

We’re going to build a Chrome extension that:

  • Injects a script into the current tab.
  • Autofills form fields intelligently.
  • Randomizes certain fields like email, phone, DOB, etc.
  • Includes a styled popup using Bootstrap.

📁 Project Structure

Here’s what our file structure looks like:

quick-autofill/
├── manifest.json
├── popup.html
└── popup.js

📄 Step 1: Create manifest.json

This tells Chrome what our extension is and how it should behave.

{
  "manifest_version": 3,
  "name": "Quick Autofill",
  "version": "1.0",
  "description": "Autofill forms with one click",
  "permissions": ["activeTab", "scripting"],
  "action": {
    "default_popup": "popup.html",
    "default_title": "Click to Autofill"
  },
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["popup.js"]
    }
  ]
}

🧩 Step 2: Design the Popup (popup.html)

Here’s a clean, Bootstrap-styled popup UI with a single button:

<!DOCTYPE html>
<html>
<head>
  <title>Quick Autofill</title>
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet">
  <style>
    body { padding: 15px; width: 300px; }
    .btn { margin-top: 10px; }
  </style>
</head>
<body>
  <h5>Dynamic Autofill</h5>
  <button id="fillBtn" class="btn btn-success w-100">Autofill Form</button>
  <script src="popup.js"></script>
</body>
</html>

⚙️ Step 3: Handle the Logic (popup.js)

Here’s where the magic happens. The popup.js file:

  • Injects a script into the active tab.
  • Fills fields like name, email, phone, DOB, etc.
  • Handles different input types: text, checkbox, radio, select, textarea.
  • Generates randomized values for a more realistic fill.

We won’t paste the entire file here again, but you can use the full JS code provided earlier in this post and copy it as your popup.js.

It includes smart helpers like:

// Trigger the autofill function when a button is clicked (assuming you have a button with id "fillBtn")
const fillBtn = document.getElementById('fillBtn');
if (fillBtn) {
  fillBtn.addEventListener('click', async () => {
    const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
    chrome.scripting.executeScript({
      target: { tabId: tab.id },
      func: autofillForm,
    });
  });
}

// Helper function to simulate typing into a text field with validation
function simulateTyping(el, value) {
  if (!el) return;

  // Check the input type and adjust the value accordingly
  if (el.type === 'date') {
    // Ensure the value is in a valid date format (e.g., YYYY-MM-DD)
    if (!isValidDate(value)) {
      console.error('Invalid date format.');
      return;
    }
  }

  if (el.type === 'number') {
    // Ensure the value is numeric and within the range
    if (isNaN(value)) {
      console.error('Invalid number.');
      return;
    }
  }

  if (el.type === 'range') {
    // Ensure the value is within the range
    const min = el.min ? parseFloat(el.min) : 0;
    const max = el.max ? parseFloat(el.max) : 100;
    const numValue = parseFloat(value);
    if (numValue < min || numValue > max) {
      console.error('Value out of range.');
      return;
    }
  }

  if (el.type === 'checkbox') {
    el.focus();
    el.checked = Boolean(value); // Set to true or false based on passed value
    el.dispatchEvent(new Event('change', { bubbles: true }));
    el.blur();
    return;
  }
  if (el.tagName.toLowerCase() === 'textarea') {
    el.focus();
    el.value = value;
    el.dispatchEvent(new Event('input', { bubbles: true }));
    el.dispatchEvent(new Event('change', { bubbles: true }));
    el.blur();
    return;
  }

  if (el.type === 'radio') {
    // Find all radios with the same name
    const name = el.name;
    const radios = document.querySelectorAll(`input[type="radio"][name="${name}"]`);
  
    let matched = false;
  
    radios.forEach(radio => {
      if (radio.value === value) {
        radio.checked = true;
        radio.dispatchEvent(new Event('change', { bubbles: true }));
        matched = true;
      } else {
        radio.checked = false;
      }
    });
  
    if (!matched) {
      console.error(`No radio button found with value "${value}" for name "${name}"`);
    }
  
    return;
  }
  
  // If no validation errors, simulate typing
  el.focus();
  el.value = value;
  el.dispatchEvent(new Event('input', { bubbles: true }));
  el.dispatchEvent(new Event('change', { bubbles: true }));
  el.blur();
}

// Helper function to check if a date is valid (in YYYY-MM-DD format)
function isValidDate(date) {
  const regex = /^\d{4}-\d{2}-\d{2}$/;
  return regex.test(date);
}

// Helper function to fill a field if it exists (check if field is empty or invalid)
function fillFieldIfExists(selector, value) {
  const el = document.querySelector(selector);
  if (el && !el.value) {  // Only fill if the field is empty
    console.log(`Filling field: ${selector} with value: ${value}`);
    simulateTyping(el, value);
  } else {
    console.log(`Field not found or already filled: ${selector}`);
  }
}

// Helper functions to generate random data
function randomEmail() {
  const chars = 'abcdefghijklmnopqrstuvwxyz';
  const digits = '0123456789';
  let prefix = '';
  for (let i = 0; i < 3; i++) {
    prefix += chars[Math.floor(Math.random() * chars.length)];
  }
  for (let i = 0; i < 3; i++) {
    prefix += digits[Math.floor(Math.random() * digits.length)];
  }
  return prefix + '@xyz.com';
}

function randomPhone() {
  const areaCode = '209';
  const rest = Math.floor(1000000 + Math.random() * 9000000).toString();
  return `(${areaCode}) ${rest.slice(0, 3)}-${rest.slice(3)}`;
}

function randomDOB() {
  const start = new Date(1970, 0, 1).getTime();
  const end = new Date(2000, 11, 31).getTime();
  const dob = new Date(start + Math.random() * (end - start));
  const mm = String(dob.getMonth() + 1).padStart(2, '0');
  const dd = String(dob.getDate()).padStart(2, '0');
  const yyyy = dob.getFullYear();
  return `${mm}/${dd}/${yyyy}`;
}

function randomLastName() {
  const chars = 'abcdefghijklmnopqrstuvwxyz';
  let suffix = '';
  for (let i = 0; i < 3; i++) {
    suffix += chars[Math.floor(Math.random() * chars.length)];
  }
  return 'Test' + suffix.charAt(0).toUpperCase() + suffix.slice(1);
}

function randomString() {
  const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
  let result = '';
  for (let i = 0; i < 8; i++) {
    result += chars[Math.floor(Math.random() * chars.length)];
  }
  return result;
}

// Function to autofill the form with random values
function autofillForm() {
   // ✅ Fill known fields directly (keep these static)
   fillFieldIfExists('input[name="height_ft"]', "5");
   fillFieldIfExists('input[name="height_in"]', "10");
   fillFieldIfExists('input[name="weight"]', "260");
   fillFieldIfExists('input[name="age"]', "30");
 
   const email = randomEmail();
   const phone = randomPhone();
   const dob = randomDOB();
   const lastName = randomLastName();
 
   // ✅ Static fields: Do NOT randomize (address remains static)
   const values = {
     'first_name': 'John',
     'last_name': lastName,
     'email': email,
     'birthday': dob,
     'phone': phone,
     'address1': '1500 E Main St',  // Static Address
     'address2': 'Apt 3',          // Static Address
     'city': 'Marion',             // Static City
     'state': 'KS',                // Static State
     'zip_code': '66861',          // Static Zip Code
     'password': 'test123',
     'card_number': 4111222233334444,
     'card_cvv':1234,
   };
  // ✅ Select fields: select a random non-default value
  const selects = document.querySelectorAll('select');
  selects.forEach(select => {
    if (select.options.length > 1) {  // Ensure there are options to choose from
      const randomIndex = Math.floor(Math.random() * (select.options.length - 1)) + 1;
      select.value = select.options[randomIndex].value;
      select.dispatchEvent(new Event('change', { bubbles: true }));
    }
  });

  // ✅ Randomize other fields per question block
  const blocks = document.querySelectorAll('.question, .question-block, .form-group, .question-wrapper, .question-container, .medical_problems_thyroid_div');
  blocks.forEach(block => {
    const inputs = Array.from(block.querySelectorAll('input[type="text"], input[type="number"]'));
    const checkboxes = Array.from(block.querySelectorAll('input[type="checkbox"]'));
    const radios = Array.from(block.querySelectorAll('input[type="radio"]'));
    const selects = Array.from(block.querySelectorAll('select'));
    const textareas = Array.from(block.querySelectorAll('textarea')); // ✅ new line
  
    const options = [];
  
    // Skip these fields from random input
    const skipFields = [
      'first_name', 'last_name', 'email', 'birthday', 'phone',
      'address1', 'address2', 'city', 'state', 'zip_code',
      'password', 'height_ft', 'height_in', 'weight', 'age','card_number'
    ];
  
    // ✅ Handle <input> fields
    if (inputs.length) options.push(() => {
      const rand = inputs[Math.floor(Math.random() * inputs.length)];
      if (skipFields.includes(rand.name)) {
        console.log(`Skipping field: ${rand.name}`);
        return;
      }
      simulateTyping(rand, randomString());
    });
  
    // ✅ Handle <textarea> fields
    if (textareas.length) options.push(() => {
      const rand = textareas[Math.floor(Math.random() * textareas.length)];
      if (skipFields.includes(rand.name)) {
        console.log(`Skipping textarea: ${rand.name}`);
        return;
      }
      simulateTyping(rand, randomString());
    });
  
    // ✅ Handle checkboxes
    if (checkboxes.length) options.push(() => {
      const rand = checkboxes[Math.floor(Math.random() * checkboxes.length)];
      if (skipFields.includes(rand.name)) {
        console.log(`Skipping checkbox: ${rand.name}`);
        return;
      }
      rand.checked = true;
      rand.dispatchEvent(new Event('change', { bubbles: true }));
    });
  
    // ✅ Handle radio buttons
    if (radios.length) options.push(() => {
      const rand = radios[Math.floor(Math.random() * radios.length)];
      if (skipFields.includes(rand.name)) {
        console.log(`Skipping radios: ${rand.name}`);
        return;
      }
      rand.checked = true;
      rand.dispatchEvent(new Event('change', { bubbles: true }));
    });
  
    // ✅ Handle selects
    if (selects.length) options.push(() => {
      selects.forEach(select => {
        if (select.options.length > 1) {
          const idx = Math.floor(Math.random() * (select.options.length - 1)) + 1;
          select.value = select.options[idx].value;
          select.dispatchEvent(new Event('change', { bubbles: true }));
        }
      });
    });
  
    // ✅ Perform a random action from the available options
    if (options.length > 0) {
      const randomAction = options[Math.floor(Math.random() * options.length)];
      randomAction();
    }
  });
 
   for (const [name, value] of Object.entries(values)) {
     const el = document.getElementById(name) || document.querySelector(`[name="${name}"]`);
     if (el && !el.value) {  // Only fill if the field is empty
       console.log(`Filling field: ${name} with value: ${value}`);
       simulateTyping(el, value);
     } else {
       console.log(`Field not found or already filled: ${name}`);
     }
   }

  // ✅ Agree terms if available
  const agree = document.getElementById('agree_terms');
  if (agree) {
    agree.checked = true;
    agree.dispatchEvent(new Event('change', { bubbles: true }));
  }

  // ✅ Set biological sex
  const sexEl = document.getElementById('biological_sex');
  if (sexEl) {
    sexEl.value = 'male';
    sexEl.dispatchEvent(new Event('change', { bubbles: true }));
  }
}

You’ll also find support for edge cases, like:

  • Validating date formats
  • Respecting field types (checkboxes, radios, etc.)
  • Avoiding already-filled fields
  • Clicking agreement checkboxes or setting select values

✅ Bonus Features

  • Randomization: Each run can generate different emails, phone numbers, and text values.
  • Skip Logic: Fields like first name, address, or card number are static for consistency.
  • Modular Design: Add more fillFieldIfExists(...) lines or update selectors as needed.

🔄 Install the Extension

  1. Visit chrome://extensions/
  2. Enable Developer Mode (top right)
  3. Click "Load Unpacked"
  4. Select your quick-autofill/ folder
  5. Click the extension icon and then "Autofill Form" — boom!

🧠 Tips & Customizations

  • Want to test on a specific form? Add a "matches" URL pattern in manifest.json.
  • Add a delay or animation to simulate human-like typing.
  • Integrate fake data libraries (like Faker.js) for richer content.

🎯 Final Thoughts

This Chrome Extension is perfect for:

  • Developers testing forms
  • QA teams filling dummy data
  • Power users tired of repetitive typing

You can extend it further—save profiles, add multiple templates, even inject different scripts based on domain. But for now, enjoy your 1-click autofill!

Have questions or improvements? Drop a comment below or fork the repo. Happy automating! 💻⚡

Let me know if you'd like this turned into a downloadable .zip, published on a blog platform (like Dev.to or Medium), or converted into a step-by-step video tutorial!

0 Comments