URL Shortener (Frontend) with HTML, CSS & JavaScript

40 DAYS 40 PROJECT CHALLENGE

Day #09

Project Overview

The URL Shortener is a frontend web application built using HTML, CSS, and JavaScript. It allows users to enter long URLs and generate shorter links with randomly created codes. Users can enter a URL, validate it, and generate a short link instantly. Each generated link displays the original URL, shortened code, and click count. Additionally, users can copy shortened links to their clipboard, delete saved links etc. JavaScript handles URL validation, random code generation, dynamic rendering, click tracking, and user interactions. This project is useful for learning DOM manipulation, browser storage, the Clipboard API, event delegation, and frontend application logic. 

Key Features

  • Long URL Input: Users can enter a long URL through a dedicated input field. The interface provides a clear placeholder showing that URLs should include a protocol such as https://.
  • URL Validation: The application validates the entered URL using JavaScript’s URL constructor. If the URL is invalid, an error message asks the user to enter a valid address.
  • Random Short Code Generation: The generateCode() function creates a random six-character code using uppercase letters, lowercase letters, and numbers. This code is used to represent the shortened link.
  • Instant URL Shortening: After successful validation, JavaScript creates a new link object containing the original URL, generated code, and an initial click count of zero.
  • Shortened Links Display: Generated links appear as individual cards inside the links container. Each card displays the original URL, a short URL format, click count, and available actions.
  • Copy to Clipboard: Users can copy the generated short URL by clicking the Copy button. The application uses navigator.clipboard.writeText() and displays a confirmation alert.
  • Delete Saved Links: Each link card includes a Delete button. When clicked, JavaScript removes the selected link from the array and updates LocalStorage.
  • LocalStorage Integration: The application stores shortened links in LocalStorage under the shortLinks key. This allows previously generated links to be loaded when the page opens again.
  • Dynamic Link Rendering: The renderLinks() function creates link cards dynamically using JavaScript. It updates the displayed links whenever a new link is added, deleted, or clicked.
  • Event Delegation: Instead of attaching separate listeners to every button, the application uses one click listener on the links container. It identifies Copy, Delete, and short URL actions using CSS classes.
  • Responsive Interface: The layout uses Flexbox, spacing, and card-based styling to create a clean interface. The link cards organize URL information and action buttons in a structured format.

What You'll Learn

  • How to validate URLs using JavaScript’s URL constructor.
  • How to generate random strings using loops and Math.random().
  • How to manage link data using JavaScript arrays and objects.
  • How to store data using LocalStorage.
  • How to convert objects into JSON using JSON.stringify().
  • How to retrieve JSON data using JSON.parse().
  • How to dynamically create elements using document.createElement().
  • How to implement clipboard functionality using the Clipboard API.
  • How to use event delegation for dynamic buttons.

HTML Code

The HTML structure creates the URL Shortener interface inside the .container element. It includes a heading, an input field for entering the original URL, and a Shorten button. The input field uses the longUrl ID, while the button uses the shortenBtn ID. JavaScript selects these elements to validate the URL and generate a shortened link.

The error element displays validation messages when users enter an invalid URL. Below this section, the linksContainer element displays all generated shortened links dynamically. Finally, the HTML file connects the external CSS and JavaScript files. The JavaScript code manages the link generation, rendering, copying, deleting, and click tracking.

<!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

The CSS code creates a clean URL Shortener interface with a dark blue page background and a centered white container. The universal selector resets the margin and padding and applies box-sizing: border-box for consistent element sizing.

The .container class defines the main card width, background color, padding, rounded corners, and box shadow,  .input-group class uses Flexbox to place the URL input and Shorten button next to each other with a gap, input selector styles the URL field with padding, a border, and rounded corners. The button selector creates blue buttons with white text, bold font weight, and a hover effect that changes the background color.

The .link-card class styles each generated link using a light background, padding, rounded corners, and Flexbox, .link-info class allows URL information to occupy the available space, while .actions arranges the Copy and Delete button, .short-url class highlights the shortened URL using blue text and a bold font. The .small-btn class reduces the size of action buttons so they fit neatly inside each link card.

*{
  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

The JavaScript code manages URL validation, random code generation, link storage, dynamic rendering, clipboard copying, deletion, and click tracking. First, the application selects the URL input, Shorten button, links container, and error message element using document.getElementById().

generateCode() function creates a random code with a default length of six characters. It selects random characters from a string containing letters and numbers, isValidURL() function checks whether the entered value is a valid URL. It uses the URL constructor inside a try...catch block and returns true for valid URLs or false when an error occurs.

This saveLinks() function converts the links array into JSON and saves it in LocalStorage. Meanwhile, renderLinks() clears the links container and loops through the array using forEach(). It creates a link card containing the original URL, generated short code, click count, Copy button, and Delete button. When users click the Shorten button, the application validates the URL and creates a new object containing original, code, and clicks properties. The new object is added to the beginning of the array using unshift(). 

Links container uses event delegation to handle Copy, Delete, and short URL clicks. The Copy button uses navigator.clipboard.writeText() to copy the generated URL. The Delete button removes the selected link using splice(). Finally, the Enter key event listener triggers the Shorten button, while renderLinks() runs when the script loads to display previously saved links.

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.