jQuery

【コピペOK】jQueryでログインフォームを作る - バリデーション・Ajax送信完全ガイド

カービー
【コピペOK】jQueryでログインフォームを作る - バリデーション・Ajax送信完全ガイド
#jQuery#ログイン#フォーム#JavaScript#Ajax

jQueryを使ったログインフォームの作り方を徹底解説。入力バリデーション、Ajax送信、ローディング表示、エラーハンドリングまで実践的なサンプルコード付き。

目次

  1. 基本的なログインフォームのHTML
  2. CSSでスタイリング
  3. jQueryでバリデーション
  4. Ajax送信処理
  5. 完成版サンプルコード
  6. 応用テクニック
  7. まとめ

基本的なログインフォームのHTML

まずはシンプルなHTMLから始めましょう。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ログイン</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="login-container">
        <div class="login-box">
            <h1>ログイン</h1>

            <form id="loginForm">
                <div class="form-group">
                    <label for="email">メールアドレス</label>
                    <input type="email" id="email" name="email" placeholder="example@mail.com">
                    <span class="error-message" id="emailError"></span>
                </div>

                <div class="form-group">
                    <label for="password">パスワード</label>
                    <input type="password" id="password" name="password" placeholder="パスワードを入力">
                    <span class="error-message" id="passwordError"></span>
                </div>

                <div class="form-options">
                    <label class="remember-me">
                        <input type="checkbox" id="remember" name="remember">
                        <span>ログイン状態を保持</span>
                    </label>
                    <a href="#" class="forgot-password">パスワードを忘れた方</a>
                </div>

                <button type="submit" id="loginBtn" class="login-btn">
                    <span class="btn-text">ログイン</span>
                    <span class="btn-loading" style="display: none;">
                        <span class="spinner"></span>
                        送信中...
                    </span>
                </button>
            </form>

            <div class="login-footer">
                <p>アカウントをお持ちでない方は<a href="#">新規登録</a></p>
            </div>
        </div>
    </div>

    <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
    <script src="login.js"></script>
</body>
</html>

ポイント解説

  • エラーメッセージ用のspan: 各入力欄の下にエラー表示用の要素を配置
  • ローディング表示: ボタン内に送信中の表示を用意
  • jQueryの読み込み: CDNから最新版を読み込み

CSSでスタイリング

見た目を整えるCSSです。

/* リセットと基本設定 */
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: 'Segoe UI', 'Hiragino Sans', sans-serif;
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
}

/* ログインコンテナ */
.login-container {
    width: 100%;
    max-width: 400px;
    padding: 20px;
}

.login-box {
    background: #fff;
    border-radius: 16px;
    padding: 40px;
    box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
}

.login-box h1 {
    text-align: center;
    color: #333;
    margin-bottom: 30px;
    font-size: 28px;
}

/* フォームグループ */
.form-group {
    margin-bottom: 20px;
}

.form-group label {
    display: block;
    margin-bottom: 8px;
    color: #555;
    font-weight: 500;
}

.form-group input[type="email"],
.form-group input[type="password"] {
    width: 100%;
    padding: 14px 16px;
    border: 2px solid #e0e0e0;
    border-radius: 8px;
    font-size: 16px;
    transition: all 0.3s ease;
}

.form-group input:focus {
    outline: none;
    border-color: #667eea;
    box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.2);
}

/* エラー状態 */
.form-group input.error {
    border-color: #ff4757;
    background-color: #fff5f5;
}

.form-group input.success {
    border-color: #2ed573;
}

.error-message {
    display: block;
    color: #ff4757;
    font-size: 13px;
    margin-top: 6px;
    min-height: 20px;
}

/* オプション */
.form-options {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 24px;
}

.remember-me {
    display: flex;
    align-items: center;
    cursor: pointer;
    color: #666;
    font-size: 14px;
}

.remember-me input {
    margin-right: 8px;
}

.forgot-password {
    color: #667eea;
    text-decoration: none;
    font-size: 14px;
}

.forgot-password:hover {
    text-decoration: underline;
}

/* ログインボタン */
.login-btn {
    width: 100%;
    padding: 16px;
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    border: none;
    border-radius: 8px;
    color: #fff;
    font-size: 16px;
    font-weight: 600;
    cursor: pointer;
    transition: all 0.3s ease;
}

.login-btn:hover:not(:disabled) {
    transform: translateY(-2px);
    box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
}

.login-btn:disabled {
    opacity: 0.7;
    cursor: not-allowed;
}

/* ローディングスピナー */
.spinner {
    display: inline-block;
    width: 16px;
    height: 16px;
    border: 2px solid #fff;
    border-top-color: transparent;
    border-radius: 50%;
    animation: spin 0.8s linear infinite;
    margin-right: 8px;
    vertical-align: middle;
}

@keyframes spin {
    to { transform: rotate(360deg); }
}

/* フッター */
.login-footer {
    text-align: center;
    margin-top: 24px;
    color: #666;
    font-size: 14px;
}

.login-footer a {
    color: #667eea;
    text-decoration: none;
    font-weight: 500;
}

.login-footer a:hover {
    text-decoration: underline;
}

jQueryでバリデーション

入力値のチェックを実装します。

バリデーション関数

// メールアドレスのバリデーション
function validateEmail(email) {
    const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    return regex.test(email);
}

// パスワードのバリデーション
function validatePassword(password) {
    return password.length >= 6;
}

// フィールドのエラー表示
function showError($input, message) {
    $input.addClass('error').removeClass('success');
    $input.siblings('.error-message').text(message);
}

// フィールドの成功表示
function showSuccess($input) {
    $input.addClass('success').removeClass('error');
    $input.siblings('.error-message').text('');
}

// エラークリア
function clearError($input) {
    $input.removeClass('error success');
    $input.siblings('.error-message').text('');
}

リアルタイムバリデーション

$(function() {
    // メールアドレスのバリデーション
    $('#email').on('blur', function() {
        const $this = $(this);
        const value = $this.val().trim();

        if (value === '') {
            showError($this, 'メールアドレスを入力してください');
        } else if (!validateEmail(value)) {
            showError($this, '正しいメールアドレスを入力してください');
        } else {
            showSuccess($this);
        }
    });

    // パスワードのバリデーション
    $('#password').on('blur', function() {
        const $this = $(this);
        const value = $this.val();

        if (value === '') {
            showError($this, 'パスワードを入力してください');
        } else if (!validatePassword(value)) {
            showError($this, 'パスワードは6文字以上で入力してください');
        } else {
            showSuccess($this);
        }
    });

    // フォーカス時にエラーをクリア
    $('input').on('focus', function() {
        clearError($(this));
    });
});

Ajax送信処理

フォームの送信処理を実装します。

$(function() {
    $('#loginForm').on('submit', function(e) {
        e.preventDefault();

        // 各フィールドの値を取得
        const email = $('#email').val().trim();
        const password = $('#password').val();
        const remember = $('#remember').prop('checked');

        // バリデーション
        let isValid = true;

        if (email === '') {
            showError($('#email'), 'メールアドレスを入力してください');
            isValid = false;
        } else if (!validateEmail(email)) {
            showError($('#email'), '正しいメールアドレスを入力してください');
            isValid = false;
        }

        if (password === '') {
            showError($('#password'), 'パスワードを入力してください');
            isValid = false;
        } else if (!validatePassword(password)) {
            showError($('#password'), 'パスワードは6文字以上で入力してください');
            isValid = false;
        }

        if (!isValid) {
            return;
        }

        // ローディング表示
        const $btn = $('#loginBtn');
        $btn.prop('disabled', true);
        $btn.find('.btn-text').hide();
        $btn.find('.btn-loading').show();

        // Ajax送信
        $.ajax({
            url: '/api/login',
            type: 'POST',
            contentType: 'application/json',
            data: JSON.stringify({
                email: email,
                password: password,
                remember: remember
            })
        })
        .done(function(response) {
            // ログイン成功
            alert('ログイン成功!');
            window.location.href = '/dashboard';
        })
        .fail(function(xhr) {
            // エラーハンドリング
            const status = xhr.status;
            let message = 'ログインに失敗しました';

            if (status === 401) {
                message = 'メールアドレスまたはパスワードが正しくありません';
            } else if (status === 429) {
                message = 'ログイン試行回数が多すぎます。しばらく待ってから再試行してください';
            } else if (status === 500) {
                message = 'サーバーエラーが発生しました';
            }

            alert(message);
        })
        .always(function() {
            // ローディング解除
            $btn.prop('disabled', false);
            $btn.find('.btn-text').show();
            $btn.find('.btn-loading').hide();
        });
    });
});

完成版サンプルコード

すべてを統合した完成版のJavaScriptです。

// login.js

$(function() {
    // ========================================
    // バリデーション関数
    // ========================================

    function validateEmail(email) {
        const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
        return regex.test(email);
    }

    function validatePassword(password) {
        return password.length >= 6;
    }

    function showError($input, message) {
        $input.addClass('error').removeClass('success');
        $input.siblings('.error-message').text(message);
    }

    function showSuccess($input) {
        $input.addClass('success').removeClass('error');
        $input.siblings('.error-message').text('');
    }

    function clearError($input) {
        $input.removeClass('error success');
        $input.siblings('.error-message').text('');
    }

    // ========================================
    // リアルタイムバリデーション
    // ========================================

    $('#email').on('blur', function() {
        const $this = $(this);
        const value = $this.val().trim();

        if (value === '') {
            showError($this, 'メールアドレスを入力してください');
        } else if (!validateEmail(value)) {
            showError($this, '正しいメールアドレスを入力してください');
        } else {
            showSuccess($this);
        }
    });

    $('#password').on('blur', function() {
        const $this = $(this);
        const value = $this.val();

        if (value === '') {
            showError($this, 'パスワードを入力してください');
        } else if (!validatePassword(value)) {
            showError($this, 'パスワードは6文字以上で入力してください');
        } else {
            showSuccess($this);
        }
    });

    $('input').on('focus', function() {
        clearError($(this));
    });

    // ========================================
    // フォーム送信
    // ========================================

    $('#loginForm').on('submit', function(e) {
        e.preventDefault();

        const email = $('#email').val().trim();
        const password = $('#password').val();
        const remember = $('#remember').prop('checked');

        // バリデーション
        let isValid = true;

        if (email === '') {
            showError($('#email'), 'メールアドレスを入力してください');
            isValid = false;
        } else if (!validateEmail(email)) {
            showError($('#email'), '正しいメールアドレスを入力してください');
            isValid = false;
        }

        if (password === '') {
            showError($('#password'), 'パスワードを入力してください');
            isValid = false;
        } else if (!validatePassword(password)) {
            showError($('#password'), 'パスワードは6文字以上で入力してください');
            isValid = false;
        }

        if (!isValid) return;

        // ローディング表示
        const $btn = $('#loginBtn');
        $btn.prop('disabled', true);
        $btn.find('.btn-text').hide();
        $btn.find('.btn-loading').show();

        // Ajax送信
        $.ajax({
            url: '/api/login',
            type: 'POST',
            contentType: 'application/json',
            data: JSON.stringify({
                email: email,
                password: password,
                remember: remember
            })
        })
        .done(function(response) {
            alert('ログイン成功!');
            window.location.href = response.redirectUrl || '/dashboard';
        })
        .fail(function(xhr) {
            const status = xhr.status;
            let message = 'ログインに失敗しました';

            switch (status) {
                case 400:
                    message = '入力内容に問題があります';
                    break;
                case 401:
                    message = 'メールアドレスまたはパスワードが正しくありません';
                    break;
                case 403:
                    message = 'アカウントがロックされています';
                    break;
                case 429:
                    message = 'ログイン試行回数が多すぎます';
                    break;
                case 500:
                    message = 'サーバーエラーが発生しました';
                    break;
            }

            alert(message);
        })
        .always(function() {
            $btn.prop('disabled', false);
            $btn.find('.btn-text').show();
            $btn.find('.btn-loading').hide();
        });
    });

    // ========================================
    // Enterキーでのフォーカス移動
    // ========================================

    $('#email').on('keypress', function(e) {
        if (e.which === 13) {
            e.preventDefault();
            $('#password').focus();
        }
    });
});

応用テクニック

パスワード表示切り替え

<div class="form-group password-field">
    <label for="password">パスワード</label>
    <div class="password-wrapper">
        <input type="password" id="password" name="password">
        <button type="button" class="toggle-password">
            <span class="show-icon">👁</span>
            <span class="hide-icon" style="display:none">🔒</span>
        </button>
    </div>
</div>
$('.toggle-password').on('click', function() {
    const $input = $(this).siblings('input');
    const type = $input.attr('type');

    if (type === 'password') {
        $input.attr('type', 'text');
        $(this).find('.show-icon').hide();
        $(this).find('.hide-icon').show();
    } else {
        $input.attr('type', 'password');
        $(this).find('.show-icon').show();
        $(this).find('.hide-icon').hide();
    }
});

ソーシャルログインボタン

<div class="social-login">
    <p>または</p>
    <button type="button" class="social-btn google">
        Googleでログイン
    </button>
    <button type="button" class="social-btn github">
        GitHubでログイン
    </button>
</div>
$('.social-btn.google').on('click', function() {
    window.location.href = '/auth/google';
});

$('.social-btn.github').on('click', function() {
    window.location.href = '/auth/github';
});

ログイン試行回数の制限(フロントエンド)

let loginAttempts = 0;
const MAX_ATTEMPTS = 5;
const LOCKOUT_TIME = 60000; // 1分

$('#loginForm').on('submit', function(e) {
    if (loginAttempts >= MAX_ATTEMPTS) {
        e.preventDefault();
        alert('ログイン試行回数が上限に達しました。1分後に再試行してください。');
        return;
    }

    // ... 通常の処理
});

// 失敗時にカウントアップ
.fail(function(xhr) {
    if (xhr.status === 401) {
        loginAttempts++;

        if (loginAttempts >= MAX_ATTEMPTS) {
            setTimeout(function() {
                loginAttempts = 0;
            }, LOCKOUT_TIME);
        }
    }
});

まとめ

ログインフォーム実装のポイント

項目 説明
バリデーション リアルタイム+送信時の二重チェック
エラー表示 各フィールドに個別のメッセージを表示
ローディング 送信中はボタンを無効化+視覚的フィードバック
エラーハンドリング HTTPステータスごとに適切なメッセージ
UX向上 パスワード表示切替、Enterキー対応など

セキュリティ上の注意点

  1. パスワードは平文で送信しない - 必ずHTTPS通信を使用
  2. CSRF対策 - トークンを含める
  3. レート制限 - サーバーサイドでも実装
  4. 入力のサニタイズ - XSS対策

関連記事

jQueryを使ったログインフォームの実装、ぜひ参考にしてください。