0
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?

【kintone】PDFをダウンロードせずに閲覧する

Last updated at Posted at 2025-08-20

PDF ViewerPDFプレビュープラグイン を使えば済むことだが

  • ブラウザの拡張機能は、端末ごとにインストールする必要があり
    そもそも社内のセキュリティ規定のせいで使えない会社もある。
  • プラグインはアプリごとに設定しないといけない。
    ポータルとスペースには対応できない。

kintone全体のカスタマイズをすれば、そうした問題をクリアできる。

(() => {
  'use strict';

  const events = [
    'portal.show',
    'space.portal.show',
    'app.record.index.show',
    'app.report.show',
    'app.record.detail.show',
    'app.record.print.show'
  ];

  kintone.events.on(events, async (event) => {
    
    // PDFダウンロードリンクを処理する関数
    const processLinks = () => {
      // ファイルダウンロードリンクを全て取得
      const links = document.querySelectorAll('a[href*="/download.do"]');
      
      // 各リンクを個別に処理
      links.forEach((link) => {
        // 既に処理済のリンクはスキップ
        if (link.dataset.pdfPreviewBound) return;
        
        // ファイルがPDFかどうかを判定
        const textIsPdf = (link.textContent || '').trim().toLowerCase().endsWith('.pdf');
        const hrefIsPdf = (link.getAttribute('href') || '').toLowerCase().includes('.pdf');
        
        // PDFでない場合はスキップ
        if (!textIsPdf && !hrefIsPdf) return;
        
        // 処理済フラグの付与
        link.dataset.pdfPreviewBound = 'true';
        
        // リンクがクリックされたときの処理
        link.addEventListener('click', async (e) => {
          e.preventDefault(); // デフォルトのクリックイベントを抑止
          e.stopPropagation(); // イベント伝播を停止
          
          // ローディング表示用の要素を作成
          const loadingDiv = document.createElement('div');
          // ローディング表示のスタイル設定
          loadingDiv.style.cssText = `
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: rgba(0, 0, 0, 0.8);
            color: white;
            padding: 20px;
            border-radius: 5px;
            z-index: 9999;
            font-family: sans-serif;
          `;
          loadingDiv.textContent = 'PDFを読み込み中...';
          document.body.appendChild(loadingDiv);
          
          // PDFをBlobとして取得
          const resp = await fetch(link.href, { credentials: 'include' });
          const blob = await resp.blob();

          // PDFのファイル名を取得
          const linkText = (link.textContent || '').trim();
          const fileName = linkText.endsWith('.pdf') ? 
            linkText : (linkText || 'document.pdf');
          
          // BlobからオブジェクトURLを作成
          const blobUrl = URL.createObjectURL(blob);
          
          // ローディング表示を削除
          document.body.removeChild(loadingDiv);

          /* PDFビューア画面を作成開始 */
          // 画面全体を覆うオーバーレイの要素を作成
          const overlay = document.createElement('div');
          overlay.id = 'pdf-viewer-overlay';
          // フルスクリーン表示のためのスタイル設定
          overlay.style.cssText = `
            position: fixed;
            top: 0;
            left: 0;
            width: 100vw;
            height: 100vh;
            background: rgba(0, 0, 0, 0.9);
            z-index: 10000;
            display: flex;
            flex-direction: column;
          `;
          
          // PDFを表示するためのiframe要素を作成
          const iframe = document.createElement('iframe');
          iframe.src = blobUrl;
          // ヘッダー以外の残り全体を使うようにiframeのスタイルを設定
          iframe.style.cssText = `
            flex: 1;
            border: none;
            background: white;
          `;
          
          // オーバーレイに要素を追加
          overlay.appendChild(iframe);
          // DOMにオーバーレイを追加して画面に表示
          document.body.appendChild(overlay);
          /* PDFビューア画面の作成終了 */
          
          // ブラウザバック対応のため現在のURLにハッシュを追加して履歴を作成
          history.pushState(null, '', window.location.href + '#pdf-viewer');

          // PDFビューアを閉じる関数
          const closePdfViewer = (fromPopstate = false) => {
            // オーバーレイがDOMに存在する場合のみ削除処理を実行
            if (document.body.contains(overlay)) {
              document.body.removeChild(overlay);
              URL.revokeObjectURL(blobUrl);
            }
            // 全てのイベントリスナーを削除してメモリリークを防止
            document.removeEventListener('keydown', escapeHandler);
            window.removeEventListener('popstate', popstateHandler);
            
            // ブラウザバックから呼ばれた場合は追加の履歴操作は不要
            if (!fromPopstate) {
              // 手動で閉じた場合は履歴を1つ戻す
              history.back();
            }
          };
          
          // PDFをダウンロードする処理
          const downloadHandler = () => {
            const link = document.createElement('a');
            link.href = blobUrl;
            link.download = fileName;
            link.style.display = 'none';
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
          };

          // ブラウザバック時にPDFビューアを閉じる処理
          const popstateHandler = () => {
            if (document.body.contains(overlay)) {
              closePdfViewer(true);
            }
          };
          
          // Escキーが押されたときにPDFビューアを閉じる処理
          const escapeHandler = (e) => {
            if (e.key === 'Escape') {
              e.preventDefault();
              e.stopPropagation();
              closePdfViewer();
            }
          };

          document.addEventListener('keydown', escapeHandler);
          window.addEventListener('popstate', popstateHandler);
          
          // キーボードイベントを確実に受け取るためオーバーレイにフォーカスを設定
          overlay.setAttribute('tabindex', '-1');
          overlay.focus();
        });
      });
    };
    
    // ページ読み込み時の初回リンク処理を実行
    processLinks();
    
    // DOM変更を監視してリンクの動的追加に対応
    if (!window.__pdfPreviewObserver__) {
      const observer = new MutationObserver(processLinks);
      observer.observe(document.body, { childList: true, subtree: true });
      window.__pdfPreviewObserver__ = observer;
    }

    return event;
  });
})();
0
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
0
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?