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();
Subscribe
Notify of
guest
0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments

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.