jQuery
【コピペOK】jQueryでログインフォームを作る - バリデーション・Ajax送信完全ガイド
カービー
#jQuery#ログイン#フォーム#JavaScript#Ajax
jQueryを使ったログインフォームの作り方を徹底解説。入力バリデーション、Ajax送信、ローディング表示、エラーハンドリングまで実践的なサンプルコード付き。
目次
基本的なログインフォームの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キー対応など |
セキュリティ上の注意点
- パスワードは平文で送信しない - 必ずHTTPS通信を使用
- CSRF対策 - トークンを含める
- レート制限 - サーバーサイドでも実装
- 入力のサニタイズ - XSS対策
関連記事
jQueryを使ったログインフォームの実装、ぜひ参考にしてください。