PDFをダウンロードさせずにブラウザ上でプレビューさせたいとき、モーダルウィンドウ内に表示するUIは効果的です。しかし iframeがモバイルで動作しない問題やBootstrap 4と5のAPI差異など、つまずきポイントも多い実装です。本記事ではiframeの基本から、PDF.jsを使った高機能ビューア、モバイル対応まで体系的に解説します。
- iframeでPDFをBootstrap 5モーダルに表示する基本実装
- Bootstrap 4との違い(data-dismiss / data-bs-dismiss)
- 複数のPDFリンクから共通モーダルを使う実装
- モーダルを閉じたときにiframeをリセットする方法
- iframeが効かないモバイルへの対応策(Google Drive・PDF.js)
- PDF.jsで独自ビューアをモーダルに組み込む方法
- ダウンロードボタン付きのPDFプレビューUI
iframeでPDFをモーダルに表示する基本実装(Bootstrap 5)
Bootstrap 5を使ったシンプルな実装です。ボタンをクリックするとモーダルが開き、iframeにPDFが表示されます。
必要なライブラリの読み込み
<!-- Bootstrap 5 CSS --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"> <!-- jQuery --> <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script> <!-- Bootstrap 5 JS(Popperバンドル版) --> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
モーダルのHTML構造
<!-- 表示ボタン -->
<button type="button"
class="btn btn-primary"
data-bs-toggle="modal"
data-bs-target="#pdfModal"
data-pdf-url="/files/sample.pdf">
PDFをプレビュー
</button>
<!-- モーダル -->
<div class="modal fade" id="pdfModal" tabindex="-1" aria-labelledby="pdfModalLabel" aria-hidden="true">
<div class="modal-dialog modal-xl modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="pdfModalLabel">PDFプレビュー</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="閉じる"></button>
</div>
<div class="modal-body p-0">
<iframe id="pdf-iframe" src="" width="100%" height="600" style="border:none;"></iframe>
</div>
</div>
</div>
</div>
jQueryでiframeのsrcを制御する
$(function () {
var $modal = $('#pdfModal');
var $iframe = $('#pdf-iframe');
// ボタンクリックでURLをiframeにセット
$('[data-bs-toggle="modal"][data-pdf-url]').on('click', function () {
var url = $(this).data('pdf-url');
$iframe.attr('src', url);
});
// モーダルを閉じたらiframeをリセット(読み込みを止める)
$modal[0].addEventListener('hidden.bs.modal', function () {
$iframe.attr('src', '');
});
});
iframeのsrcをリセットしないと、モーダルを閉じた後もバックグラウンドでPDFの読み込みや描画が続き、パフォーマンスに影響します。
hidden.bs.modal イベントはモーダルが完全に非表示になった後に発火するため、アニメーション中に src が空になって表示が崩れることもありません。Bootstrap 4との違いと対応方法
Bootstrap 4から5に移行したプロジェクト、または古いテンプレートを使っている場合はAPIの差異に注意してください。
| 項目 | Bootstrap 4 | Bootstrap 5 |
|---|---|---|
| モーダル表示ボタン属性 | data-toggle=”modal” | data-bs-toggle=”modal” |
| 閉じるボタン属性 | data-dismiss=”modal” | data-bs-dismiss=”modal” |
| jQuery依存 | 必須 | 不要(独立して動作) |
| 非表示イベント名 | hidden.bs.modal | hidden.bs.modal(同じ) |
| JSでモーダル操作 | $(“#id”).modal(“show”) | new bootstrap.Modal(el).show() |
Bootstrap 4版のjQueryコード
// Bootstrap 4版
$(function () {
$('#showPdfBtn').on('click', function () {
var url = $(this).data('pdf-url');
$('#pdf-iframe').attr('src', url);
$('#pdfModal').modal('show'); // Bootstrap 4のjQuery API
});
$('#pdfModal').on('hidden.bs.modal', function () {
$('#pdf-iframe').attr('src', '');
});
});
Bootstrap 5では
$("#id").modal("show") のjQuery連携APIが廃止されました。jQueryで制御する必要がある場合は new bootstrap.Modal(document.getElementById("id")).show() を使ってください。複数のPDFリンクから共通モーダルを使う
ページ内に複数のPDFリンクがある場合、モーダルを1つ共有してそれぞれのPDFを表示するパターンです。
<!-- 複数のPDFリンク -->
<a href="#" class="pdf-link" data-pdf-url="/files/report-2024.pdf" data-pdf-title="2024年度 年次報告書">2024年度 年次報告書</a>
<a href="#" class="pdf-link" data-pdf-url="/files/catalog.pdf" data-pdf-title="製品カタログ">製品カタログ</a>
<a href="#" class="pdf-link" data-pdf-url="/files/manual.pdf" data-pdf-title="取扱説明書">取扱説明書</a>
<!-- 共通モーダル -->
<div class="modal fade" id="pdfModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-xl modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="pdf-modal-title"></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="閉じる"></button>
</div>
<div class="modal-body p-0">
<iframe id="pdf-iframe" src="" width="100%" height="600" style="border:none;"></iframe>
</div>
</div>
</div>
</div>
$(function () {
var modal = new bootstrap.Modal(document.getElementById('pdfModal'));
$('.pdf-link').on('click', function (e) {
e.preventDefault();
var url = $(this).data('pdf-url');
var title = $(this).data('pdf-title');
$('#pdf-iframe').attr('src', url);
$('#pdf-modal-title').text(title);
modal.show();
});
document.getElementById('pdfModal').addEventListener('hidden.bs.modal', function () {
$('#pdf-iframe').attr('src', '');
});
});
モバイル対応:iframeが効かない場合の対処法
iOS Safariの多くのバージョンでは <iframe> 内のPDFが表示されず、ダウンロードが促されます。モバイルユーザーに対応するには代替手段が必要です。
Google Driveビューアを使う方法
// PDFのURLをGoogle Driveビューア経由に変換
function toGoogleViewerUrl(pdfUrl) {
var encoded = encodeURIComponent(pdfUrl);
return 'https://docs.google.com/viewer?embedded=true&url=' + encoded;
}
$(function () {
$('.pdf-link').on('click', function (e) {
e.preventDefault();
var rawUrl = $(this).data('pdf-url');
var viewUrl = toGoogleViewerUrl(rawUrl);
$('#pdf-iframe').attr('src', viewUrl);
new bootstrap.Modal(document.getElementById('pdfModal')).show();
});
});
- PDFのURLは公開アクセス可能なURLである必要があります(localhost不可)
- Google Driveビューアの利用はGoogleの利用規約に従います
- 大きなPDFはGoogle側でのレンダリングに時間がかかることがあります
デバイス判定で挙動を切り替える方法
$(function () {
$('.pdf-link').on('click', function (e) {
e.preventDefault();
var url = $(this).data('pdf-url');
// iOSかどうかを判定
var isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
if (isIOS) {
// iOS: 新しいタブで直接開く(ダウンロードを促す代わりにプレビュー可能)
window.open(url, '_blank');
} else {
// それ以外: モーダルで表示
$('#pdf-iframe').attr('src', url);
new bootstrap.Modal(document.getElementById('pdfModal')).show();
}
});
});
PDF.jsを使った高機能ビューア
Mozilla製のオープンソースPDFビューア PDF.js を使うと、iframeの代わりに独自のPDFレンダリングエンジンを使えるためクロスブラウザ・モバイル対応が向上します。
PDF.jsビューアURLを使う方法(最も簡単)
<!-- PDF.jsのビューアURLを iframe の src に指定する --> <!-- CDN: https://mozilla.github.io/pdf.js/web/viewer.html?file=YOUR_PDF_URL --> <iframe id="pdfjs-iframe" src="" width="100%" height="600" style="border:none;"> </iframe>
$(function () {
var PDFJS_VIEWER = 'https://mozilla.github.io/pdf.js/web/viewer.html?file=';
$('.pdf-link').on('click', function (e) {
e.preventDefault();
// 絶対URLに変換(PDF.jsビューアはフルURLが必要)
var pdfUrl = location.origin + $(this).data('pdf-url');
var viewUrl = PDFJS_VIEWER + encodeURIComponent(pdfUrl);
$('#pdfjs-iframe').attr('src', viewUrl);
new bootstrap.Modal(document.getElementById('pdfModal')).show();
});
});
- ページサムネイル・テキスト検索・印刷・フルスクリーンなどが標準装備
- モバイルでもページスクロールが滑らか(iframeより優れる)
- CDN版はサードパーティ制限でCORSエラーになることがあるため、本番環境ではPDF.jsをダウンロードして自サーバーに配置することを推奨
ダウンロードボタン付きPDFプレビューUI
モーダルフッターにダウンロードボタンを設置して、「プレビューを確認してからダウンロード」というUXを提供します。
<div class="modal fade" id="pdfModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-xl modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="pdf-modal-title"></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="閉じる"></button>
</div>
<div class="modal-body p-0">
<iframe id="pdf-iframe" src="" width="100%" height="560" style="border:none;"></iframe>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">閉じる</button>
<a id="pdf-download-btn" href="#" download class="btn btn-primary">ダウンロード</a>
</div>
</div>
</div>
</div>
$(function () {
$('.pdf-link').on('click', function (e) {
e.preventDefault();
var url = $(this).data('pdf-url');
var title = $(this).data('pdf-title') || 'PDFプレビュー';
$('#pdf-iframe').attr('src', url);
$('#pdf-modal-title').text(title);
$('#pdf-download-btn').attr('href', url); // ダウンロードボタンにも同じURLをセット
new bootstrap.Modal(document.getElementById('pdfModal')).show();
});
document.getElementById('pdfModal').addEventListener('hidden.bs.modal', function () {
$('#pdf-iframe').attr('src', '');
});
});
実装方法の比較と選び方
| 方法 | モバイル対応 | 外部依存 | 機能 | 向いている用途 |
|---|---|---|---|---|
| iframe(直接) | △(iOS非対応) | なし | 最小限 | PC向け・社内ツール |
| Google Driveビューア | ○ | Google依存 | 基本的 | 公開PDFのプレビュー |
| PDF.jsビューア | ◎ | PDF.jsのみ | 高機能 | 汎用・モバイル重視 |
まとめ
PDF をモーダルで表示する際は、まず対象ユーザーがモバイルを使うかどうかを確認してください。PC専用の社内ツールならiframe直接が最もシンプルです。モバイル対応が必要な場合はPDF.jsを検討してください。
関連記事: 画像をクリックすると拡大表示する方法 / modal-video.jsでYouTube動画をモーダル表示する方法 / ボタンクリックで要素の表示・非表示を切り替える方法
よくある質問(FAQ)
hidden.bs.modal イベントで $('#pdf-iframe').attr('src', '') を呼んでいるか確認してください。srcを空文字に設定することでブラウザがiframeの読み込みを中止します。X-Frame-Options: DENY または SAMEORIGIN を返している場合はiframeでの表示が拒否されます。自分のサーバーのPDFであれば、Content-Security-PolicyとX-Frame-Optionsの設定を確認してください。height: calc(100vh - 200px) に設定すると、ビューポートの高さからヘッダー・フッターの分を引いた高さに自動調整されます。モーダル自体に modal-dialog-scrollable クラスを付けるとモーダル内でスクロールも可能です。position:fixed; top:0; left:0; width:100%; height:100%; の背景レイヤーとiframeを含むコンテンツボックスを配置し、jQueryでshow/hideを制御します。Bootstrapの依存を避けたい軽量なページに有効です。