
Building an Image CAPTCHA Checker with JavaScript
This article will guide you through building a dynamic CAPTCHA with JavaScript, HTML, and CSS. We’ll also explain how to handle user feedback and refresh images upon incorrect attempts.
Features of the CAPTCHA
- Displays a grid of 9 images, where 4 belong to a specified category (e.g., planes).
- Users must select all 4 correct images to pass the CAPTCHA.
- If incorrect, a new set of images is loaded automatically.
- Includes visual and auditory feedback to enhance accessibility.
- Results are displayed dynamically within the CAPTCHA interface.
Step 1: Setting Up the HTML Structure
The HTML defines the basic structure of the CAPTCHA interface. Here’s the code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Image CAPTCHA</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="captcha-card">
<div class="captcha-header">
<h2>Select all images containing a <span id="category-name">plane</span></h2>
<img id="category-image" src="./images/plane1.jpg" alt="Category example">
</div>
<div id="captcha-container" class="captcha-grid"></div>
<div class="captcha-footer">
<div>
<span class="icon" id="refresh-btn">🔄</span>
<span class="icon" id="audio-btn">🔊</span>
</div>
<button id="verify-btn">Verify</button>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
Step 2: Styling with CSS
The CSS styles the timer, input fields, and buttons, adding visual appeal and an animation for the timer display.
body {
font-family: Arial, sans-serif;
background-color: #161a20;
color: #fff;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
}
.captcha-card {
background-color: #fff;
color: #000;
width: 400px;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
}
.captcha-header {
display: flex;
justify-content: space-between;
align-items: center;
background-color: #007c7e;
padding: 10px 20px;
}
.captcha-header h2 {
margin: 0;
font-size: 16px;
font-weight: normal;
color: #fff;
}
.captcha-header img {
width: 50px;
height: 50px;
object-fit: cover;
border-radius: 5px;
}
#category-name {
font-weight: bold;
}
.captcha-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
padding: 20px;
background-color: #fff;
}
.captcha-image {
width: 100%;
height: 100px;
object-fit: cover;
border: 2px solid transparent;
cursor: pointer;
transition: 0.3s;
}
.captcha-image.selected {
border-color: #00ff00;
}
.captcha-footer {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 20px;
background-color: #f8f8f8;
}
.captcha-footer button {
padding: 10px 15px;
font-size: 14px;
background-color: #007c7e;
border: none;
cursor: pointer;
color: #fff;
border-radius: 5px;
}
.captcha-footer button:hover {
background-color: #00cc00;
}
.captcha-footer .icon {
font-size: 18px;
cursor: pointer;
margin-right: 10px;
}
.captcha-footer .icon:hover {
color: #00cc00;
}
Explanation
- Card Layout: The entire CAPTCHA is wrapped in a
.captcha-card
for styling. - Header: Displays the instruction text and an example image for the target category.
- Grid Section: Contains a dynamic 3x3 grid of images.
- Footer: Includes buttons for refreshing the images, playing audio instructions, and verifying the selection. A dynamic message is displayed between the voice icon and the Verify button.
Step 3: Adding Functionality with JavaScript
The JavaScript dynamically generates the CAPTCHA, handles user selections, and provides feedback.
const captchaContainer = document.getElementById('captcha-container');
const categoryImage = document.getElementById('category-image');
const messageElement = document.createElement('span'); // For the result message
const verifyButton = document.getElementById('verify-btn');
const refreshButton = document.getElementById('refresh-btn');
const audioButton = document.getElementById('audio-btn');
const categoryNameElement = document.getElementById('category-name');
const categories = ['bus', 'car', 'bike', 'road', 'plane', 'boat', 'cat', 'dog'];
let targetCategory = '';
let correctImageIndexes = [];
// Example image URL set (replace these with your own images)
const imagePool = {
bus: ['bus1.jpg', 'bus2.png', 'bus3.png', 'bus4.png'],
car: ['car1.png', 'car2.png', 'car3.png', 'car4.png'],
bike: ['bike1.png', 'bike2.png', 'bike3.png', 'bike4.png'],
road: ['road1.png', 'road2.png', 'road3.png', 'road4.png'],
plane: ['plane1.png', 'plane2.png', 'plane3.png', 'plane4.png'],
boat: ['boat1.png', 'boat2.png', 'boat3.png', 'boat4.png'],
cat: ['cat1.jpg', 'cat2.png', 'cat3.png', 'cat4.png'],
dog: ['dog1.jpg', 'dog2.jpg', 'dog3.jpg', 'dog4.jpg']
};
// Function to shuffle an array
function shuffle(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array;
}
// Function to load a new CAPTCHA challenge
function loadCaptcha() {
captchaContainer.innerHTML = '';
messageElement.textContent = ''; // Clear previous messages
// Randomly pick a target category
const allCategories = Object.keys(imagePool);
targetCategory = allCategories[Math.floor(Math.random() * allCategories.length)];
categoryNameElement.textContent = targetCategory;
// Update example image
categoryImage.src = `./images/${imagePool[targetCategory][0]}`;
// Get 4 images from the target category
const targetImages = shuffle([...imagePool[targetCategory]]).slice(0, 4);
// Get 5 random images from other categories
const otherCategories = allCategories.filter(cat => cat !== targetCategory);
let otherImages = [];
while (otherImages.length < 5) {
const randomCategory = otherCategories[Math.floor(Math.random() * otherCategories.length)];
const randomImage = shuffle([...imagePool[randomCategory]])[0];
if (!otherImages.includes(randomImage)) {
otherImages.push(randomImage);
}
}
// Merge and shuffle the 9 images
const allImages = shuffle([...targetImages, ...otherImages]);
// Render the images in the captcha container
allImages.forEach((imgSrc, index) => {
const imgElement = document.createElement('img');
imgElement.src = `./images/${imgSrc}`;
imgElement.classList.add('captcha-image');
imgElement.dataset.correct = targetImages.includes(imgSrc) ? 'true' : 'false';
imgElement.addEventListener('click', () => toggleImageSelection(imgElement));
captchaContainer.appendChild(imgElement);
});
// Store correct image indexes
correctImageIndexes = allImages
.map((img, index) => ({ img, index }))
.filter(item => targetImages.includes(item.img))
.map(item => item.index);
}
// Function to toggle selection
function toggleImageSelection(imgElement) {
imgElement.classList.toggle('selected');
}
// Function to verify the CAPTCHA
function verifyCaptcha() {
const selectedImages = Array.from(document.querySelectorAll('.captcha-image.selected'));
const selectedIndexes = selectedImages.map(img => Array.from(captchaContainer.children).indexOf(img));
const isCorrect = selectedIndexes.length === 4 && correctImageIndexes.every(index => selectedIndexes.includes(index));
if (isCorrect) {
messageElement.textContent = '✅ CAPTCHA Passed!';
messageElement.style.color = 'green';
} else {
messageElement.textContent = '❌ Incorrect! Refreshing...';
messageElement.style.color = 'red';
setTimeout(loadCaptcha, 1000); // Refresh the CAPTCHA after a brief delay
}
}
// Function to play audio
function playAudio() {
const utterance = new SpeechSynthesisUtterance(`Select all images containing a ${targetCategory}`);
speechSynthesis.speak(utterance);
}
// Add the message element to the footer dynamically
const captchaFooter = document.querySelector('.captcha-footer');
captchaFooter.insertBefore(messageElement, verifyButton);
// Event listeners
verifyButton.addEventListener('click', verifyCaptcha);
refreshButton.addEventListener('click', loadCaptcha);
audioButton.addEventListener('click', playAudio);
// Load the initial CAPTCHA
loadCaptcha();
How It Works
- Dynamic Grid Creation:
- The
loadCaptcha
function generates 9 images dynamically—4 from the target category and 5 from other categories. - Images are shuffled before being displayed.
- User Interaction:
- Users click images to select them. Selected images are highlighted with a green border.
-
Verification: The
verifyCaptcha
function checks if exactly 4 correct images are selected. If correct, it shows a success message. Otherwise, it refreshes the CAPTCHA. -
Accessibility:
- The
playAudio
function uses theSpeechSynthesis
API to read the CAPTCHA instruction aloud.
Final Thoughts
This interactive CAPTCHA provides a user-friendly, accessible, and secure method for verifying human users. You can customize it further by using different categories, images, or layouts. Let us know if you have any questions or improvements!