1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【第7回】板が増えても一瞬で見つかる!カテゴリ絞り込みフィルター&改行対応のローカルルール表示

1
Posted at

「板のルール説明文を丁寧に書いたのに、改行がすべて消滅して1行の巨大テキストに潰れてしまった」
令和に自作した2ちゃんねる風掲示板「つゔぁいちゃんねる(2ch.biz)」開発秘話の第7弾。今回は、板が数十個規模にスケールした際の管理画面の操作性改善(即時フィルター)と、2ch文化の象徴である「ローカルルール(板説明文)」を安全に複数行表示するためのフロントエンド設計について解説します。


💡 はじめに:板が増えると何が起きるか?

掲示板を育てていくと、初期の数個から「ニュース」「実況」「雑談」「ゲーム」「アニメ」「IT」「政治」「料理」…と、あっという間に板の数が30個、50個と増えていきます。

板が増えること自体は活気の証ですが、開発者・管理者としては新たな課題に直面します。

  1. 管理画面の「スクロール地獄」
    管理画面の板一覧テーブルが縦に何千ピクセルも伸びてしまい、特定の板を探して設定を変更するだけで激しいストレスに。
  2. 板の「ローカルルール」が読めない
    板ごとに「sage進行推奨」「ネタバレ厳禁」「AA禁止」などのルール(説明文)を改行入りで設定したのに、画面に表示すると 改行がすべて消えて1行の長文テキストに潰れてしまう 。

今回は、この2つの「Webアプリ開発で地味ながら誰もが絶対に踏む壁」をスマートに解決した記録です。


🔍 1. サーバー負荷ゼロ!クライアント側カテゴリ絞り込みフィルター

板が増えたからといって、検索や絞り込みのたびにサーバーへ GET /api.php?category=... と通信していては無駄なネットワーク遅延が発生します。
すでに管理画面を開いた時点で全板のデータはブラウザ(メモリ内)に読み込まれているため、 完全クライアントサイドで完結するゼロ遅延フィルター を構築しました。

<!-- admin.html の板管理ツールバー -->
<div class="table-toolbar">
    <div class="filter-group">
        <label for="boardCategoryFilter">🏷️ カテゴリ絞り込み:</label>
        <select id="boardCategoryFilter" class="form-control-inline">
            <option value="">すべてのカテゴリを表示</option>
            <!-- 登録済みカテゴリが動的に生成される -->
        </select>
    </div>
    <button id="btnNewBoardModal" class="btn btn-primary">+ 新規板を作成</button>
</div>

イベントハンドラーの実装(admin.js 抜粋)

DOMの再取得を行わず、各行に持たせた data-category 属性、またはメモリ上の配列をフィルタリングして即座にテーブルを再描画します。

// カテゴリフィルターの変更イベント
document.getElementById('boardCategoryFilter').addEventListener('change', (e) => {
    const selectedCategory = e.target.value;
    filterAndRenderBoards(selectedCategory);
});

function filterAndRenderBoards(categoryFilter = '') {
    const tbody = document.getElementById('boardListTableBody');
    tbody.innerHTML = '';

    // メモリ上の全板データから条件に一致するものを抽出
    const filteredBoards = categoryFilter === ''
        ? allLoadedBoards
        : allLoadedBoards.filter(b => b.category === categoryFilter);

    if (filteredBoards.length === 0) {
        tbody.innerHTML = `<tr><td colspan="6" class="text-center text-muted">該当する板がありません</td></tr>`;
        return;
    }

    // テーブル行の描画
    filteredBoards.forEach(board => {
        tbody.appendChild(createBoardTableRow(board));
    });
}

第5回・第6回で構築した「カテゴリ管理」とも連携しており、カテゴリを追加したり並べ替えた際にも、このドロップダウンの選択肢が自動で最新の並び順に同期されるリアクティブな構造に仕上げました。


📝 2. 「改行消失問題」とXSS脆弱性のジレンマ

板の作成フォームには、以下のようなルールを入力していました。

【当板のローカルルール】
・政治・宗教の話題は禁止です。
・荒らしは完全スルー(あぼ〜ん)推奨。
・次スレは>>950が立ててください。

しかし、これを保存して板トップ画面(#current-board-desc)やホーム画面の板カードに表示させると、ブラウザ上ではこうなってしまいました。

【当板のローカルルール】 ・政治・宗教の話題は禁止です。 ・荒らしは完全スルー(あぼ〜ん)推奨。 ・次スレは>>950が立ててください。

なぜ改行が消えるのか?

HTMLの仕様上、ソースコード内の通常の改行(\n)はブラウザによって「半角スペース1個」として扱われるためです。

やってはいけない「安易な解決策」:nl2br() + innerHTML

初心者がやりがちなのが、サーバー側で改行を <br> に変換して、フロント側で innerHTML にそのまま流し込む方法です。

// ❌ 危険!XSS(クロスサイトスクリプティング)の温床
element.innerHTML = board.description.replace(/\n/g, '<br>');

もし管理者のアカウントが侵害されたり、外部から不正なスクリプトタグ(<script>alert(document.cookie)</script>)を含む説明文が保存された場合、ユーザーのブラウザ上で悪意あるスクリプトが即座に実行されてしまいます。


🛡️ 安全かつエレガントな解決策:CSS white-space: pre-wrap

テキストをHTMLとして解釈させる必要はありません。 テキストは安全なプレーンテキスト(textContent)のまま流し込み、見た目の改行制御をCSSに任せるのが現代Webの正解 です。

/* style.css */

/* 板画面ヘッダーのローカルルール表示 */
#current-board-desc {
    white-space: pre-wrap;      /* \n をそのまま改行として描画 */
    word-break: break-word;     /* 画面幅を超えた長い英単語の自動折り返し */
    line-height: 1.6;           /* 読みやすい行間 */
    font-size: 0.9rem;
    color: var(--text-muted);
    background-color: var(--bg-card);
    padding: 12px 16px;
    border-radius: 6px;
    border-left: 4px solid var(--accent-color); /* 2ch風のワンポイントアクセント */
}

/* トップページの板一覧カードの説明文 */
.board-card-desc {
    white-space: pre-wrap;
    word-break: break-word;
    font-size: 0.82rem;
    color: var(--text-secondary);
}
// 安全な描画コード(app.js)
const descElement = document.getElementById('current-board-desc');
descElement.textContent = board.description; // ✅ XSSの危険性ゼロ!

white-space: pre-wrap の絶大なメリット

  1. 100%のXSS安全性 : HTMLタグとして解釈されないため、どんな文字列が入ってきても無害化される。
  2. 文字コードや特殊文字の破壊なし : サロゲートペア(絵文字)や記号、スペースの連続もそのまま綺麗に保持される。
  3. レスポンシブな自動折り返し : pre だと画面を突き破って横スクロールしてしまいますが、pre-wrap ならスマホの狭い画面でも枠内に自動で折り返してくれる。

🎯 まとめと次回予告

第7回となる今回は、掲示板の日常運用を支える 「カテゴリ絞り込みフィルター」 と、2ch独自のローカルルール文化を美しく安全に再現する 「改行表示対応」 について解説しました。

今回の学び・ポイント

  • クライアント側でのゼロ遅延フィルタリング :すでに取得済みのデータはフロントエンドで絞り込むのが最速かつ省サーバーリソース。
  • 改行表示を innerHTML で解決しない :セキュリティリスクを冒して <br> を埋め込むのではなく、CSSの white-space: pre-wrap を活用するのがベストプラクティス。
  • 板ごとのアイデンティティ(ローカルルール)の確立 :読みやすい注意書き表示が、コミュニティの治安維持の第一歩になる。

板が整理され、ルールも明記できるようになりました。
しかし、いざ書き込みが始まると、どうしてもルールを破る「悪質な荒らし投稿者」が現れます。

「この投稿はどこから書き込まれたのか?どのプロバイダなのか?」

次回、 【第8回】荒らし投稿を特定せよ!管理コンソールからの投稿IPアドレス照会&ホスト逆引き機能 へと続きます!


🔗 関連リンク

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?