
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
- Visit chrome://extensions/
- Enable Developer Mode (top right)
- Click "Load Unpacked"
- Select your quick-autofill/ folder
- 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