2025/11/27 / 技術メモ

ポートフォリオの詳細表示をモーダル化してUXを改善する

ポートフォリオサイトの作品詳細を、ページ遷移なしで閲覧できるようにモーダルウィンドウ化しました。DOMParserを使ったHTML取得や、実装時のハマりポイントについて。

javascript ui-ux

こんにちは!パン君です。

これまでこのサイトの「ポートフォリオ一覧」から作品をクリックすると詳細ページ(portfolio/xxxx.html)に遷移する仕様になっていました。

しかしポートフォリオを見に来てくれた人は「 いろいろな作品をザッピングしたい 」はずです。
「クリックして詳細へ移動」→「ブラウザバックで戻る」→「またスクロールして次の作品へ...」という操作は、地味ながらストレスになります。

そこで 一覧ページを維持したまま、詳細情報をモーダルウィンドウで重ねて表示する 形に改修しました。


実装の方針

このサイトはビルドツールを使ってMarkdownファイルから静的なHTMLファイルを生成しています。
ポートフォリオの詳細ページもすでに portfolio/work_01.html のような形で実ファイルとして存在しています。

JSONデータにすべての情報を詰め込んでJSで構築する方法もありますが、
「すでに生成されている詳細ページのHTMLを再利用する」 方が、SEO的にもビルドフロー的にも無駄がありません。

処理の流れ

  1. ユーザーがカードをクリック。
  2. JSで詳細ページのURL (portfolio/xxxx.html) を fetch する。
  3. 取得したHTMLテキストをパースして、中身のコンテンツ部分だけを取り出す。
  4. モーダルの中にポイッと入れる。

技術的なポイント

1. HTMLをfetchしてパースする

fetch で取得できるのは単なる「文字列」です。これをDOM要素として扱うために DOMParser を使います。

Javascript
// HTMLを取得
const res = await fetch(contentPath);
const text = await res.text();

// 文字列をHTMLドキュメントとしてパース
const parser = new DOMParser();
const doc = parser.parseFromString(text, "text/html");

// 必要な部分(.work-detail)だけを抜き出す
const detail = doc.querySelector(".work-detail");

// モーダルに挿入
modalContent.appendChild(detail);

これで、iframe を使わずにシームレスにコンテンツを埋め込むことができます。

2. 相対パスの罠

詳細ページは portfolio/ ディレクトリ配下にある前提でビルドされているため、画像などのパスが ../assets/img/... のようになっています。
これをトップページ(ルート階層)のモーダルでそのまま表示すると、リンク切れになってしまいます。

そこで、取り出したDOMに対してパスの書き換え処理を行いました。

Javascript
// "../" で始まるパスを修正する
function fixPaths(element) {
  const nodes = element.querySelectorAll('[src], [href]');
  nodes.forEach((node) => {
    // 属性値を取得して "../" を削除するなど置換処理
    // ...
  });
}

ハマったポイント:中身が表示されない?

実装していざ動かしてみるとモーダルは開くのに 中身が真っ白 という現象が発生しました。
開発者ツールで見ると要素は存在しているのに、画面には映っていません。

原因:スクロールアニメーション

原因は、詳細ページ側で使っていた「スクロールしたらふわっと表示する」ためのクラス reveal-on-scroll でした。

CSS
.reveal-on-scroll {
    opacity: 0; /* 初期状態は透明 */
    transform: translateY(20px);
    /* スクロールして画面内に入ったら .is-visible が付いて不透明になる */
}

詳細ページとして表示するときはスクロール検知スクリプトが動くので問題ないのですが、
モーダル内に挿入されたときはそのスクリプトが動かず、 永遠に opacity: 0 のまま待機 してしまっていたのです。

解決策

モーダルに挿入する前にこのクラスを削除するようにしました。

Javascript
const detail = doc.querySelector(".work-detail");
// アニメーション用クラスを削除して強制表示
detail.classList.remove("reveal-on-scroll");

これで無事に表示されるようになりました。


まとめ

今回の改修で作品の閲覧体験(UX)はかなり向上したと思います。

  • ページ遷移がないのでサクサク見れる。
  • 一覧のスクロール位置が維持されるので、続きから探しやすい。
  • 詳細ページへの直接リンク(SEO)も維持されている。

「静的サイトだから」と諦めず、JavaScriptをうまく使うことで、動的サイトのようなリッチな挙動を実現できました。

← 自作Discord Bot…← ブログ一覧へ戻る静的サイトにお問い合わせフ… →