クライアントワークのデバッグ作業をスムーズにする「デバイス情報チェッカー」を作りました

クライアントワークのデバッグ作業をスムーズにする「デバイス情報チェッカー」を作りました

クライアントワークをしていると、「クライアント側でバグが発生しているのに、こちらでは再現できない」という状況に遭遇することは少なくないと思います。
そんな時、クライアントのデバイス環境を正確に把握することが、デバッグ作業にはとても重要になります。

これまでは、

「使っているブラウザは何ですか?」
「ブラウザーのバージョンは?」
「画面サイズは幾つでご覧になってますか?」

と質問ラリーを重ねていましたが、クライアント担当者は必ずしも技術的な知識があるわけではありません。

「どこ見ればいいの?」
「どうやって送ればいい?」
みたいな、新しいラリーを始めるきっかけにもなります。
説明するのは楽しいですが、必ずしも担当者さんの時間が空いている分けではありません。

そこで、URLを開いてボタンを押すだけで環境情報を送ってもらえるツールを作成しました。
冬休みの自由研究です。

まずは完成品

https://mifa.tokyo/tools/device-info

主な機能:

  • 画面解像度とビューポートサイズの表示
  • vw/vh単位の実際のピクセル値表示
  • 新しいビューポート単位(dvh/svh/lvh)のサポート
  • ブラウザ情報とデバイス情報の取得
  • 情報をテキストデータでコピーする機能
  • 表示情報をPNG画像として保存する機能

技術的な実装

1. 情報の整理と2カラムレイアウト

最初はタブ切り替えで実装しましたが、バグ確認という用途を考えるとすべての情報が一画面で見える方が使いやすいと判断し、2カラムレイアウトに変更しました。

<div class="info-grid">
    <div class="info-section">
        <h3 class="section-title">画面・レイアウト情報</h3>
        <!-- レスポンシブデザインに関する情報 -->
    </div>
    
    <div class="info-section">
        <h3 class="section-title">ブラウザ・デバイス情報</h3>
        <!-- 環境・互換性に関する情報 -->
    </div>
</div>

設計のポイント:

  • 左側:レイアウト崩れの原因特定に使う情報
  • 右側:互換性やバグの原因となる環境情報

2. vw/vhの実ピクセル値取得

CSSで50vwと書いても、実際に何ピクセルになるかは環境によって異なります。
レスポンシブデザインのデバッグでは、実際のピクセル値を知ることが重要です。

function getScreenInfo() {
    const vw = window.innerWidth / 100;
    const vh = window.innerHeight / 100;
    
    return {
        '1vw': `${vw.toFixed(2)}px`,
        '1vh': `${vh.toFixed(2)}px`,
        '100vw': `${window.innerWidth}px`,
        '100vh': `${window.innerHeight}px`
    };
}

これにより、「このデバイスでは1vw = 3.75px」といった具体的な値が分かります。

3. 新しいビューポート単位(dvh/svh/lvh)の取得

スマートフォンのアドレスバー問題を解決する(比較的)新しいCSS単位があります。
これらをサポートしているかどうか、実際の値はどうかを確認できるようにしました。

// dvh/svh/lvhのサポート確認
const testDiv = document.createElement('div');
testDiv.style.position = 'fixed';
testDiv.style.height = '100dvh';
document.body.appendChild(testDiv);

const dvhSupported = getComputedStyle(testDiv).height !== '0px';
if (dvhSupported) {
    const dvhValue = parseFloat(getComputedStyle(testDiv).height);
    result['100dvh'] = `${dvhValue.toFixed(2)}px`;
}

document.body.removeChild(testDiv);

各単位の違い:

  • 100vh:従来の単位(モバイルでアドレスバー問題あり)
  • 100dvh:動的に変化(アドレスバーの表示/非表示に追従)
  • 100svh:最小値(アドレスバー表示時の高さ)
  • 100lvh:最大値(アドレスバー非表示時の高さ)

4. ブラウザとデバイスの判定

User Agentから正確にブラウザを判定するのは意外と難しいですよね。
特に「Chromium」ベースのEdgeとChromeの区別に注意が必要です。

function getDeviceInfo() {
    const ua = navigator.userAgent;
    let browserName = 'Unknown';
    let browserVersion = 'Unknown';

    // Edgeの新旧判定
    if (ua.indexOf('Chrome') > -1 && ua.indexOf('Edge') === -1 && ua.indexOf('Edg') === -1) {
        browserName = 'Chrome';
        browserVersion = ua.match(/Chrome\/([0-9.]+)/)?.[1];
    } else if (ua.indexOf('Edg') > -1) {
        browserName = 'Edge';
        browserVersion = ua.match(/Edg\/([0-9.]+)/)?.[1];
    }
    // ... 他のブラウザも判定

    // デバイスタイプの判定
    let deviceType = 'Desktop';
    if (/Mobi|Android/i.test(ua)) {
        deviceType = 'Mobile';
    } else if (/Tablet|iPad/i.test(ua)) {
        deviceType = 'Tablet';
    }

    return { browserName, browserVersion, deviceType };
}

5. テキストコピー機能

Clipboard APIを使って、整形されたテキストをクリップボードにコピーする機能を実装します。
これで、キャプチャの仕方がわからなくても、デバイス情報は送ってもらえます。

function copyInfo() {
    const screenInfo = getScreenInfo();
    const deviceInfo = getDeviceInfo();
    
    let text = '【デバイス情報】\n\n';
    text += '■ 画面・レイアウト情報\n';
    Object.entries(screenInfo).forEach(([key, value]) => {
        text += `${key}: ${value}\n`;
    });
    
    const btn = event.target;
    const originalText = btn.textContent;
    
    navigator.clipboard.writeText(text).then(() => {
        btn.textContent = 'コピーしました!';
        btn.style.background = '#48bb78';
        setTimeout(() => {
            btn.textContent = originalText;
            btn.style.background = '#2c3e50';
        }, 2000);
    });
}

ボタン押下時の処理:

  • 成功時:ボタンが緑色になり「コピーしました!」と表示
  • 失敗時:赤色になり「コピー失敗」と表示
  • 2秒後に自動で元に戻る

6. 画像保存機能

html2canvasライブラリを使って、表示中の情報をPNG画像として保存できます。
画像化ができれば、コピーしたテキストが崩れてみにくくなったり、ということはなくなりますね。

function saveAsImage() {
    const element = document.getElementById('info-display');
    const btn = event.target;
    
    btn.textContent = '処理中...';
    btn.disabled = true;
    
    html2canvas(element, {
        backgroundColor: '#ffffff',
        scale: 2
    }).then(canvas => {
        const link = document.createElement('a');
        const timestamp = new Date().toISOString().slice(0, 19).replace(/:/g, '-');
        link.download = `device-info_${timestamp}.png`;
        link.href = canvas.toDataURL('image/png');
        link.click();
        
        btn.textContent = '保存しました!';
        btn.style.background = '#48bb78';
    });
}

7. リアルタイム更新

画面サイズの変更やオンライン状態の変化を監視して、自動で情報を更新します。

// 画面サイズ変更の監視(デバウンス処理)
window.addEventListener('resize', () => {
    setTimeout(updateAllInfo, 100);
});

// オンライン/オフライン状態の監視
window.addEventListener('online', updateAllInfo);
window.addEventListener('offline', updateAllInfo);

実際の使用例

  1. クライアントに「このURLを開いてください」と伝える
  2. クライアントが「テキストをコピー」または「画像として保存」ボタンを押す
  3. Slackやメールで送ってもらう
  4. こちらで環境を確認してバグ再現

これまで何度も質問を重ねていた作業が、ワンクリックで完結します。

まとめ

このツールを作ったことで、

  • クライアントとのやり取りが大幅に効率化
  • 正確な環境情報を取得できるので、原因特定まで時間短縮

というメリットがありました。
引き続き利用してもらい、改善できる点は都度回収していこうと思います。

参考リンク