ساخت ابزار یادداشت‌برداری سریع با ذخیره خودکار در مرورگر و HTML تک‌فایلی

آموزش ساخت یک ابزار یادداشت‌برداری سریع، مینیمال و آفلاین با قابلیت ذخیره خودکار در مرورگر (LocalStorage)، کپی با یک کلیک و شمارشگر کلمات در یک فایل HTML.

🎧 خلاصه صوتی مقاله (پادکست ۲ دقیقه‌ای):

فرصت مطالعه ندارید؟ چکیده این مطلب را همین‌جا گوش دهید:

ابزارهای یادداشت‌برداری آنلاین زیادی در وب وجود دارند، اما بیشتر آن‌ها نیازمند ساخت حساب کاربری، اتصال دائمی به اینترنت یا پرداخت هزینه‌های اشتراک هستند. اگر برنامه‌نویس، دانشجو یا فردی هستید که مدام نیاز دارید متن‌ها، کدهای کوتاه یا ایده‌های خود را موقتاً یادداشت کنید، داشتن یک دفترچه یادداشت شخصی، سبک و کاملاً آفلاین روی مرورگر بهترین راه‌حل است. در این مقاله قصد داریم ساخت یک ابزار یادداشت‌برداری سریع، مینیمال و زیبا را یاد بگیریم که تمام کدهای آن (HTML، CSS و JavaScript) تنها در یک فایل منفرد قرار دارند و با بستن مرورگر یا قطع اینترنت، هیچ‌گاه اطلاعات شما را از دست نمی‌دهند.

برای افرادی که به ابزارهای کاربردی متکی بر مرورگر علاقه دارند، پیش‌تر در مقاله‌ای دیگر به معرفی افزونه ColorZilla؛ قطره‌چکان و پالت‌ساز حرفه‌ای رنگ در مرورگر پرداخته بودیم که تجربه‌ی کاربری وب را بهبود می‌بخشد. پروژه‌ای که امروز می‌سازیم دقیقاً با همین روحیه سادگی و کارایی طراحی شده است.

چرا ساخت یک ابزار یادداشت‌برداری محلی (LocalStorage) اهمیت دارد؟

بسیاری از مواقع نیازی به نرم‌افزارهای سنگین یا سرویس‌های ابری پیچیده برای چند خط یادداشت موقت نداریم. ویژگی اصلی این پروژه وابستگی صفر به سرور، دیتابیس یا کتابخانه‌های خارجی است. تمام پردازش‌ها روی سیستم خود کاربر و داخل حافظه مرورگر انجام می‌شود. این رویکرد چند مزیت کلیدی دارد:

  • حریم خصوصی کامل: هیچ متنی به هیچ سروری ارسال نمی‌شود و تمام داده‌ها در حافظه مرورگر دستگاه شما باقی می‌ماند.
  • سرعت فوق‌العاده بالا: به دلیل حجم بسیار کم فایل و عدم بارگذاری اسکریپت‌های سنگین خارجی، ابزار در کسری از ثانیه اجرا می‌شود.
  • اجرای کاملاً آفلاین: پس از ذخیره فایل روی سیستم، بدون نیاز به اتصال اینترنت می‌توانید در هر لحظه از آن استفاده کنید.
مشخصات فنی ابزار یادداشت‌برداری سریع

ویژگیتوضیحات فنی
تکنولوژی‌های ساختHTML5, CSS3, Vanilla JavaScript
محل ذخیره‌سازی دادهحافظه محلی مرورگر (localStorage)
وابستگی خارجیندارد (۱۰۰٪ مستقل و آفلاین)
قابلیت‌های کلیدیذخیره خودکار، کپی سریع، شمارشگر کلمات، حالت واکنش‌گرا
وضعیت دسترسی برای ایرانبدون محدودیت (کاملاً آزاد)

کد کامل و یک‌پارچه ابزار یادداشت‌برداری

برای استفاده از این ابزار، کافی است کد زیر را داخل یک فایل متنی با پسوند .html (مثلاً notes.html) کپی کنید و آن را با هر مرورگر وب دلخواهی (کروم، فایرفاکس، اج و غیره) باز کنید. طراحی این صفحه کاملاً واکنش‌گرا (Responsive) بوده و در گوشی‌های هوشمند و رایانه‌های شخصی به شکلی زیبا نمایش داده می‌شود.

<!DOCTYPE html>
<html lang="fa" dir="rtl">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>دفترچه یادداشت سریع و هوشمند</title>
    <style>
        :root {
            --bg-color: #0f172a;
            --card-bg: #1e293b;
            --text-color: #f8fafc;
            --accent-color: #38bdf8;
            --accent-hover: #0ea5e9;
            --danger-color: #ef4444;
            --border-color: #334155;
        }

        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
            font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
        }

        body {
            background-color: var(--bg-color);
            color: var(--text-color);
            min-height: 100vh;
            display: flex;
            flex-direction: column;
            align-items: center;
            padding: 2rem 1rem;
        }

        .container {
            width: 100%;
            max-width: 800px;
            background-color: var(--card-bg);
            border-radius: 12px;
            box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.3);
            border: 1px solid var(--border-color);
            display: flex;
            flex-direction: column;
            overflow: hidden;
        }

        header {
            padding: 1.5rem;
            border-bottom: 1px solid var(--border-color);
            display: flex;
            justify-content: space-between;
            align-items: center;
            flex-wrap: wrap;
            gap: 1rem;
        }

        h1 {
            font-size: 1.25rem;
            font-weight: 700;
            color: var(--accent-color);
        }

        .stats {
            font-size: 0.875rem;
            color: #94a3b8;
        }

        .editor-area {
            padding: 1.5rem;
            flex-grow: 1;
            display: flex;
            flex-direction: column;
        }

        textarea {
            width: 100%;
            height: 350px;
            background-color: transparent;
            color: var(--text-color);
            border: 1px solid var(--border-color);
            border-radius: 8px;
            padding: 1rem;
            font-size: 1rem;
            line-height: 1.6;
            resize: vertical;
            outline: none;
            transition: border-color 0.2s;
        }

        textarea:focus {
            border-color: var(--accent-color);
        }

        .actions {
            padding: 1rem 1.5rem;
            background-color: rgba(15, 23, 42, 0.5);
            border-top: 1px solid var(--border-color);
            display: flex;
            justify-content: space-between;
            align-items: center;
            flex-wrap: wrap;
            gap: 1rem;
        }

        .btn-group {
            display: flex;
            gap: 0.75rem;
        }

        button {
            padding: 0.625rem 1.25rem;
            border: none;
            border-radius: 6px;
            font-size: 0.875rem;
            font-weight: 600;
            cursor: pointer;
            transition: background-color 0.2s, transform 0.1s;
        }

        button:active {
            transform: scale(0.98);
        }

        .btn-primary {
            background-color: var(--accent-color);
            color: #0f172a;
        }

        .btn-primary:hover {
            background-color: var(--accent-hover);
        }

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

        .btn-danger:hover {
            background-color: rgba(239, 68, 68, 0.1);
        }

        .status-msg {
            font-size: 0.875rem;
            color: #10b981;
            opacity: 0;
            transition: opacity 0.3s ease;
        }

        .status-msg.show {
            opacity: 1;
        }

        @media (max-width: 600px) {
            body {
                padding: 1rem 0.5rem;
            }
            .actions {
                flex-direction: column;
                align-items: stretch;
            }
            .btn-group {
                justify-content: space-between;
            }
        }
    </style>
</head>
<body>

    <div class="container">
        <header>
            <h1>دفترچه یادداشت سریع</h1>
            <div class="stats" id="statsDisplay">کلمات: 0 | کاراکترها: 0</div>
        </header>

        <div class="editor-area">
            <textarea id="noteInput" placeholder="متن خود را اینجا بنویسید... یادداشت‌ها به صورت خودکار ذخیره می‌شوند."></textarea>
        </div>

        <div class="actions">
            <div class="status-msg" id="statusMsg">متن با موفقیت کپی شد!</div>
            <div class="btn-group">
                <button class="btn-danger" id="clearBtn">پاک کردن همه</button>
                <button class="btn-primary" id="copyBtn">کپی متن</button>
            </div>
        </div>
    </div>

    <script>
        const noteInput = document.getElementById('noteInput');
        const statsDisplay = document.getElementById('statsDisplay');
        const copyBtn = document.getElementById('copyBtn');
        const clearBtn = document.getElementById('clearBtn');
        const statusMsg = document.getElementById('statusMsg');

        // بارگذاری یادداشت ذخیره شده قبلی هنگام باز شدن صفحه
        window.addEventListener('DOMContentLoaded', () => {
            const savedNote = localStorage.getItem('quick_note_content');
            if (savedNote !== null) {
                noteInput.value = savedNote;
                updateStats(savedNote);
            }
        });

        // ذخیره خودکار و به‌روزرسانی آمار به هنگام تایپ کاربر
        noteInput.addEventListener('input', () => {
            const content = noteInput.value;
            localStorage.setItem('quick_note_content', content);
            updateStats(content);
        });

        // تابع محاسبه تعداد کلمات و کاراکترها
        function updateStats(text) {
            const characters = text.length;
            // فیلتر کردن رشته‌های خالی برای شمارش دقیق کلمات فارسی و انگلیسی
            const words = text.trim() === '' ? 0 : text.trim().split(/\s+/).length;
            statsDisplay.textContent = `کلمات: ${words} | کاراکترها: ${characters}`;
        }

        // قابلیت کپی متن با یک کلیک
        copyBtn.addEventListener('click', () => {
            if (noteInput.value.trim() === '') return;
            
            navigator.clipboard.writeText(noteInput.value).then(() => {
                statusMsg.classList.add('show');
                setTimeout(() => {
                    statusMsg.classList.remove('show');
                }, 2000);
            }).catch(err => {
                console.error('خطا در کپی کردن متن: ', err);
            });
        });

        // دکمه پاک کردن کل صفحه با تأیید کاربر
        clearBtn.addEventListener('click', () => {
            if (confirm('آیا مطمئن هستید که می‌خواهید تمام یادداشت را پاک کنید؟')) {
                noteInput.value = '';
                localStorage.removeItem('quick_note_content');
                updateStats('');
            }
        });
    </script>
</body>
</html>

بررسی بخش‌به‌بخش کد و نحوه کارکرد آن

برای اینکه درک درستی از ساختار این پروژه داشته باشید، اجازه دهید بخش‌های مختلف آن را بررسی کنیم:

۱. ساختار ظاهری (CSS و متغیرها)

در بخش بالای کدهای استایل، از متغیرهای CSS (:root) استفاده شده است تا تغییر رنگ‌بندی کلی برنامه بسیار ساده باشد. رنگ پس‌زمینه تیره (Dark Mode) انتخاب شده تا در استفاده طولانی‌مدت چشم کاربر خسته نشود. همچنین با استفاده از ویژگی flexbox، چیدمان هدر، کادر متن و نوار دکمه‌ها در تمام ابعاد صفحه (از موبایل‌های کوچک تا مانیتورهای بزرگ) به شکلی منظم قرار می‌گیرند.

۲. مکانیزم ذخیره‌سازی خودکار (LocalStorage)

قلب تپنده‌ی این ابزار رویداد input در جاوا اسکریپت است. هر زمان که کاربر کلیدی را در کادر متن بفشارد یا متنی را جای‌گذاری (Paste) کند، این رویداد فعال شده و محتوای جدید مستقیماً با کلید quick_note_content در حافظه localStorage مرورگر ذخیره می‌شود. به محض بستن و مجدد باز کردن مرورگر، رویداد DOMContentLoaded مقدار ذخیره‌شده را از حافظه خوانده و در کادر قرار می‌دهد.

۳. شمارشگر هوشمند کلمات و کاراکترها

تابع updateStats مسئولیت محاسبه تعداد کاراکترها و کلمات را بر عهده دارد. تعداد کاراکترها با استفاده از ویژگی طول رشته (length) به دست می‌آید. برای شمارش کلمات، متن با استفاده از عبارت منظم /\s+/ بر اساس فاصله‌های خالی و خطوط جدید تفکیک می‌شود تا شمارش کلمات هم برای متون فارسی و هم انگلیسی به درستی انجام گیرد.

۴. کپی با یک کلیک و پیام تأیید

با استفاده از رابط مدرن navigator.clipboard.writeText، متن موجود در کادر با فشردن دکمه «کپی متن» مستقیماً به حافظه کلیپ‌بورد سیستم منتقل می‌شود. پس از انجام کپی، یک پیام کوتاه سبز رنگ با کلاس show ظاهر شده و بعد از ۲ ثانیه به طور خودکار محو می‌شود تا کاربر از موفقیت عملیات مطمئن شود.

اشتباهات رایج و نکات پنهان در توسعه ابزارهای لوکال‌استوریج

هنگام کار با حافظه محلی مرورگر (LocalStorage) چند نکته مهم وجود دارد که باید به آن‌ها توجه داشته باشید:

  • محدودیت حجم حافظه: حافظه LocalStorage معمولاً حدود ۵ مگابایت فضا در اختیار هر وب‌سایت قرار می‌دهد. این فضا برای میلیون‌ها کاراکتر متنی کافی است، اما نباید از آن برای ذخیره‌سازی فایل‌های حجیم یا تصاویر استفاده کنید.
  • حالت مرور ناشناس (Incognito/Private): در حالت مرور ناشناس برخی مرورگرها، ممکن است داده‌های LocalStorage پس از بستن آخرین پنجره به کلی پاک شوند. بنابراین برای یادداشت‌های بلندمدت همیشگی، از حالت عادی مرورگر استفاده کنید.
  • پاک کردن داده‌های مرورگر: اگر کاربر تاریخچه مرورگر یا داده‌های سایت‌ها (Browsing Data) را به صورت دستی پاک کند، این یادداشت‌ها نیز پاک خواهند شد. اگر یادداشت بسیار مهمی دارید، حتماً پیش از پاکسازی کش مرورگر آن را به جای دیگری منتقل کنید.

اگر علاقه‌مند به یادگیری ابزارهای پیشرفته‌تر فرانت‌اند یا حل مسائل منطقی برنامه‌نویسی هستید، پیشنهاد می‌کنیم مطالعه‌ی پروژه‌های آموزشی دیگر را در برنامه‌ی خود قرار دهید.

پرسش‌های متداول

آیا برای اجرای این فایل نیاز به نصب نرم‌افزار خاصی است؟

خیر. این ابزار به صورت کامل در یک فایل HTML نوشته شده است. تنها چیزی که نیاز دارید یک مرورگر وب مدرن (مثل کروم، فایرفاکس، ادج یا سافاری) است تا فایل را باز کنید.

آیا اطلاعات نوشته شده با بستن مرورگر پاک می‌شوند؟

خیر. به لطف استفاده از تکنولوژی LocalStorage، تمام متن‌های تایپ شده به صورت دائمی در حافظه مرورگر شما روی سیستم ذخیره می‌شوند و حتی با بستن کامل مرورگر یا ریستارت سیستم از بین نمی‌روند.

آیا این ابزار نیازی به اینترنت دارد؟

خیر. این اسکریپت هیچ‌گونه وابستگی به کتابخانه‌ها، فونت‌ها یا سرورهای خارجی ندارد و به صورت ۱۰۰ درصد آفلاین روی سیستم شما اجرا می‌شود.

حداکثر چقدر متن می‌توانم در این دفترچه یادداشت ذخیره کنم؟

حافظه LocalStorage مرورگرها حدود ۵ مگابایت فضا در اختیار شما می‌گذارد که معادل میلیون‌ها کاراکتر متنی است و برای هزاران صفحه یادداشت کاملاً کافی است.

جمع‌بندی

در این مقاله یاد گرفتیم که چگونه تنها با نوشتن چند خط کد ساده در قالب یک فایل HTML، CSS و JavaScript، یک ابزار یادداشت‌برداری سریع، کاملاً رایگان، شخصی و آفلاین بسازیم. استفاده از حافظه محلی مرورگر باعث می‌شود بدون نیاز به پایگاه داده یا سرویس‌های ابری پیچیده، به اطلاعات خود دسترسی داشته باشید. این پروژه یک تمرین عالی برای برنامه‌نویسان مبتدی است تا با نحوه‌ی تعامل جاوا اسکریپت با حافظه مرورگر و رویدادها آشنا شوند.

پاسخی بگذارید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *