URL Shortener (Frontend) with HTML, CSS & JavaScript
40 DAYS 40 PROJECT CHALLENGE
Day #09
Project Overview
A simple URL Shortener (Frontend) built using HTML, CSS, and JavaScript with a clean and responsive interface. The application allows users to enter a long URL and generate a shortened link for easier sharing. This project demonstrates frontend concepts such as form input handling, dynamic link generation, DOM manipulation, and responsive UI design, making it a useful beginner project for understanding how URL shortening tools work.
Key Features
- Input field to enter long URLs
- Generate shortened links instantly
- Copy shortened URL to clipboard functionality
- Clean and responsive interface
- User-friendly link management layout
- Interactive elements powered by JavaScript
- Instant feedback when generating links
- Mobile-friendly design for all screen sizes
HTML Code
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>URL Shortener</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<h1>🔗 URL Shortener</h1>
<div class="input-group">
<input type="text" id="longUrl" placeholder="Enter long URL (https://example.com)">
<button id="shortenBtn">Shorten</button>
</div>
<div id="error"></div>
<h2>Shortened Links</h2>
<div id="linksContainer"></div>
</div>
<script src="script.js"></script>
</body>
</html> CSS Code
*{
margin:0;
padding:0;
box-sizing:border-box;
font-family:Arial, sans-serif;
}
body{
background:#042354;
display:flex;
justify-content:center;
padding:50px 20px;
}
.container{
width:650px;
background:white;
padding:30px;
border-radius:12px;
box-shadow:0 10px 30px rgba(0,0,0,0.1);
}
h1{
margin-bottom:20px;
}
.input-group{
display:flex;
gap:10px;
margin-bottom:10px;
}
input{
flex:1;
padding:10px;
border:1px solid #ddd;
border-radius:6px;
}
button{
padding:10px 15px;
border:none;
background:#2563eb;
color:white;
border-radius:6px;
cursor:pointer;
font-weight:bold;
}
button:hover{
background:#1d4ed8;
}
#error{
color:red;
margin-bottom:10px;
font-size:14px;
}
.link-card{
background:#f9fafb;
padding:15px;
border-radius:8px;
margin-bottom:10px;
display:flex;
justify-content:space-between;
align-items:center;
}
.link-info{
flex:1;
}
.short-url{
color:#2563eb;
font-weight:bold;
cursor:pointer;
margin-top:5px;
display:inline-block;
}
.actions{
display:flex;
gap:8px;
}
.small-btn{
padding:5px 8px;
font-size:12px;
border-radius:4px;
}-box;
font-family:'Segoe UI',Arial;
}
body{
display:flex;
gap:30px;
padding:30px;
background:#042354;
justify-content:center;
}
/* Form */
.container{
width:35%;
background:white;
padding:25px;
border-radius:16px;
box-shadow:0 10px 30px rgba(0,0,0,0.08);
height:95vh;
overflow:auto;
}
input, textarea, select{
width:100%;
padding:10px;
margin-bottom:12px;
border:1px solid #e5e7eb;
border-radius:8px;
font-size:14px;
}
textarea{
resize:none;
height:80px;
}
button{
width:100%;
padding:10px;
border:none;
background:#2563eb;
color:white;
border-radius:8px;
cursor:pointer;
font-weight:600;
margin-bottom:10px;
}
button:hover{
background:#1d4ed8;
}
/* Resume */
.preview{
width:794px;
min-height:1123px;
background:white;
border-radius:10px;
overflow:hidden;
display:flex;
}
.sidebar{
width:30%;
background:#1e293b;
color:white;
padding:30px 20px;
}
.sidebar h1{
font-size:22px;
margin-bottom:5px;
}
.sidebar h3{
font-size:14px;
margin-bottom:15px;
color:#cbd5e1;
}
.sidebar p{
font-size:13px;
margin-bottom:6px;
}
.sidebar h2{
margin-top:20px;
margin-bottom:10px;
font-size:14px;
text-transform:uppercase;
border-bottom:1px solid rgba(255,255,255,0.2);
padding-bottom:5px;
}
.main{
width:70%;
padding:40px;
}
.section{
margin-bottom:25px;
cursor:move;
}
.section h2{
font-size:16px;
margin-bottom:10px;
border-bottom:2px solid #2563eb;
padding-bottom:5px;
}
.section p{
font-size:14px;
margin-bottom:8px;
line-height:1.5;
}
ul{
margin-left:18px;
}
ul li{
font-size:14px;
margin-bottom:6px;
}
/* Templates */
.template-corporate .sidebar{
background:#0f172a;
}
.template-modern .sidebar{
background:#111827;
}
.template-modern .section h2{
border-bottom:2px solid #38bdf8;
} Javascript Code
const longUrlInput = document.getElementById("longUrl");
const shortenBtn = document.getElementById("shortenBtn");
const linksContainer = document.getElementById("linksContainer");
const errorDiv = document.getElementById("error");
let links = JSON.parse(localStorage.getItem("shortLinks")) || [];
/* Generate random short code */
function generateCode(length = 6){
const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
let code = "";
for(let i=0;i<length;i++){
code += chars.charAt(Math.floor(Math.random()*chars.length));
}
return code;
}
/* Validate URL */
function isValidURL(url){
try{
new URL(url);
return true;
}catch{
return false;
}
}
/* Save links */
function saveLinks(){
localStorage.setItem("shortLinks", JSON.stringify(links));
}
/* Render links */
function renderLinks(){
linksContainer.innerHTML = "";
links.forEach((link,index)=>{
const card = document.createElement("div");
card.className = "link-card";
card.innerHTML = `
<div class="link-info">
<div>${link.original}</div>
<div class="short-url" data-index="${index}">
http://short.ly/${link.code}
</div>
<small>Clicks: ${link.clicks}</small>
</div>
<div class="actions">
<button class="small-btn copy-btn" data-index="${index}">Copy</button>
<button class="small-btn delete-btn" data-index="${index}">Delete</button>
</div>
`;
linksContainer.appendChild(card);
});
}
/* Shorten URL */
shortenBtn.addEventListener("click", ()=>{
const url = longUrlInput.value.trim();
if(!isValidURL(url)){
errorDiv.innerText = "Please enter a valid URL (include https://)";
return;
}
errorDiv.innerText = "";
const newLink = {
original:url,
code:generateCode(),
clicks:0
};
links.unshift(newLink);
saveLinks();
renderLinks();
longUrlInput.value="";
});
/* Copy / Delete / Click tracking */
linksContainer.addEventListener("click",(e)=>{
// Copy link
if(e.target.classList.contains("copy-btn")){
const index = e.target.dataset.index;
const shortUrl = `http://short.ly/${links[index].code}`;
navigator.clipboard.writeText(shortUrl);
alert("Copied!");
}
// Delete link
if(e.target.classList.contains("delete-btn")){
const index = e.target.dataset.index;
links.splice(index,1);
saveLinks();
renderLinks();
}
// Track clicks
if(e.target.classList.contains("short-url")){
const index = e.target.dataset.index;
links[index].clicks++;
saveLinks();
renderLinks();
window.open(links[index].original,"_blank");
}
});
/* Enter key support */
longUrlInput.addEventListener("keypress",function(e){
if(e.key==="Enter"){
shortenBtn.click();
}
});
renderLinks(); Related Projects
Day 6 : File Upload Progress Bar
File upload interface with a visual progress bar and smooth animation.
Concepts: File API, progress tracking, animations, event handling.
Day 7 : Survey / Poll App
Switches content dynamically using a tab-based navigation UI.
Concepts: DOM manipulation, event handling, UI state management.
Day 11 : AI Resume Skill Analyzer
Generates a user profile card dynamically from form inputs.
Concepts: Form handling, dynamic UI generation, data mapping.