<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Bansidhar Kadiya - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Bansidhar Kadiya - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Tue, 27 Oct 2026 12:34:23 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/author/99tools/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Real-Time Word Counter Tool with HTML, CSS, and JavaScript ]]>
                </title>
                <description>
                    <![CDATA[ Whether you're writing an essay, a tweet, or a blog post, keeping track of your word and character count is very important. In this tutorial, you'll build a fully functional, real-time Word Counter to ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-real-time-word-counter-tool-with-html-css-and-javascript/</link>
                <guid isPermaLink="false">6abca43ca3fbe2215db75ecf</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ HTML5 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Beginner Developers ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Bansidhar Kadiya ]]>
                </dc:creator>
                <pubDate>Wed, 30 Sep 2026 05:55:08 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/d32feb51-3930-4bd7-be76-9a9150d069d1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Whether you're writing an essay, a tweet, or a blog post, keeping track of your word and character count is very important.</p>
<p>In this tutorial, you'll build a fully functional, real-time Word Counter tool from scratch. You'll use HTML for the structure, CSS for a clean design, and vanilla JavaScript to handle the counting logic.</p>
<p>Because this tool runs completely in the browser, it works instantly as you type and keeps your text data completely private.</p>
<p>Let's get started.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>To follow this guide easily, you should have:</p>
<ul>
<li><p>A basic understanding of HTML tags and CSS styling.</p>
</li>
<li><p>Familiarity with JavaScript concepts like functions, event listeners, and basic Regular Expressions (Regex).</p>
</li>
<li><p>A code editor (like VS Code) and a web browser.</p>
</li>
</ul>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a href="#heading-step-1-set-up-your-project">Step 1: Set Up Your Project</a></p>
</li>
<li><p><a href="#heading-step-2-build-the-html-structure">Step 2: Build the HTML Structure</a></p>
<ul>
<li><a href="#heading-understanding-the-html">Understanding the HTML:</a></li>
</ul>
</li>
<li><p><a href="#heading-step-3-style-the-tool-with-css">Step 3: Style the Tool with CSS</a></p>
<ul>
<li><a href="#heading-understanding-the-css">Understanding the CSS:</a></li>
</ul>
</li>
<li><p><a href="#heading-step-4-add-the-javascript-logic">Step 4: Add the JavaScript Logic</a></p>
<ul>
<li><a href="#heading-understanding-the-javascript">Understanding the JavaScript:</a></li>
</ul>
</li>
<li><p><a href="#heading-step-5-test-your-application">Step 5: Test Your Application</a></p>
</li>
</ul>
<h2 id="heading-step-1-set-up-your-project">Step 1: Set Up Your Project</h2>
<p>First, you need to set up your workspace. Create a new folder on your computer and name it <code>word-counter-app</code>.</p>
<p>Inside this folder, create three empty files:</p>
<ul>
<li><p><code>index.html</code></p>
</li>
<li><p><code>style.css</code></p>
</li>
<li><p><code>script.js</code></p>
</li>
</ul>
<h2 id="heading-step-2-build-the-html-structure">Step 2: Build the HTML Structure</h2>
<p>Open your <code>index.html</code> file. You need to create a layout that includes a header, a grid to display the real-time statistics, a text area for the user to type in, and buttons to copy or clear the text.</p>
<p>Add the following code into your HTML file:</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
    &lt;title&gt;Word Counter Tool&lt;/title&gt;
    &lt;link rel="stylesheet" href="style.css"&gt;
&lt;/head&gt;
&lt;body&gt;

    &lt;header class="header-section"&gt;
        &lt;h1&gt;Word Counter&lt;/h1&gt;
        &lt;p class="description"&gt;Paste or type your text below to get a real-time count of words, characters, sentences, and paragraphs.&lt;/p&gt;
    &lt;/header&gt;

    &lt;main class="tool-container"&gt;
        
        &lt;!-- Statistics Panel --&gt;
        &lt;div class="stats-grid"&gt;
            &lt;div class="stat-box"&gt;
                &lt;div class="stat-value" id="wordCount"&gt;0&lt;/div&gt;
                &lt;div class="stat-label"&gt;Words&lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="stat-box"&gt;
                &lt;div class="stat-value" id="charCount"&gt;0&lt;/div&gt;
                &lt;div class="stat-label"&gt;Characters&lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="stat-box"&gt;
                &lt;div class="stat-value" id="sentenceCount"&gt;0&lt;/div&gt;
                &lt;div class="stat-label"&gt;Sentences&lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="stat-box"&gt;
                &lt;div class="stat-value" id="paragraphCount"&gt;0&lt;/div&gt;
                &lt;div class="stat-label"&gt;Paragraphs&lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;!-- User Input Area --&gt;
        &lt;textarea id="textInput" placeholder="Start typing or paste your text here..."&gt;&lt;/textarea&gt;

        &lt;!-- Action Buttons --&gt;
        &lt;div class="controls"&gt;
            &lt;button class="btn-primary" onclick="copyText()" id="copyBtn"&gt;Copy Text&lt;/button&gt;
            &lt;button class="btn-secondary" onclick="clearText()"&gt;Clear&lt;/button&gt;
        &lt;/div&gt;

    &lt;/main&gt;

    &lt;script src="script.js"&gt;&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>Understanding the HTML:</p>
<ul>
<li><p><strong>The</strong> <code>.stats-grid</code><strong>:</strong> This holds four separate boxes to show the counts. Each number has a unique <code>id</code> (like <code>wordCount</code>) so JavaScript can find and update it easily.</p>
</li>
<li><p><strong>The</strong> <code>&lt;textarea&gt;</code><strong>:</strong> This is the main input box where users will type or paste their content.</p>
</li>
<li><p><strong>The</strong> <code>&lt;script&gt;</code> <strong>tag:</strong> This connects your HTML to the logic you will write in the next steps.</p>
</li>
</ul>
<h2 id="heading-step-3-style-the-tool-with-css">Step 3: Style the Tool with CSS</h2>
<p>Next, you'll give your tool a modern, professional look. You'll use CSS Grid to align the statistics boxes perfectly.</p>
<p>Open your <code>style.css</code> file and add this code:</p>
<pre><code class="language-css">:root {
    --primary-color: #007bff;
    --bg-color: #f8f9fa;
    --text-dark: #202124;
    --text-muted: #5f6368;
    --border-color: #dadce0;
    --panel-bg: #ffffff;
}

body {
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    background-color: var(--bg-color);
    color: var(--text-dark);
    margin: 0;
    padding: 40px 20px;
    display: flex;
    flex-direction: column;
    align-items: center;
}

.header-section {
    text-align: center;
    margin-bottom: 30px;
}

h1 {
    font-size: 2.5rem;
    margin: 0 0 10px 0;
    font-weight: 800;
}

p.description {
    color: var(--text-muted);
    max-width: 600px;
    margin: 0 auto;
    line-height: 1.6;
    font-size: 1.1rem;
}

.tool-container {
    background-color: var(--panel-bg);
    border: 1px solid var(--border-color);
    border-radius: 12px;
    padding: 24px;
    width: 100%;
    max-width: 900px;
    box-shadow: 0 4px 12px rgba(0,0,0,0.05);
}

/* Stats Grid */
.stats-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 16px;
    margin-bottom: 24px;
}

.stat-box {
    background-color: var(--bg-color);
    border: 1px solid var(--border-color);
    border-radius: 8px;
    padding: 16px;
    text-align: center;
}

.stat-value {
    font-size: 2rem;
    font-weight: 700;
    color: var(--primary-color);
    margin-bottom: 4px;
}

.stat-label {
    font-size: 0.9rem;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    font-weight: 600;
}

/* Text Area */
textarea {
    width: 100%;
    height: 250px;
    padding: 16px;
    border: 1px solid var(--border-color);
    border-radius: 8px;
    font-size: 1rem;
    line-height: 1.6;
    resize: vertical;
    box-sizing: border-box;
    font-family: inherit;
    margin-bottom: 20px;
    transition: border-color 0.2s ease;
}

textarea:focus {
    outline: none;
    border-color: var(--primary-color);
}

/* Buttons */
.controls {
    display: flex;
    gap: 12px;
}

button {
    padding: 10px 20px;
    font-size: 1rem;
    font-weight: 600;
    border-radius: 6px;
    cursor: pointer;
    border: none;
    transition: background-color 0.2s ease;
}

.btn-primary {
    background-color: var(--primary-color);
    color: #ffffff;
}

.btn-primary:hover {
    background-color: #0056b3;
}

.btn-secondary {
    background-color: transparent;
    color: var(--text-dark);
    border: 1px solid var(--border-color);
}

.btn-secondary:hover {
    background-color: var(--bg-color);
}
</code></pre>
<p>Understanding the CSS:</p>
<ul>
<li><p><strong>CSS Grid:</strong> The <code>.stats-grid</code> class uses <code>grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));</code>. This makes the four stat boxes automatically stack neatly on top of each other if a user views the tool on a small mobile screen.</p>
</li>
<li><p><strong>CSS Variables:</strong> Using <code>:root</code> at the top allows you to quickly change the brand color later if you want to use something other than the default blue (<code>#007bff</code>).</p>
</li>
</ul>
<p>At this point, the visual design of your tool is complete. Here's what the final Word Counter will look like in your browser:</p>
<img src="https://cdn.hashnode.com/uploads/covers/699c7b22cf5def0f6aaf982b/dc292767-ac0f-4e87-b88c-d64fab38f370.png" alt="Word Counter Tool" style="display: block;" width="600" height="400" loading="lazy">

<h2 id="heading-step-4-add-the-javascript-logic">Step 4: Add the JavaScript Logic</h2>
<p>Now you need to make the tool count the text. You'll use an event listener that watches every keystroke. Every time the user types, it recalculates the words, characters, sentences, and paragraphs.</p>
<p>Open your <code>script.js</code> file and paste this code:</p>
<pre><code class="language-javascript">const textInput = document.getElementById('textInput');
const wordCountDisplay = document.getElementById('wordCount');
const charCountDisplay = document.getElementById('charCount');
const sentenceCountDisplay = document.getElementById('sentenceCount');
const paragraphCountDisplay = document.getElementById('paragraphCount');
const copyBtn = document.getElementById('copyBtn');

// 1. Listen for user input in real-time
textInput.addEventListener('input', updateStatistics);

// 2. The Core Counting Logic
function updateStatistics() {
    const text = textInput.value;

    // Character Count (includes spaces)
    charCountDisplay.textContent = text.length;

    // Word Count
    const words = text.match(/\S+/g) || [];
    wordCountDisplay.textContent = words.length;

    // Sentence Count
    const sentences = text.split(/[.!?]+(?=\s|$)/).filter(sentence =&gt; sentence.trim().length &gt; 0);
    sentenceCountDisplay.textContent = sentences.length;

    // Paragraph Count
    const paragraphs = text.split(/\n+/).filter(paragraph =&gt; paragraph.trim().length &gt; 0);
    paragraphCountDisplay.textContent = paragraphs.length;
}

// 3. Copy functionality
function copyText() {
    if (!textInput.value) return;
    
    textInput.select();
    document.execCommand('copy');
    
    // Provide visual feedback
    copyBtn.textContent = 'Copied!';
    setTimeout(() =&gt; {
        copyBtn.textContent = 'Copy Text';
    }, 1500);
}

// 4. Clear functionality
function clearText() {
    textInput.value = '';
    updateStatistics(); // Reset counts to zero
}
</code></pre>
<p>Understanding the JavaScript:</p>
<ul>
<li><p><strong>The</strong> <code>input</code> <strong>Event:</strong> <code>textInput.addEventListener('input', ...)</code> is the secret to real-time updates. It triggers the counting function the exact moment a key is pressed or text is pasted.</p>
</li>
<li><p><strong>Counting Words:</strong> <code>text.match(/\S+/g)</code> is a Regular Expression that looks for unbroken strings of non-whitespace characters. This is much more accurate than just splitting the text by spaces, because it ignores extra empty spaces left by mistake.</p>
</li>
<li><p><strong>Counting Sentences:</strong> The <code>.split(/[.!?]+(?=\s|$)/)</code> logic cuts the text into pieces every time it sees a period, exclamation mark, or question mark followed by a space.</p>
</li>
</ul>
<h2 id="heading-step-5-test-your-application">Step 5: Test Your Application</h2>
<p>You're completely done coding! Now it's time to verify that your logic works correctly.</p>
<ol>
<li><p>Open your <code>word-counter-app</code> folder.</p>
</li>
<li><p>Double-click the <code>index.html</code> file to open it in your web browser.</p>
</li>
<li><p>Type a few sentences into the text area. Watch the numbers at the top update instantly.</p>
</li>
<li><p>Try adding double spaces or hitting the "Enter" key to make new paragraphs, and ensure the logic counts them accurately.</p>
</li>
<li><p>Click <strong>Copy Text</strong> to test the clipboard feature, and <strong>Clear</strong> to reset the board.</p>
</li>
</ol>
<p>By completing this project, you've built a fast, client-side utility tool using pure vanilla JavaScript. You learned how to manipulate strings, use Regular Expressions for text analysis, and create responsive UI grids.</p>
<p>If you want to see this exact codebase running in a live production environment, you can try out this <a href="https://99tools.net/word-counter/">Online Word Counter</a>. Keep building, and happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Bulk Image Compressor Tool with HTML, CSS, and JavaScript ]]>
                </title>
                <description>
                    <![CDATA[ High-resolution images look great, but they can significantly slow down page load times and consume massive amounts of storage. While backend compression tools are common, building a client-side image ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-bulk-image-compressor-tool-with-html-css-and-javascript/</link>
                <guid isPermaLink="false">6aad5cf52b32e1bee69c90a8</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Bansidhar Kadiya ]]>
                </dc:creator>
                <pubDate>Fri, 18 Sep 2026 15:47:01 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/f0d7a939-276b-4eb9-a380-6f376997dbe6.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>High-resolution images look great, but they can significantly slow down page load times and consume massive amounts of storage.</p>
<p>While backend compression tools are common, building a client-side image compressor offers a massive advantage: privacy. When you process images directly in the browser, no user data ever touches a server.</p>
<p>In this tutorial, you'll learn how to build a fully functional, browser-based bulk image compressor. You'll use the HTML5 Canvas API to reduce image file sizes and integrate the JSZip library to package multiple compressed files into a single, convenient ZIP download.</p>
<p>To make this project highly practical, you'll structure the code as an embeddable widget. By omitting standard HTML boilerplate tags, you can easily drop this snippet directly into a WordPress Custom HTML block or any other CMS without causing layout conflicts.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>To follow along, you should have a basic understanding of:</p>
<ul>
<li><p><strong>HTML &amp; CSS:</strong> Structuring a UI and creating interactive hover/drag states.</p>
</li>
<li><p><strong>JavaScript Promises:</strong> Handling asynchronous operations like file reading and ZIP generation.</p>
</li>
<li><p><strong>The Canvas API:</strong> Understanding how browsers can draw and manipulate image data natively.</p>
</li>
</ul>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a href="#heading-step-1-build-the-html-structure">Step 1: Build the HTML Structure</a></p>
<ul>
<li><a href="#heading-understanding-the-html">Understanding the HTML</a></li>
</ul>
</li>
<li><p><a href="#heading-step-2-style-the-interface-with-css">Step 2: Style the Interface with CSS</a></p>
<ul>
<li><a href="#heading-understanding-the-css">Understanding the CSS</a></li>
</ul>
</li>
<li><p><a href="#heading-step-3-add-the-javascript-logic">Step 3: Add the JavaScript Logic</a></p>
<ul>
<li><a href="#heading-understanding-the-javascript">Understanding the JavaScript</a></li>
</ul>
</li>
<li><p><a href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-step-1-build-the-html-structure">Step 1: Build the HTML Structure</h2>
<p>First, you need to create the visual interface. You'll build a drag-and-drop zone, an invisible file input, and a hidden action panel that appears once the user selects their images.</p>
<p>Add the following code to your file. Notice that you're including the JSZip CDN link right at the top so your script can access it later.</p>
<pre><code class="language-HTML">&lt;!-- JSZip Library for Bulk Downloading --&gt;
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"&gt;&lt;/script&gt;

&lt;div class="ic-main-wrapper"&gt;
    &lt;div class="ic-title"&gt;Image Compressor&lt;/div&gt;
    
    &lt;!-- The Drag and Drop Zone --&gt;
    &lt;div class="ic-dropzone-area" id="icDropzone"&gt;
        &lt;input type="file" id="icFileInput" multiple accept="image/jpeg, image/png, image/webp, image/gif, image/bmp, image/tiff, image/avif" style="display: none;"&gt;
        
        &lt;div class="ic-icon-box"&gt;
            &lt;svg viewBox="0 0 24 24"&gt;
                &lt;path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V5h14v14zm-5-7l-3 3.72L9 13l-3 4h14l-4-5z"/&gt;
            &lt;/svg&gt;
        &lt;/div&gt;
        
        &lt;div class="ic-primary-text"&gt;
            Drop images here or &lt;span class="ic-browse-link" id="icBrowseBtn"&gt;click to browse&lt;/span&gt;
        &lt;/div&gt;
        &lt;div class="ic-secondary-text"&gt;
            Multiple images supported — bulk compress &amp; download as ZIP
        &lt;/div&gt;
        
        &lt;div class="ic-format-tags"&gt;
            &lt;span&gt;JPEG&lt;/span&gt;&lt;span&gt;PNG&lt;/span&gt;&lt;span&gt;WebP&lt;/span&gt;&lt;span&gt;GIF&lt;/span&gt;&lt;span&gt;BMP&lt;/span&gt;&lt;span&gt;TIFF&lt;/span&gt;&lt;span&gt;AVIF&lt;/span&gt;
        &lt;/div&gt;
    &lt;/div&gt;
    
    &lt;!-- Action Panel (Hidden by default) --&gt;
    &lt;div class="ic-action-panel" id="icActionPanel"&gt;
        &lt;div id="icStatusText" class="ic-status-msg"&gt;0 images selected&lt;/div&gt;
        &lt;button class="ic-btn-primary" id="icCompressBtn"&gt;Compress &amp; Download ZIP&lt;/button&gt;
    &lt;/div&gt;
&lt;/div&gt;
</code></pre>
<h3 id="heading-understanding-the-html">Understanding the HTML:</h3>
<ul>
<li><p><code>accept="..."</code> <strong>attribute:</strong> This restricts the hidden <code>&lt;input type="file"&gt;</code> to only accept image formats, preventing users from accidentally uploading PDFs or text documents.</p>
</li>
<li><p><strong>Embeddable Structure:</strong> Because this markup relies on a single <code>.ic-main-wrapper</code> container rather than full <code>&lt;html&gt;</code> and <code>&lt;body&gt;</code> tags, you can safely embed it into existing web pages without breaking the parent theme.</p>
</li>
</ul>
<h2 id="heading-step-2-style-the-interface-with-css">Step 2: Style the Interface with CSS</h2>
<p>Next, you'll apply styling to make the tool look professional and responsive. You'll use a clean, modern aesthetic with a specific brand accent color (<code>#1A73E8</code>) to highlight interactive elements.</p>
<p>Add this <code>&lt;style&gt;</code> block right above your HTML:</p>
<pre><code class="language-CSS">&lt;style&gt;
.ic-main-wrapper {
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    max-width: 850px;
    margin: 20px auto;
    background: #ffffff;
    border-radius: 12px;
    padding: 30px;
    box-shadow: 0 4px 20px rgba(0,0,0,0.04);
}

.ic-title {
    font-size: 2.2rem;
    font-weight: 700;
    color: #0f172a;
    text-align: center;
    margin-bottom: 30px;
}

.ic-dropzone-area {
    border: 1.5px dashed #cbd5e1;
    border-radius: 12px;
    padding: 60px 20px;
    text-align: center;
    background-color: #f8fafc;
    transition: all 0.3s ease;
}

/* Active Drag State */
.ic-dropzone-area.dragover {
    border-color: #1A73E8;
    background-color: #f1f5f9;
}

.ic-icon-box {
    width: 64px;
    height: 64px;
    background-color: #e8f0fe;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    margin: 0 auto 24px;
    color: #1A73E8;
}

.ic-icon-box svg {
    width: 28px;
    height: 28px;
    fill: currentColor;
}

.ic-primary-text {
    font-size: 1.15rem;
    color: #0f172a;
    font-weight: 500;
    margin-bottom: 12px;
}

.ic-browse-link {
    color: #1A73E8;
    cursor: pointer;
    text-decoration: none;
    font-weight: 500;
}

.ic-browse-link:hover {
    text-decoration: underline;
}

.ic-secondary-text {
    font-size: 0.95rem;
    color: #64748b;
    margin-bottom: 24px;
}

.ic-format-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    justify-content: center;
}

.ic-format-tags span {
    border: 1px solid #e2e8f0;
    background: #ffffff;
    color: #64748b;
    font-size: 0.8rem;
    padding: 6px 18px;
    border-radius: 20px;
}

.ic-action-panel {
    margin-top: 25px;
    display: none;
    text-align: center;
    background: #f8fafc;
    padding: 20px;
    border-radius: 8px;
    border: 1px solid #e2e8f0;
}

.ic-btn-primary {
    background: #1A73E8;
    color: #ffffff;
    border: none;
    padding: 12px 28px;
    border-radius: 6px;
    font-size: 1rem;
    cursor: pointer;
    font-weight: 500;
    transition: background 0.3s;
}

.ic-btn-primary:hover {
    background: #1557b0;
}

.ic-btn-primary:disabled {
    background: #94a3b8;
    cursor: not-allowed;
}

.ic-status-msg {
    margin-bottom: 15px;
    font-size: 0.95rem;
    color: #334155;
    font-weight: 500;
}
&lt;/style&gt;
</code></pre>
<h3 id="heading-understanding-the-css">Understanding the CSS:</h3>
<ul>
<li><p><strong>The</strong> <code>.dragover</code> <strong>Class:</strong> This class alters the border and background color of the dropzone. You'll use JavaScript to apply this class dynamically when a user hovers a file over the area, providing crucial visual feedback.</p>
</li>
<li><p><strong>Unique Prefixes:</strong> Notice how every class starts with <code>ic-</code> (Image Compressor). This acts as a CSS namespace, ensuring your tool's styles won't accidentally override or be overridden by other styles on your website.</p>
</li>
</ul>
<p>At this stage, your user interface is fully structured and styled. It will look like this:</p>
<img src="https://cdn.hashnode.com/uploads/covers/699c7b22cf5def0f6aaf982b/2e75b26b-e1ae-443b-a78f-6f9833a4774a.png" alt="2e75b26b-e1ae-443b-a78f-6f9833a4774a" style="display: block;" width="1432" height="723" loading="lazy">

<h2 id="heading-step-3-add-the-javascript-logic">Step 3: Add the JavaScript Logic</h2>
<p>Now comes the engine of the tool. The JavaScript will handle file selection, convert the images using the Canvas API to reduce their size, and package them into a ZIP file.</p>
<p>Add this <code>&lt;script&gt;</code> block below your HTML:</p>
<pre><code class="language-javascript">&lt;script&gt;
document.addEventListener('DOMContentLoaded', () =&gt; {
    const dropzone = document.getElementById('icDropzone');
    const fileInput = document.getElementById('icFileInput');
    const browseBtn = document.getElementById('icBrowseBtn');
    const actionPanel = document.getElementById('icActionPanel');
    const statusText = document.getElementById('icStatusText');
    const compressBtn = document.getElementById('icCompressBtn');
    
    let filesArray = [];

    // 1. Handle File Input and Drag &amp; Drop
    browseBtn.addEventListener('click', (e) =&gt; {
        e.stopPropagation();
        fileInput.click();
    });
    
    dropzone.addEventListener('dragover', (e) =&gt; {
        e.preventDefault();
        dropzone.classList.add('dragover');
    });
    
    dropzone.addEventListener('dragleave', () =&gt; {
        dropzone.classList.remove('dragover');
    });
    
    dropzone.addEventListener('drop', (e) =&gt; {
        e.preventDefault();
        dropzone.classList.remove('dragover');
        if (e.dataTransfer.files.length) {
            processSelectedFiles(e.dataTransfer.files);
        }
    });

    fileInput.addEventListener('change', () =&gt; {
        if (fileInput.files.length) {
            processSelectedFiles(fileInput.files);
        }
    });

    // 2. Validate and Display the Action Panel
    function processSelectedFiles(files) {
        filesArray = Array.from(files).filter(file =&gt; file.type.startsWith('image/'));
        if (filesArray.length &gt; 0) {
            actionPanel.style.display = 'block';
            statusText.innerText = `${filesArray.length} image(s) selected. Ready to compress.`;
        }
    }

    // 3. The Core Compression Engine
    function compressImage(file) {
        return new Promise((resolve) =&gt; {
            const reader = new FileReader();
            reader.onload = (event) =&gt; {
                const img = new Image();
                img.onload = () =&gt; {
                    const canvas = document.createElement('canvas');
                    canvas.width = img.width;
                    canvas.height = img.height;
                    const ctx = canvas.getContext('2d');
                    
                    // Draw the image onto the canvas
                    ctx.drawImage(img, 0, 0);
                    
                    // Force compatibility: Convert unusual formats to JPEG or WebP
                    let mimeType = file.type;
                    if (mimeType !== 'image/jpeg' &amp;&amp; mimeType !== 'image/webp') {
                        mimeType = 'image/jpeg';
                    }
                    
                    // Compress using the canvas.toBlob method at 70% quality (0.7)
                    canvas.toBlob((blob) =&gt; {
                        let finalName = file.name;
                        
                        // Ensure the file extension matches the new mimeType
                        if (mimeType === 'image/jpeg' &amp;&amp; !finalName.match(/\.(jpg|jpeg)$/i)) {
                            finalName = finalName.substring(0, finalName.lastIndexOf('.')) + '.jpg';
                        }
                        
                        resolve({ name: finalName, blob: blob });
                    }, mimeType, 0.7); 
                };
                img.src = event.target.result;
            };
            reader.readAsDataURL(file);
        });
    }

    // 4. Handle the Bulk ZIP Process
    compressBtn.addEventListener('click', async () =&gt; {
        if (filesArray.length === 0) return;
        
        compressBtn.disabled = true;
        
        try {
            const zip = new JSZip();
            const folder = zip.folder("compressed_images");
            
            // Loop through each file and await its compression
            for (let i = 0; i &lt; filesArray.length; i++) {
                statusText.innerText = `Compressing ${i + 1} of ${filesArray.length}...`;
                const compressedData = await compressImage(filesArray[i]);
                folder.file(compressedData.name, compressedData.blob);
            }
            
            statusText.innerText = 'Creating ZIP file...';
            
            // Generate and trigger the ZIP download
            const zipContent = await zip.generateAsync({ type: "blob" });
            const downloadLink = document.createElement('a');
            downloadLink.href = URL.createObjectURL(zipContent);
            downloadLink.download = "compressed_images.zip";
            downloadLink.click();
            
            statusText.innerText = 'Download successful!';
        } catch (error) {
            statusText.innerText = 'An error occurred during compression.';
            console.error(error);
        } finally {
            compressBtn.disabled = false;
        }
    });
});
&lt;/script&gt;
</code></pre>
<h3 id="heading-understanding-the-javascript">Understanding the JavaScript:</h3>
<ul>
<li><p><strong>Drag and drop events:</strong> The <code>dragover</code>, <code>dragleave</code>, and <code>drop</code> event listeners work together to capture files dragged from a user's desktop directly into the browser window.</p>
</li>
<li><p><strong>The canvas trick:</strong> Browsers can't magically compress files on their own. Instead, the <code>compressImage</code> function reads the image, draws it onto an invisible HTML5 <code>&lt;canvas&gt;</code>, and then uses the <code>canvas.toBlob()</code> method to export a new, optimized version of that image. The <code>0.7</code> argument sets the compression quality to 70%.</p>
</li>
<li><p><strong>Extension handling:</strong> Because the canvas exports images as either JPEG or WebP, the script actively checks and renames the file extensions (for example, changing a <code>.png</code> filename to <code>.jpg</code>) before packaging them.</p>
</li>
<li><p><strong>Asynchronous zipping:</strong> A <code>for</code> loop uses <code>await</code> to compress each image sequentially. Once complete, JSZip bundles the blobs into a folder and triggers a programmatic click event on a temporary anchor <code>&lt;a&gt;</code> tag to download the final <code>.zip</code> file.</p>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>You've successfully built a fast, client-side bulk image compressor.</p>
<p>By leveraging the Canvas API alongside JSZip, you created a highly practical utility that saves users bandwidth and storage without compromising their privacy. Because the entire codebase is self-contained without HTML boilerplate, it's ready to be deployed instantly on almost any modern web platform.</p>
<p>If you want to see this codebase deployed in a live, production environment, you can test out the functionality over at this Live <a href="https://99tools.net/image-compressor/">Image Compressor Tool</a>. Happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Text Compare Tool with HTML, CSS, and JavaScript ]]>
                </title>
                <description>
                    <![CDATA[ Have you ever tried to spot the differences between two long paragraphs of text? Reading line-by-line to find a missing word or a new sentence is a massive headache. In this tutorial, you'll build you ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-text-compare-tool-html-css-javascript/</link>
                <guid isPermaLink="false">6a4533f11247307c0491a76f</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ HTML5 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Tutorial ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Bansidhar Kadiya ]]>
                </dc:creator>
                <pubDate>Wed, 01 Jul 2026 15:36:17 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/632b58c6-8930-421d-b17d-847b24bb0e9e.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Have you ever tried to spot the differences between two long paragraphs of text? Reading line-by-line to find a missing word or a new sentence is a massive headache.</p>
<p>In this tutorial, you'll build your very own browser-based Text Compare Tool. It will take an original piece of text, compare it against a changed version, and instantly highlight exactly what was added or removed.</p>
<p>Building this project will help you level up your JavaScript skills. You'll also create a tool that's highly secure, because everything happens locally in the user's browser. No sensitive data is ever sent to a server.</p>
<p>Let’s get started.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>To follow along easily, you should know:</p>
<ul>
<li><p><strong>Basic HTML and CSS knowledge:</strong> How to structure a page and use Flexbox to put items side-by-side.</p>
</li>
<li><p><strong>Basic JavaScript knowledge:</strong> How to write functions, use arrays, and listen for button clicks.</p>
</li>
<li><p><strong>Your Setup:</strong> A code editor (like VS Code) and a web browser to view your work.</p>
</li>
</ul>
<h2 id="heading-table-of-contents"><strong>Table of Contents</strong></h2>
<ul>
<li><p><a href="#heading-step-1-set-up-your-project-files">Step 1: Set Up Your Project Files</a></p>
</li>
<li><p><a href="#heading-step-2-build-the-html-structure">Step 2: Build the HTML Structure</a></p>
</li>
<li><p><a href="#heading-step-3-style-the-tool-with-css">Step 3: Style the Tool with CSS</a></p>
</li>
<li><p><a href="#heading-step-4-write-the-javascript-engine">Step 4: Write the JavaScript Engine</a></p>
</li>
<li><p><a href="#heading-step-5-test-your-application">Step 5: Test Your Application</a></p>
</li>
<li><p><a href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-step-1-set-up-your-project-files">Step 1: Set Up Your Project Files</h2>
<p>First, you need a place to store your code. Create a new folder on your computer and name it <code>text-compare-tool</code>.</p>
<p>Inside that folder, create three empty files:</p>
<ul>
<li><p><code>index.html</code> (This holds the structure of your app)</p>
</li>
<li><p><code>style.css</code> (This makes your app look good)</p>
</li>
<li><p><code>script.js</code> (This makes your app actually work)</p>
</li>
</ul>
<h2 id="heading-step-2-build-the-html-structure">Step 2: Build the HTML Structure</h2>
<p>Open your <code>index.html</code> file. You need to create a simple layout with two large text boxes: one for the original text, and one for the updated text.</p>
<p>Copy and paste this code into your HTML file:</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
    &lt;title&gt;Text Compare Tool&lt;/title&gt;
    &lt;link rel="stylesheet" href="style.css"&gt;
&lt;/head&gt;
&lt;body&gt;

    &lt;h1&gt;Text Compare Tool&lt;/h1&gt;
    &lt;p class="description"&gt;
        Quickly find every addition and deletion between two versions of your text. Just paste them into our tool, and we’ll show you exactly what’s been changed.
    &lt;/p&gt;

    &lt;div class="container"&gt;
        
        &lt;div class="panels-wrapper"&gt;
            &lt;!-- Left Side: Original Text --&gt;
            &lt;div class="panel"&gt;
                &lt;textarea id="text1" placeholder="Paste your original text here..."&gt;&lt;/textarea&gt;
                &lt;div id="result1" class="result-box"&gt;&lt;/div&gt;
            &lt;/div&gt;
            
            &lt;!-- Right Side: Changed Text --&gt;
            &lt;div class="panel"&gt;
                &lt;textarea id="text2" placeholder="Paste your changed text here..."&gt;&lt;/textarea&gt;
                &lt;div id="result2" class="result-box"&gt;&lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;!-- Action Buttons --&gt;
        &lt;div class="controls"&gt;
            &lt;button class="btn-compare" onclick="compareText()"&gt;Compare&lt;/button&gt;
            &lt;button class="btn-clear" onclick="clearText()"&gt;Clear&lt;/button&gt;
        &lt;/div&gt;

    &lt;/div&gt;

    &lt;script src="script.js"&gt;&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>Understanding the HTML:</p>
<ul>
<li><p><strong>The two panels:</strong> Inside the <code>.panels-wrapper</code>, you have a left side and a right side.</p>
</li>
<li><p><strong>Textareas vs results:</strong> Each side has a <code>&lt;textarea&gt;</code> where the user can type. Right below the text area is a <code>&lt;div&gt;</code> with the class <code>.result-box</code>. Right now, those result boxes are invisible. Later, JavaScript will hide the text areas and show the result boxes instead.</p>
</li>
<li><p><strong>The buttons:</strong> The "Compare" and "Clear" buttons are hooked up to JavaScript functions using <code>onclick</code>.</p>
</li>
</ul>
<h2 id="heading-step-3-style-the-tool-with-css">Step 3: Style the Tool with CSS</h2>
<p>A good utility tool should be easy on the eyes. You'll use a clean white and blue design, and apply soft red and green colors to highlight the text changes.</p>
<p>Open your <code>style.css</code> file and add this code:</p>
<pre><code class="language-css">:root {
    --primary-blue: #007bff;
    --background-color: #f8f9fa;
    --text-color: #202124;
    --border-color: #dadce0;
    
    /* Highlight Colors */
    --red-bg: #fce8e6;
    --red-text: #c5221f;
    --green-bg: #e6f4ea;
    --green-text: #137333;
}

body {
    font-family: Arial, sans-serif;
    background-color: var(--background-color);
    color: var(--text-color);
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 40px 20px;
    margin: 0;
}

h1 {
    margin-bottom: 10px;
}

.description {
    text-align: center;
    max-width: 600px;
    color: #5f6368;
    margin-bottom: 30px;
    line-height: 1.5;
}

.container {
    background: white;
    padding: 20px;
    border-radius: 8px;
    border: 1px solid var(--border-color);
    width: 100%;
    max-width: 1000px;
    box-shadow: 0 4px 10px rgba(0,0,0,0.05);
}

.panels-wrapper {
    display: flex;
    gap: 20px;
    margin-bottom: 20px;
}

.panel {
    flex: 1;
    display: flex;
    flex-direction: column;
}

textarea, .result-box {
    width: 100%;
    height: 300px;
    padding: 15px;
    border: 1px solid var(--border-color);
    border-radius: 6px;
    font-size: 16px;
    line-height: 1.5;
    box-sizing: border-box;
    resize: vertical;
}

textarea:focus {
    outline: none;
    border-color: var(--primary-blue);
}

/* Hidden by default */
.result-box {
    display: none; 
    background-color: #fafafa;
    overflow-y: auto;
    white-space: pre-wrap; 
}

.controls {
    display: flex;
    justify-content: center;
    gap: 15px;
}

button {
    padding: 10px 25px;
    font-size: 16px;
    font-weight: bold;
    border: none;
    border-radius: 5px;
    cursor: pointer;
}

.btn-compare {
    background-color: var(--primary-blue);
    color: white;
}

.btn-clear {
    background-color: white;
    color: var(--primary-blue);
    border: 1px solid var(--border-color);
}

/* How the differences will look */
.deleted {
    background-color: var(--red-bg);
    color: var(--red-text);
    padding: 2px 4px;
    border-radius: 3px;
}

.added {
    background-color: var(--green-bg);
    color: var(--green-text);
    padding: 2px 4px;
    border-radius: 3px;
}
</code></pre>
<p>Understanding the CSS:</p>
<ul>
<li><p><strong>Flexbox layout:</strong> <code>display: flex;</code> inside <code>.panels-wrapper</code> is what places your two text boxes neatly side-by-side.</p>
</li>
<li><p><strong>The highlighters:</strong> The <code>.deleted</code> and <code>.added</code> classes are the most important part of the visual design. When a user deletes a word, we give it a soft red background. When they add a word, it gets a soft green background.</p>
</li>
</ul>
<p>This is what your tool will look like once it's finished:</p>
<img src="https://cdn.hashnode.com/uploads/covers/699c7b22cf5def0f6aaf982b/6676b86c-c0ea-4dec-b5b7-489e8e06f58b.png" alt="Text Compare Tool Preview" style="display: block;" width="1874" height="872" loading="lazy">

<h2 id="heading-step-4-write-the-javascript-engine">Step 4: Write the JavaScript Engine</h2>
<p>Now you need to make the tool actually work. How does your computer know if a word has changed?</p>
<p>We have to write logic that breaks paragraphs down into individual words. The code will look at the original list of words and compare it to the new list. If a word from the original text is missing, it gets marked as "deleted." If a brand new word appears, it gets marked as "added."</p>
<p>Open your <code>script.js</code> file and paste in this complete, working code:</p>
<pre><code class="language-javascript">function compareText() {
    // 1. Grab the text from the text boxes
    const text1 = document.getElementById('text1').value;
    const text2 = document.getElementById('text2').value;

    // 2. Chop the text up into an array of words (and keep the spaces)
    const words1 = text1.split(/(\s+)/);
    const words2 = text2.split(/(\s+)/);

    // 3. Find the differences
    const { diff1, diff2 } = calculateDifferences(words1, words2);

    const resultBox1 = document.getElementById('result1');
    const resultBox2 = document.getElementById('result2');
    
    // 4. Turn those differences into HTML with colors
    resultBox1.innerHTML = createColoredHTML(diff1, 'deleted');
    resultBox2.innerHTML = createColoredHTML(diff2, 'added');

    // 5. Hide the text boxes and show the final results
    document.getElementById('text1').style.display = 'none';
    document.getElementById('text2').style.display = 'none';
    resultBox1.style.display = 'block';
    resultBox2.style.display = 'block';
}

// The engine that compares the two lists of words
function calculateDifferences(arr1, arr2) {
    const n = arr1.length;
    const m = arr2.length;
    
    // Create a grid to keep track of matching words
    const grid = Array.from({ length: n + 1 }, () =&gt; Array(m + 1).fill(0));

    for (let i = 1; i &lt;= n; i++) {
        for (let j = 1; j &lt;= m; j++) {
            if (arr1[i - 1] === arr2[j - 1]) {
                grid[i][j] = grid[i - 1][j - 1] + 1;
            } else {
                grid[i][j] = Math.max(grid[i - 1][j], grid[i][j - 1]);
            }
        }
    }

    let i = n, j = m;
    const diff1 = [];
    const diff2 = [];

    // Walk backwards through the grid to mark what changed
    while (i &gt; 0 || j &gt; 0) {
        if (i &gt; 0 &amp;&amp; j &gt; 0 &amp;&amp; arr1[i - 1] === arr2[j - 1]) {
            diff1.unshift({ value: arr1[i - 1], type: 'equal' });
            diff2.unshift({ value: arr2[j - 1], type: 'equal' });
            i--;
            j--;
        } else if (j &gt; 0 &amp;&amp; (i === 0 || grid[i][j - 1] &gt;= grid[i - 1][j])) {
            diff2.unshift({ value: arr2[j - 1], type: 'changed' });
            j--;
        } else if (i &gt; 0 &amp;&amp; (j === 0 || grid[i][j - 1] &lt; grid[i - 1][j])) {
            diff1.unshift({ value: arr1[i - 1], type: 'changed' });
            i--;
        }
    }

    return { diff1, diff2 };
}

// Packages the text safely into HTML span elements
function createColoredHTML(diffArray, colorClass) {
    return diffArray.map(wordItem =&gt; {
        // Replace dangerous characters so the browser doesn't crash
        const safeText = wordItem.value.replace(/&lt;/g, "&amp;lt;").replace(/&gt;/g, "&amp;gt;");
        
        // If the word was changed (and isn't just a blank space), wrap it in color
        if (wordItem.type === 'changed' &amp;&amp; !/^\s+$/.test(wordItem.value)) {
            return `&lt;span class="${colorClass}"&gt;${safeText}&lt;/span&gt;`;
        }
        return safeText;
    }).join('');
}

// Puts the tool back to its default state
function clearText() {
    document.getElementById('text1').value = '';
    document.getElementById('text2').value = '';
    
    document.getElementById('text1').style.display = 'block';
    document.getElementById('text2').style.display = 'block';
    
    document.getElementById('result1').style.display = 'none';
    document.getElementById('result2').style.display = 'none';
}
</code></pre>
<p>Understanding the JavaScript:</p>
<ol>
<li><p><strong>Keeping the formatting:</strong> In the first function, you see <code>.split(/(\s+)/)</code>. This splits the text up by spaces, but <em>keeps</em> the spaces and line-breaks. If you don't do this, all of the user's paragraphs will mash into one giant block of text!</p>
</li>
<li><p><strong>The grid system:</strong> The <code>calculateDifferences</code> function creates an invisible grid. It compares every word in the first box with every word in the second box. If it sees the same word in the same order, it leaves it alone. If it hits a snag, it marks the word as a change.</p>
</li>
<li><p><strong>Safety first:</strong> The <code>createColoredHTML</code> function wraps our changed words in <code>&lt;span class="added"&gt;</code> or <code>&lt;span class="deleted"&gt;</code> so CSS can color them. But before it does that, it removes any <code>&lt;</code> or <code>&gt;</code> symbols using <code>.replace()</code>. This stops hackers from pasting malicious code into your app.</p>
</li>
</ol>
<h2 id="heading-step-5-test-your-application">Step 5: Test Your Application</h2>
<p>You're completely done coding! Now it’s time to see it in action.</p>
<ol>
<li><p>Open your <code>text-compare-tool</code> folder.</p>
</li>
<li><p>Double-click the <code>index.html</code> file. It will open in your default web browser.</p>
</li>
<li><p>Type a sentence into the left box: <em>"The quick brown fox jumps over the lazy dog."</em></p>
</li>
<li><p>Type a slightly different sentence into the right box: <em>"The fast brown fox jumps over the sleepy dog."</em></p>
</li>
<li><p>Click <strong>Compare</strong>.</p>
</li>
</ol>
<p>You will instantly see the word "quick" highlight in red on the left, and the word "fast" highlight in green on the right. If you want to start over, just click <strong>Clear</strong>.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Great job! You just built a highly practical, browser-based text comparison utility using nothing but pure HTML, CSS, and JavaScript.</p>
<p>You learned how to break text into arrays, compare them using a grid-based algorithm, and manipulate the DOM to show those differences to the user safely. Because this tool relies on local browser processing, it's incredibly fast and 100% private.</p>
<p>If you want to see this exact logic running in a live production environment, or if you need to bookmark a fast tool for your own writing tasks, check out the live <a href="https://99tools.net/text-compare-tool/">Text Compare Tool</a>. Keep experimenting with the code, and happy building!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Case Converter Tool Using HTML, CSS, and JavaScript ]]>
                </title>
                <description>
                    <![CDATA[ If you're looking to level up your front-end development skills by building a practical web utility, this is the guide for you. We'll code a fully functional Case Converter Tool from scratch using onl ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-case-converter-tool/</link>
                <guid isPermaLink="false">6a2bba6e86b91d1d78662a12</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ HTML5 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Bansidhar Kadiya ]]>
                </dc:creator>
                <pubDate>Fri, 12 Jun 2026 07:51:10 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/72153c4c-a59f-4cc8-a6c5-2bd457c729ab.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If you're looking to level up your front-end development skills by building a practical web utility, this is the guide for you.</p>
<p>We'll code a fully functional Case Converter Tool from scratch using only HTML, CSS, and vanilla JavaScript.</p>
<p>This lightweight application allows users to paste their content and immediately transform it into standard formats like UPPERCASE, lowercase, Title Case, and Sentence case.</p>
<p>Alongside the text formatting, we'll integrate a live character counter and set up functionality to export the final text as a PDF or Word document.</p>
<p>Grab your favorite code editor, and let's dive in.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before you begin, you should have a basic familiarity with the following tools and concepts:</p>
<ul>
<li><p><strong>Core Web Technologies:</strong> A fundamental understanding of HTML structure, basic CSS styling, and JavaScript concepts like functions, array methods, and string manipulation.</p>
</li>
<li><p><strong>Development Environment:</strong> A code editor installed on your computer (for example, Visual Studio Code) and a modern web browser to test your application locally.</p>
</li>
</ul>
<h2 id="heading-table-of-contents"><strong>Table of Contents</strong></h2>
<ul>
<li><p><a href="#heading-step-1-set-up-your-project">Step 1: Set Up Your Project</a></p>
</li>
<li><p><a href="#heading-step-2-build-the-html-structure">Step 2: Build the HTML Structure</a></p>
</li>
<li><p><a href="#heading-step-3-style-the-tool-with-css">Step 3: Style the Tool with CSS</a></p>
</li>
<li><p><a href="#heading-step-4-add-javascript-functionality">Step 4: Add JavaScript Functionality</a></p>
</li>
<li><p><a href="#heading-step-5-test-your-tool">Step 5: Test Your Tool</a></p>
</li>
<li><p><a href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-step-1-set-up-your-project">Step 1: Set Up Your Project</h2>
<p>Before writing any code, you need to establish a clean directory structure for your application files.</p>
<p>First, you'll need to initialize a workspace. Open your file manager and create a brand new directory to keep your work organized. Let's name this directory <code>case-converter-app</code>.</p>
<p>Then you'll generate the required files. Inside your newly created directory, set up the following three blank files:</p>
<ul>
<li><p><code>index.html</code></p>
</li>
<li><p><code>styles.css</code></p>
</li>
<li><p><code>script.js</code></p>
</li>
</ul>
<h2 id="heading-step-2-build-the-html-structure">Step 2: Build the HTML Structure</h2>
<p>Open the <code>index.html</code> file in your code editor. You'll add the structural foundation of the tool here.</p>
<p>Add the following code into your <code>index.html</code> file:</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
    &lt;title&gt;Case Converter Tool&lt;/title&gt;
    &lt;link rel="stylesheet" href="styles.css"&gt;
    
    &lt;!-- jsPDF library for generating PDF files --&gt;
    &lt;script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"&gt;&lt;/script&gt;
    &lt;!-- Google Fonts for a modern look --&gt;
    &lt;link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&amp;display=swap" rel="stylesheet"&gt;
&lt;/head&gt;
&lt;body&gt;

    &lt;div class="app-container"&gt;
        
        &lt;div class="editor-section"&gt;
            &lt;div class="textarea-header"&gt;
                &lt;span class="tip-badge"&gt;💡 Tip: Use Download buttons to save results&lt;/span&gt;
            &lt;/div&gt;
            &lt;textarea id="inputText" placeholder="Type or paste your content here..."&gt;&lt;/textarea&gt;
        &lt;/div&gt;
        
        &lt;!-- Case Conversion Buttons --&gt;
        &lt;div class="button-grid case-buttons"&gt;
            &lt;button class="case-btn" onclick="convertCase(event, 'upper')"&gt;UPPER CASE&lt;/button&gt;
            &lt;button class="case-btn" onclick="convertCase(event, 'lower')"&gt;lower case&lt;/button&gt;
            &lt;button class="case-btn" onclick="convertCase(event, 'capitalized')"&gt;Capitalized Case&lt;/button&gt;
            &lt;button class="case-btn" onclick="convertCase(event, 'title')"&gt;Title Case&lt;/button&gt;
            &lt;button class="case-btn" onclick="convertCase(event, 'sentence')"&gt;Sentence case&lt;/button&gt;
            &lt;button class="case-btn" onclick="convertCase(event, 'inverse')"&gt;iNvErSe CaSe&lt;/button&gt;
            &lt;button class="case-btn" onclick="convertCase(event, 'alternate')"&gt;aLtErNaTiNg cAsE&lt;/button&gt;
        &lt;/div&gt;

        &lt;div class="divider"&gt;&lt;/div&gt;

        &lt;!-- Action Buttons --&gt;
        &lt;div class="button-grid action-buttons"&gt;
            &lt;button class="action-btn primary-action copy-btn" onclick="copyToClipboard()"&gt;Copy To Clipboard&lt;/button&gt;
            &lt;button class="action-btn" onclick="downloadPDF()"&gt;Download PDF&lt;/button&gt;
            &lt;button class="action-btn" onclick="downloadWord()"&gt;Download Word&lt;/button&gt;
            &lt;button class="action-btn danger-action" onclick="clearText()"&gt;Clear Text&lt;/button&gt;
        &lt;/div&gt;

        &lt;!-- Real-time Statistics --&gt;
        &lt;div class="stats-panel"&gt;
            &lt;div class="stat-box"&gt;
                &lt;span class="stat-value" id="charCount"&gt;0&lt;/span&gt;
                &lt;span class="stat-label"&gt;Characters&lt;/span&gt;
            &lt;/div&gt;
            &lt;div class="stat-box"&gt;
                &lt;span class="stat-value" id="wordCount"&gt;0&lt;/span&gt;
                &lt;span class="stat-label"&gt;Words&lt;/span&gt;
            &lt;/div&gt;
            &lt;div class="stat-box"&gt;
                &lt;span class="stat-value" id="paragraphCount"&gt;0&lt;/span&gt;
                &lt;span class="stat-label"&gt;Paragraphs&lt;/span&gt;
            &lt;/div&gt;
            &lt;div class="stat-box"&gt;
                &lt;span class="stat-value" id="sentenceCount"&gt;0&lt;/span&gt;
                &lt;span class="stat-label"&gt;Sentences&lt;/span&gt;
            &lt;/div&gt;
        &lt;/div&gt;

    &lt;/div&gt;

    &lt;script src="script.js"&gt;&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>Understanding this HTML:</p>
<ul>
<li><p><code>&lt;script src="...jspdf..."&gt;&lt;/script&gt;</code>: This links to an external library that allows JavaScript to generate PDF files directly in the user's browser.</p>
</li>
<li><p><code>&lt;textarea id="inputText"&gt;</code>: This creates the main text box where users will paste their content.</p>
</li>
<li><p><code>&lt;div class="stats-panel"&gt;</code>: This section contains <code>span</code> elements with unique IDs. You'll target these IDs with JavaScript to update the text statistics in real-time.</p>
</li>
</ul>
<h2 id="heading-step-3-style-the-tool-with-css">Step 3: Style the Tool with CSS</h2>
<p>Next, you'll give the tool a clean, professional design. Open your <code>styles.css</code> file and add the following code:</p>
<pre><code class="language-css">* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    font-family: 'Inter', sans-serif;
}

body {
    background: linear-gradient(135deg, #e0eafc 0%, #cfdef3 100%);
    min-height: 100vh;
    display: flex;
    justify-content: center;
    align-items: center;
    padding: 2rem;
    color: #1e293b;
}

.app-container {
    background: #ffffff;
    width: 100%;
    max-width: 900px;
    border-radius: 24px;
    box-shadow: 0 20px 40px rgba(0,0,0,0.08);
    padding: 2.5rem;
}

.textarea-header {
    display: flex;
    justify-content: flex-end;
    margin-bottom: 0.5rem;
}

.tip-badge {
    background: #fef08a;
    color: #854d0e;
    padding: 0.35rem 0.85rem;
    border-radius: 20px;
    font-size: 0.75rem;
    font-weight: 600;
}

textarea {
    width: 100%;
    height: 220px;
    padding: 1.5rem;
    border: 2px solid #e2e8f0;
    border-radius: 16px;
    font-size: 1rem;
    resize: vertical;
    outline: none;
    transition: all 0.3s ease;
    background: #f8fafc;
}

textarea:focus {
    border-color: #007bff;
    background: #fff;
    box-shadow: 0 0 0 4px rgba(0, 123, 255, 0.1);
}

.button-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin-top: 1.5rem;
}

button {
    padding: 0.75rem 1.25rem;
    border: none;
    border-radius: 12px;
    font-size: 0.875rem;
    font-weight: 600;
    cursor: pointer;
    transition: all 0.2s ease;
}

.case-btn {
    background: #f1f5f9;
    color: #475569;
    border: 1px solid #e2e8f0;
}

.case-btn:hover { 
    background: #e2e8f0; 
}

/* The active class highlights the selected button */
.case-btn.active {
    background: #007bff;
    color: #fff;
    border-color: #007bff;
    box-shadow: 0 4px 12px rgba(0, 123, 255, 0.25);
}

.divider {
    height: 1px;
    background: #e2e8f0;
    margin: 1.5rem 0;
}

.action-btn { 
    background: #fff; 
    border: 1px solid #cbd5e1; 
}

.action-btn:hover { 
    background: #f8fafc; 
    border-color: #94a3b8; 
}

.primary-action { 
    background: #007bff; 
    color: #fff; 
    border-color: #007bff; 
}

.primary-action:hover { 
    background: #0056b3; 
    border-color: #0056b3; 
}

.danger-action { 
    color: #ef4444; 
    border-color: #fca5a5; 
    background: #fef2f2; 
}

.danger-action:hover { 
    background: #fee2e2; 
    border-color: #f87171; 
}

.stats-panel {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
    gap: 1rem;
    margin-top: 2rem;
    background: #f8fafc;
    padding: 1.5rem;
    border-radius: 16px;
    border: 1px solid #e2e8f0;
}

.stat-box { 
    display: flex; 
    flex-direction: column; 
    align-items: center; 
}

.stat-value { 
    font-size: 1.75rem; 
    font-weight: 700; 
}

.stat-label { 
    font-size: 0.75rem; 
    color: #64748b; 
    text-transform: uppercase; 
}
</code></pre>
<p>Understanding this CSS:</p>
<ul>
<li><p><code>body</code>: You use Flexbox to center the tool perfectly on the screen and apply a soft gradient background.</p>
</li>
<li><p><code>.app-container</code>: This creates a white, rounded card with a soft shadow to hold the user interface.</p>
</li>
<li><p><code>.case-btn.active</code>: You define an active state here. You'll use JavaScript to apply this class to the specific button the user clicks.</p>
</li>
</ul>
<p>At this stage, we've completely structured and styled the user interface. The tool will look like this:</p>
<img src="https://cdn.hashnode.com/uploads/covers/699c7b22cf5def0f6aaf982b/53e128aa-fb0d-47f3-8dca-9e3e0aa130c1.png" alt="Case Converter Tool screenshot" style="display: block;" width="1315" height="887" loading="lazy">

<p>Right now, the front-end is visible, but the buttons are entirely static. To make the transformations actually work, we have to write the logic in JavaScript.</p>
<h2 id="heading-step-4-add-javascript-functionality">Step 4: Add JavaScript Functionality</h2>
<p>Now you need to make the tool interactive. Open the <code>script.js</code> file and add this code:</p>
<pre><code class="language-javascript">const textArea = document.getElementById('inputText');

// Listen for typing to update statistics in real-time
textArea.addEventListener('input', updateStats);

function updateStats() {
    const text = textArea.value;
    
    document.getElementById('charCount').textContent = text.length;
    
    const words = text.trim().split(/\s+/).filter(word =&gt; word.length &gt; 0);
    document.getElementById('wordCount').textContent = words.length;
    
    const sentences = text.split(/[.!?]+/).filter(sentence =&gt; sentence.trim().length &gt; 0);
    document.getElementById('sentenceCount').textContent = sentences.length;
    
    const paragraphs = text.split(/\n+/).filter(paragraph =&gt; paragraph.trim().length &gt; 0);
    document.getElementById('paragraphCount').textContent = paragraphs.length;
}

function convertCase(event, type) {
    let text = textArea.value;
    if (!text) return; 

    // Highlight the active button
    const buttons = document.querySelectorAll('.case-btn');
    buttons.forEach(btn =&gt; btn.classList.remove('active'));
    if (event) {
        event.target.classList.add('active');
    }

    // Process the text
    switch (type) {
        case 'upper':
            text = text.toUpperCase();
            break;
        case 'lower':
            text = text.toLowerCase();
            break;
        case 'capitalized':
            text = text.toLowerCase().replace(/\b\w/g, c =&gt; c.toUpperCase());
            break;
        case 'title':
            const minorWords = ['a', 'an', 'the', 'and', 'but', 'or', 'for', 'nor', 'on', 'at', 'to', 'from', 'by'];
            text = text.toLowerCase().split(' ').map((word, index) =&gt; {
                if (index !== 0 &amp;&amp; minorWords.includes(word)) return word;
                return word.charAt(0).toUpperCase() + word.slice(1);
            }).join(' ');
            break;
        case 'sentence':
            text = text.toLowerCase().replace(/(^\s*\w|[\.\!\?]\n*\s*\w)/g, c =&gt; c.toUpperCase());
            break;
        case 'inverse':
            text = text.split('').map(c =&gt; c === c.toUpperCase() ? c.toLowerCase() : c.toUpperCase()).join('');
            break;
        case 'alternate':
            text = text.toLowerCase().split('').map((c, i) =&gt; i % 2 === 0 ? c : c.toUpperCase()).join('');
            break;
    }

    textArea.value = text;
    updateStats(); 
}

function copyToClipboard() {
    if (!textArea.value) return;
    textArea.select();
    document.execCommand('copy');
    
    const copyBtn = document.querySelector('.copy-btn');
    copyBtn.textContent = 'Copied!';
    setTimeout(() =&gt; copyBtn.textContent = 'Copy To Clipboard', 1500);
}

function clearText() {
    textArea.value = '';
    updateStats();
    document.querySelectorAll('.case-btn').forEach(btn =&gt; btn.classList.remove('active'));
}

function downloadWord() {
    if (!textArea.value) return;
    const blob = new Blob([textArea.value], { type: 'application/msword' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = 'converted_text.doc';
    a.click();
    URL.revokeObjectURL(url);
}

function downloadPDF() {
    if (!textArea.value) return;
    const { jsPDF } = window.jspdf;
    const doc = new jsPDF();
    const splitText = doc.splitTextToSize(textArea.value, 180);
    doc.text(splitText, 15, 15);
    doc.save('converted_text.pdf');
}
</code></pre>
<p>Understanding this JavaScript:</p>
<ul>
<li><p><code>addEventListener('input', ...)</code>: This listens to every single keystroke. Every time you type, it instantly recalculates the words, characters, and sentences.</p>
</li>
<li><p><code>convertCase(event, type)</code>: This function takes the selected style (like <code>upper</code> or <code>sentence</code>) and applies Regular Expressions (Regex) or array mapping to format the string. It also dynamically adds the <code>.active</code> CSS class to the specific button you clicked.</p>
</li>
<li><p><code>document.execCommand('copy')</code>: This is a browser command that copies the selected text directly to the user's clipboard.</p>
</li>
<li><p><code>new Blob()</code>: You use a Blob (Binary Large Object) to construct a file out of the text on the fly. This allows users to download a <code>.doc</code> file without needing a backend server.</p>
</li>
</ul>
<h2 id="heading-step-5-test-your-tool">Step 5: Test Your Tool</h2>
<p>You're now ready to evaluate your code in a real browser environment.</p>
<ol>
<li><p>Open the <code>case-converter-app</code> folder on your computer.</p>
</li>
<li><p>Double-click the <code>index.html</code> file to launch the application.</p>
</li>
<li><p>Paste a long paragraph into the text area to verify that the live statistics update accurately.</p>
</li>
<li><p>Switch between the formatting options to observe the immediate DOM manipulation, and test the export buttons to ensure files are downloading correctly.</p>
</li>
</ol>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this tutorial, you successfully engineered a browser-based Case Converter Tool using vanilla JavaScript.</p>
<p>You learned how to handle continuous user inputs, manipulate string data using Regular Expressions, and trigger local file downloads directly from the front end.</p>
<p>Most importantly, you learned that modern web browsers are highly capable of handling complex document modifications locally, removing the strict need for external backend servers. This method guarantees fast processing speeds and keeps user data completely private.</p>
<p>For a live demonstration of these concepts in a production environment, feel free to test out this <a href="https://99tools.net/case-converter/">Case Converter</a> and experience how seamlessly these text transformations operate.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
