1. はじめに
ドットインストールのパスワードジェネレーター作成レッスンを通じて、JavaScriptによるDOM操作・イベント処理・ランダム生成を一通り学びました。
レッスン動画では「数字・記号にチェックを入れても確率次第で含まれないことがある」という仕様のままでしたが、必ず1文字は含まれるように自分でアレンジ実装してみました。この記事では、そのコードを中心に要点を整理していきます。
2. 実装の全体像
まずは今回実装したコード全体を確認します。各処理の詳細は以降のセクションで整理します。
<!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>
<main>
<p id="result"></p>
<button id="btn">パスワードを生成</button>
<fieldset>
<legend>オプション</legend>
<label>
長さ(<span id="password-length">8</span>)
<input type="range" id="slider" min="8" max="24" value="8">
</label>
<label>
数字
<input type="checkbox" id="numbers-checkbox">
</label>
<label>
記号
<input type="checkbox" id="symbols-checkbox">
</label>
</fieldset>
</main>
<script src="main.js"></script>
</body>
</html>
"use strict";
{
function showPassword() {
const result = document.getElementById("result");
const numbersCheckbox = document.getElementById("numbers-checkbox");
const symbolsCheckbox = document.getElementById("symbols-checkbox");
const letters = "abcdefghijklmnopqrstuvwxyz";
const numbers = "0123456789";
const symbols = "!#$%&()";
let password = "";
let checkCount = 0;
let seed = letters + letters.toUpperCase(); // 大文字 + 小文字の52文字
// 数字チェックボックスがONの場合、seedに追加し1文字確定で入れる
if (numbersCheckbox.checked) {
seed += numbers;
password += numbers[Math.floor(Math.random() * numbers.length)];
checkCount++;
}
// 記号チェックボックスがONの場合、seedに追加し1文字確定で入れる
if (symbolsCheckbox.checked) {
seed += symbols;
password += symbols[Math.floor(Math.random() * symbols.length)];
checkCount++;
}
// 確定文字数を除いた残り文字をランダムに補充
for (let i = 0; i < slider.value - checkCount; i++) {
password += seed[Math.floor(Math.random() * seed.length)];
}
// Fisher-Yatesシャッフルで文字の並び順をランダムにする
password = password.split("");
for (let i = 0; i < slider.value; i++) {
let randomIndex = Math.floor(Math.random() * slider.value);
let temp = password[i];
password[i] = password[randomIndex];
password[randomIndex] = temp;
}
result.textContent = password.join("");
}
const slider = document.getElementById("slider");
const btn = document.getElementById("btn");
// スライダー操作時:長さの数値表示のみ更新
slider.addEventListener("input", () => {
const passwordLength = document.getElementById("password-length");
passwordLength.textContent = slider.value;
});
// ボタンクリック時:パスワード生成
btn.addEventListener("click", () => {
showPassword();
});
showPassword(); // ページ読み込み時に初期表示
}
3. 各処理の整理
コードの主要な処理を4つのセクションに分けて整理します。それぞれの役割と実装の意図を順に確認していきます。
3.1 seedの組み立てとチェック必須1文字保証
パスワードの元になる文字列(seed)の組み立てと、チェックボックスに応じた処理が今回の核心部分です。
seed はベースとなるアルファベット大小文字52文字からスタートし、チェックが入っていれば数字・記号を追加していく設計と理解しました。
let seed = letters + letters.toUpperCase(); // 大文字 + 小文字の52文字
let password = "";
let checkCount = 0;
if (numbersCheckbox.checked) {
seed += numbers; // seedに数字を追加
password += numbers[Math.floor(Math.random() * numbers.length)]; // 数字を1文字確定で入れる
checkCount++; // 確定文字数をカウント
}
checkCount は「確定で入れた文字数」を管理するための変数です。後続のループで残り文字数を計算するために使います。
動画の元の実装では seed からランダムに選ぶだけのため、数字が選ばれない可能性がありました。今回はチェックが入ったタイミングで先に1文字 password へ追加することで、必ず含まれるように変更しています。
3.2 残り文字数の補充
確定した文字の分を差し引いた残り文字数をループで補充しています。
slider.value から checkCount を引いた分だけ、seed全体からランダムに文字を選ぶ仕組みです。
for (let i = 0; i < slider.value - checkCount; i++) {
password += seed[Math.floor(Math.random() * seed.length)];
}
slider.value は文字列として取得されます。今回は引き算の中で使っているため暗黙的に数値に変換されますが、明示的に Number(slider.value) や parseInt() で変換する書き方もあるようです。
3.3 Fisher-Yatesシャッフル
チェックボックスで確定した文字を先頭に追加した場合、そのままでは「先頭は必ず数字・記号」という偏りが生じます。そこでシャッフル処理を追加しました。
password = password.split(""); // 文字列を1文字ずつの配列に変換
for (let i = 0; i < slider.value; i++) {
let randomIndex = Math.floor(Math.random() * slider.value); // ランダムな位置を選ぶ
let temp = password[i]; // 現在の文字を一時退避
password[i] = password[randomIndex]; // ランダム位置の文字と入れ替え
password[randomIndex] = temp; // 一時退避した文字をランダム位置へ
}
result.textContent = password.join(""); // 配列を文字列に戻して表示
これは Fisher-Yatesシャッフル と呼ばれる手法のようです。各要素を順番にランダムな位置の要素と交換していくことで、配列全体を均等にシャッフルできると理解しました。
split("") で文字列を1文字ずつの配列に分解し、シャッフル後に join("") で再度文字列に戻しています。
3.4 イベントリスナーと関数の呼び出し
パスワード生成処理を showPassword() として関数化することで、ボタンクリック時・ページ読み込み時の両方から呼び出せるようにしています。
btn.addEventListener("click", () => {
showPassword(); // ボタンクリックで生成
});
showPassword(); // ページ読み込み時にも初期表示
スライダーの input イベントはパスワードの再生成ではなく、長さの数値表示の更新のみを担当しています。スライダーを動かすだけでは再生成されない設計です。
まとめ
今回のパスワードジェネレーター実装を通じて整理できた内容は以下の通りです。
-
getElementByIdでDOM要素を取得し、textContentで値を書き換える基本的な流れを理解しました - チェックボックスの
checkedプロパティで状態を判定し、seedと確定文字の両方を動的に組み立てる設計を体験しました -
checkCountを使って「確定文字数の分だけループを減らす」という考え方は、ロジックを組み立てる上で参考になりました - Fisher-Yatesシャッフルで配列をランダムに並び替える手法を初めて使いました。
splitとjoinの組み合わせも今後使えそうです
動画をそのまま写経するだけでなく、仕様の不満点を自分でアレンジしてみることで、理解が深まったように感じています。
