How to Create a URL Encoder/Decoder Tool with HTML, CSS & JavaScript
How to Create a URL Encoder/Decoder Tool with HTML, CSS & JavaScript
URL encoding and decoding are essential skills for web developers. In this tutorial, we'll build a responsive, user-friendly URL encoder/decoder tool using HTML, CSS (with Tailwind), and vanilla JavaScript.
Step 1: Set Up the HTML Structure
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>URL Encoder/Decoder</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
</head>
<body class="bg-gray-100 min-h-screen flex items-center justify-center p-4">
<div class="max-w-3xl w-full bg-white rounded-xl shadow-lg overflow-hidden">
<!-- Tool content will go here -->
</div>
</body>
</html>
Step 2: Create the Header Section
Add this inside the main div
:
<div class="p-6 bg-gradient-to-r from-blue-600 to-blue-800 text-white">
<h1 class="text-2xl font-bold">URL Encoder/Decoder</h1>
<p class="opacity-90">Encode or decode URLs and strings for web applications</p>
</div>
Step 3: Build the Input/Output Text Areas
<div class="p-6 space-y-6">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label for="input-text" class="block text-sm font-medium text-gray-700 mb-1">Input Text</label>
<textarea
id="input-text"
rows="5"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none transition font-mono"
placeholder="Enter text to encode or decode"
></textarea>
</div>
<div>
<label for="output-text" class="block text-sm font-medium text-gray-700 mb-1">Result</label>
<textarea
id="output-text"
rows="5"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none transition font-mono bg-gray-50"
placeholder="Encoded or decoded result will appear here"
readonly
></textarea>
</div>
</div>
Step 4: Add Function Buttons
<div class="grid grid-cols-2 md:grid-cols-4 gap-3">
<button
id="encode-btn"
class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition flex items-center justify-center"
>
<i class="fas fa-lock mr-2"></i> Encode URL
</button>
<button
id="decode-btn"
class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition flex items-center justify-center"
>
<i class="fas fa-lock-open mr-2"></i> Decode URL
</button>
<button
id="encode-component-btn"
class="px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2 transition flex items-center justify-center"
>
<i class="fas fa-code mr-2"></i> Encode Component
</button>
<button
id="decode-component-btn"
class="px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2 transition flex items-center justify-center"
>
<i class="fas fa-file-code mr-2"></i> Decode Component
</button>
</div>
Step 5: Add Utility Buttons
<div class="flex flex-wrap gap-3 justify-between">
<button
id="copy-btn"
class="px-4 py-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 transition flex items-center"
disabled
>
<i class="fas fa-copy mr-2"></i> Copy Result
</button>
<button
id="clear-btn"
class="px-4 py-2 bg-red-100 text-red-700 rounded-lg hover:bg-red-200 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 transition flex items-center"
>
<i class="fas fa-trash-alt mr-2"></i> Clear All
</button>
<button
id="test-url-btn"
class="px-4 py-2 bg-purple-100 text-purple-700 rounded-lg hover:bg-purple-200 focus:outline-none focus:ring-2 focus:ring-purple-500 focus:ring-offset-2 transition flex items-center"
disabled
>
<i class="fas fa-external-link-alt mr-2"></i> Test URL
</button>
</div>
Step 6: Add Information Section
<div class="bg-gray-50 px-6 py-4 border-t border-gray-200">
<h3 class="font-medium text-gray-700 mb-2">About URL Encoding</h3>
<div class="text-sm text-gray-600 space-y-2">
<p><strong>URL Encoding</strong> converts characters into a format that can be transmitted over the Internet. Only alphanumerics and these special characters are allowed: <code class="bg-gray-200 px-1 rounded">- _ . ~</code></p>
<p><strong>encodeURI()</strong> - Encodes a complete URL (doesn't encode: <code class="bg-gray-200 px-1 rounded">;,/?:@&=+$#</code>)</p>
<p><strong>encodeURIComponent()</strong> - Encodes a URI component (encodes more characters)</p>
</div>
</div>
Step 7: Add the JavaScript Functionality
<script>
document.addEventListener('DOMContentLoaded', function() {
// Get all DOM elements
const inputText = document.getElementById('input-text');
const outputText = document.getElementById('output-text');
const encodeBtn = document.getElementById('encode-btn');
const decodeBtn = document.getElementById('decode-btn');
const encodeComponentBtn = document.getElementById('encode-component-btn');
const decodeComponentBtn = document.getElementById('decode-component-btn');
const copyBtn = document.getElementById('copy-btn');
const clearBtn = document.getElementById('clear-btn');
const testUrlBtn = document.getElementById('test-url-btn');
const notification = document.getElementById('notification');
// Button event listeners
encodeBtn.addEventListener('click', () => {
try {
outputText.value = encodeURI(inputText.value);
updateButtonStates();
showNotification('URL encoded successfully!');
} catch (e) {
showNotification('Error encoding URL: ' + e.message, false);
}
});
decodeBtn.addEventListener('click', () => {
try {
outputText.value = decodeURI(inputText.value);
updateButtonStates();
showNotification('URL decoded successfully!');
} catch (e) {
showNotification('Error decoding URL: ' + e.message, false);
}
});
encodeComponentBtn.addEventListener('click', () => {
try {
outputText.value = encodeURIComponent(inputText.value);
updateButtonStates();
showNotification('URI component encoded successfully!');
} catch (e) {
showNotification('Error encoding component: ' + e.message, false);
}
});
decodeComponentBtn.addEventListener('click', () => {
try {
outputText.value = decodeURIComponent(inputText.value);
updateButtonStates();
showNotification('URI component decoded successfully!');
} catch (e) {
showNotification('Error decoding component: ' + e.message, false);
}
});
copyBtn.addEventListener('click', () => {
if (outputText.value) {
navigator.clipboard.writeText(outputText.value).then(() => {
showNotification('Copied to clipboard!');
}).catch(err => {
showNotification('Failed to copy: ' + err, false);
});
}
});
clearBtn.addEventListener('click', () => {
inputText.value = '';
outputText.value = '';
updateButtonStates();
showNotification('Cleared all fields!');
});
testUrlBtn.addEventListener('click', () => {
if (outputText.value) {
try {
if (/^https?:\/\//i.test(outputText.value) || /^[a-z]+:\/\//i.test(outputText.value)) {
window.open(outputText.value, '_blank');
} else {
showNotification('This doesn\'t appear to be a complete URL', false);
}
} catch (e) {
showNotification('Error testing URL: ' + e.message, false);
}
}
});
// Input event to enable/disable buttons
inputText.addEventListener('input', updateButtonStates);
outputText.addEventListener('input', updateButtonStates);
function updateButtonStates() {
const hasInput = inputText.value.trim() !== '';
const hasOutput = outputText.value.trim() !== '';
encodeBtn.disabled = !hasInput;
decodeBtn.disabled = !hasInput;
encodeComponentBtn.disabled = !hasInput;
decodeComponentBtn.disabled = !hasInput;
copyBtn.disabled = !hasOutput;
testUrlBtn.disabled = !hasOutput || !(/^[a-z]+:/i.test(outputText.value));
}
function showNotification(message, isSuccess = true) {
notification.textContent = message;
notification.className = 'fixed top-4 right-4 p-4 rounded-lg shadow-lg transition-opacity duration-300';
if (isSuccess) {
notification.classList.add('bg-green-100', 'border-green-400', 'text-green-700');
} else {
notification.classList.add('bg-red-100', 'border-red-400', 'text-red-700');
}
notification.classList.remove('hidden');
setTimeout(() => {
notification.classList.add('hidden');
}, 3000);
}
});
</script>
Step 8: Add Notification Element
<div id="notification" class="hidden fixed top-4 right-4 p-4 bg-green-100 border border-green-400 text-green-700 rounded-lg shadow-lg transition-opacity duration-300"></div>