4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ChatGPTでBootstrapを使ったHTMLのモックを作ってみる

Last updated at Posted at 2024-03-06

画面のモックを作るツールはいろいろありますが、簡単にモックを作る方法としてChatGPTを利用する記事を見つけました。

参考 : ChatGPTを利用して画面モックを爆速で作成する - Taste of Tech Topics

「無料」で「インストールや環境構築不要」がよいので、無料版の「ChatGPT 3.5」を利用します。
また、モックツールを検索するとちょくちょく見かける「Bootstrap」も使ってみました。

ログイン画面

まずはシンプルに「Bootstrapを使ったログイン画面のhtmlを書いて」とGhatGPTに伝えるとこんな感じになりました。

a.gif

GhatGPTの出力結果

Bootstrapを使用して作成したログイン画面のHTMLの例を以下に示します。
このHTMLコードでは、Bootstrapライブラリを使用してログインフォームを作成しています。フォームの入力フィールドとボタンはBootstrapのスタイルが適用され、レスポンシブデザインにも対応しています。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>ログイン</title>
  <!-- Bootstrap CSS -->
  <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
  <style>
    body {
      background-color: #f4f4f4;
    }
    .login-form {
      max-width: 400px;
      margin: 0 auto;
      padding: 30px 20px;
      background-color: #fff;
      border-radius: 8px;
      box-shadow: 0 0 10px rgba(0,0,0,0.1);
      margin-top: 100px;
    }
    .login-form h2 {
      text-align: center;
      margin-bottom: 30px;
    }
    .login-form input[type="text"],
    .login-form input[type="password"] {
      width: 100%;
      padding: 10px;
      margin-bottom: 20px;
      border: 1px solid #ccc;
      border-radius: 4px;
    }
    .login-form button {
      width: 100%;
      padding: 10px;
      background-color: #007bff;
      border: none;
      border-radius: 4px;
      color: #fff;
      cursor: pointer;
    }
    .login-form button:hover {
      background-color: #0056b3;
    }
  </style>
</head>
<body>

<div class="container">
  <div class="row">
    <div class="col-md-6 offset-md-3">
      <div class="login-form">
        <h2>ログイン</h2>
        <form action="login.php" method="post">
          <div class="form-group">
            <label for="username">ユーザー名:</label>
            <input type="text" class="form-control" id="username" name="username" required>
          </div>
          <div class="form-group">
            <label for="password">パスワード:</label>
            <input type="password" class="form-control" id="password" name="password" required>
          </div>
          <button type="submit" class="btn btn-primary">ログイン</button>
        </form>
      </div>
    </div>
  </div>
</div>

<!-- Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@1.16.1/dist/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>

</body>
</html>

検索画面

まずは、「本を検索する画面のHTMLを作って」と頼むととてもシンプルな検索画面が出力されました。

a.gif

GhatGPTの出力結果

以下は、本を検索する画面のHTMLの例です。
このHTMLコードでは、Bootstrapを使用して本を検索するためのフォームが作成されています。入力フィールドと検索ボタンがあり、ユーザーがキーワードを入力して検索することができます。デザインはBootstrapのスタイルが適用されており、レスポンシブに対応しています。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>本を検索する</title>
  <!-- Bootstrap CSS -->
  <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
  <style>
    body {
      background-color: #f4f4f4;
    }
    .search-form {
      max-width: 600px;
      margin: 0 auto;
      padding: 30px 20px;
      background-color: #fff;
      border-radius: 8px;
      box-shadow: 0 0 10px rgba(0,0,0,0.1);
      margin-top: 50px;
    }
    .search-form h2 {
      text-align: center;
      margin-bottom: 30px;
    }
    .search-form input[type="text"] {
      width: 100%;
      padding: 10px;
      margin-bottom: 20px;
      border: 1px solid #ccc;
      border-radius: 4px;
    }
    .search-form button {
      width: 100%;
      padding: 10px;
      background-color: #007bff;
      border: none;
      border-radius: 4px;
      color: #fff;
      cursor: pointer;
    }
    .search-form button:hover {
      background-color: #0056b3;
    }
  </style>
</head>
<body>

<div class="container">
  <div class="row">
    <div class="col-md-8 offset-md-2">
      <div class="search-form">
        <h2>本を検索する</h2>
        <form action="search.php" method="get">
          <div class="form-group">
            <label for="keyword">キーワード:</label>
            <input type="text" class="form-control" id="keyword" name="keyword" required>
          </div>
          <button type="submit" class="btn btn-primary">検索</button>
        </form>
      </div>
    </div>
  </div>
</div>

<!-- Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@1.16.1/dist/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>

</body>
</html>

次に「検索はタイトルと著者名にして」と検索項目を指定するとこんな感じになりました。
a.gif

GhatGPTの出力結果
> 申し訳ありません。タイトルと著者名で検索するように修正したHTMLを以下に示します。 > このHTMLコードでは、タイトルと著者名の入力フィールドが追加され、それぞれ検索の条件となります。ユーザーはこれらのフィールドにキーワードを入力し、検索ボタンをクリックして検索を実行できます。
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>本を検索する</title>
  <!-- Bootstrap CSS -->
  <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
  <style>
    body {
      background-color: #f4f4f4;
    }
    .search-form {
      max-width: 600px;
      margin: 0 auto;
      padding: 30px 20px;
      background-color: #fff;
      border-radius: 8px;
      box-shadow: 0 0 10px rgba(0,0,0,0.1);
      margin-top: 50px;
    }
    .search-form h2 {
      text-align: center;
      margin-bottom: 30px;
    }
    .search-form input[type="text"] {
      width: 100%;
      padding: 10px;
      margin-bottom: 20px;
      border: 1px solid #ccc;
      border-radius: 4px;
    }
    .search-form button {
      width: 100%;
      padding: 10px;
      background-color: #007bff;
      border: none;
      border-radius: 4px;
      color: #fff;
      cursor: pointer;
    }
    .search-form button:hover {
      background-color: #0056b3;
    }
  </style>
</head>
<body>

<div class="container">
  <div class="row">
    <div class="col-md-8 offset-md-2">
      <div class="search-form">
        <h2>本を検索する</h2>
        <form action="search.php" method="get">
          <div class="form-group">
            <label for="title">タイトル:</label>
            <input type="text" class="form-control" id="title" name="title" required>
          </div>
          <div class="form-group">
            <label for="author">著者名:</label>
            <input type="text" class="form-control" id="author" name="author" required>
          </div>
          <button type="submit" class="btn btn-primary">検索</button>
        </form>
      </div>
    </div>
  </div>
</div>

<!-- Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@1.16.1/dist/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>

</body>
</html>

「検索項目は1つ以上入力」「国会図書館のAPIで検索」「検索結果を一覧表にする」などいろいろChatGPTに伝えて以下のような画面のHTMLを出力することができました。

a.gif

GhatGPTの出力結果
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>国会図書館検索</title>
  <!-- Bootstrap CSS -->
  <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>

<div class="container">
  <h1 class="mt-5">国会図書館検索</h1>

  <form id="searchForm" class="mt-4 mb-3" onsubmit="submitSearch(event);">
    <div class="form-row">
      <div class="col-md-4 mb-3">
        <label for="title">タイトル:</label>
        <input type="text" class="form-control" id="title" name="title">
      </div>
      <div class="col-md-4 mb-3">
        <label for="author">著者名:</label>
        <input type="text" class="form-control" id="author" name="author">
      </div>
    </div>
    <div class="form-row text-right">
      <div class="col-md-8 mb-3">
        <button type="submit" class="btn btn-primary">検索</button>
        <button type="button" class="btn btn-secondary ml-2" onclick="clearSearch()">クリア</button>
      </div>
    </div>
  </form>

  <div id="searchResults"></div>
</div>

<!-- Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@1.16.1/dist/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>

<script>
  function submitSearch(event) {
    event.preventDefault();

    var title = document.getElementById("title").value.trim();
    var author = document.getElementById("author").value.trim();

    // 検索条件のチェック
    if (title === "" && author === "") {
      alert("タイトルまたは著者名の少なくとも一つを入力してください。");
      return;
    }

    // 国会図書館のAPIエンドポイント
    var apiUrl = "https://ndlsearch.ndl.go.jp/api/opensearch";

    // 検索クエリの構築
    var query = "";
    if (title !== "") {
      query += "title=" + encodeURIComponent(title) + "&";
    }
    if (author !== "") {
      query += "creator=" + encodeURIComponent(author) + "&";
    }
    query += "count=10"; // 最大検索結果数を指定
    
    // APIリクエストを送信
    fetch(apiUrl + "?" + query)
      .then(response => response.text())
      .then(data => {
        // 検索結果を整形して表示
        var searchResults = document.getElementById("searchResults");
        searchResults.innerHTML = ""; // 一度中身をクリア

        // テーブル要素を作成
        var table = document.createElement("table");
        table.classList.add("table", "table-striped", "mt-3");

        // テーブルヘッダーを作成
        var thead = document.createElement("thead");
        thead.innerHTML = `
          <tr>
            <th>タイトル</th>
            <th>著者名</th>
            <th>出版日</th>
            <th>ISBN</th>
          </tr>
        `;
        table.appendChild(thead);

        // テーブルボディーを作成
        var tbody = document.createElement("tbody");

        // XMLデータをパース
        var parser = new DOMParser();
        var xmlDoc = parser.parseFromString(data, "text/xml");
        var items = xmlDoc.querySelectorAll("rss channel item");
        items.forEach(item => {
          var title = item.querySelector("title").textContent;
          var author = item.querySelector("author").textContent;
          var pubDate = item.querySelector("pubDate").textContent;

          // ISBNを取得
          var identifiers = item.querySelectorAll("identifier");
          var isbn = "";
          identifiers.forEach(identifier => {
            var xsiType = identifier.getAttribute("xsi:type");
            if (xsiType === "dcndl:ISBN") {
              isbn = identifier.textContent;
            }
          });

          var row = document.createElement("tr");
          var titleCell = document.createElement("td");
          titleCell.textContent = title;
          var authorCell = document.createElement("td");
          authorCell.textContent = author;
          var pubDateCell = document.createElement("td");
          pubDateCell.textContent = pubDate;
          var isbnCell = document.createElement("td");
          isbnCell.textContent = isbn;

          row.appendChild(titleCell);
          row.appendChild(authorCell);
          row.appendChild(pubDateCell);
          row.appendChild(isbnCell);

          tbody.appendChild(row);
        });
        table.appendChild(tbody);

        searchResults.appendChild(table);
      })
      .catch(error => {
        console.error("検索エラー:", error);
      });
  }

  function clearSearch() {
    document.getElementById("title").value = "";
    document.getElementById("author").value = "";
  }
</script>

</body>
</html>

ファイルアップロード画面

ここでは要件をまとめてChatGPTに伝えてみました。

伝えた要件
Bootstrapを使ってファイルアップロード画面をHTML出力して。

- ドラックアンドドロップでアップロードできるようにする
- アップロード後に「登録」ボタンでアップロード履歴に履歴を表示する
- 「登録」ボタンは右端に表示する
- 「登録」はファイルがアップロードされていない場合は非活性にする
- アップロード履歴を下に一覧で表示する
- アップロード履歴には「ファイル名」「アップロード日時」「状態」を表示する
- 状態には「アップロード中」「成功」「失敗」がある
- アップロード履歴にはサンプルデータを表示して
- アップロード欄には選択したファイル名を表示する
- 「登録」ボタンを押下したらアップロード欄のファイル名はクリアする
- アップロード履歴はアップロード日時の降順で表示する
- 「アップロード日時」の表示フォーマットは「YYYY-mm-dd HH:MM:DD」

a.gif

GhatGPTの出力結果

以下は、要件に合わせてBootstrapを使用してファイルアップロード画面をHTMLで出力するコードです。このコードでは、ドラッグアンドドロップでファイルをアップロードし、アップロード後に「登録」ボタンを押すことでアップロード履歴にファイルの情報が表示されます。登録ボタンは右端に表示され、ファイルがアップロードされていない場合は非活性になります。アップロード履歴には「ファイル名」「アップロード日時」「状態」が表示され、状態には「アップロード中」「成功」「失敗」があります。アップロード履歴にはサンプルデータが表示され、アップロード欄には選択したファイル名が表示されます。また、「登録」ボタンを押すとアップロード欄のファイル名がクリアされ、アップロード履歴はアップロード日時の降順で表示されます。アップロード日時の表示フォーマットは「YYYY-mm-dd HH:MM:SS」です。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>File Upload</title>
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
  <style>
    #file-upload {
      border: 2px dashed #aaa;
      border-radius: 10px;
      padding: 20px;
      cursor: pointer;
    }
    #register-btn {
      float: right;
    }
  </style>
</head>
<body>
  <div class="container mt-5">
    <h1>File Upload</h1>
    <div id="file-upload" class="mb-3">
      <label for="file-input" class="form-label">ドラッグアンドドロップまたはクリックしてファイルをアップロード</label>
      <input type="file" class="form-control-file" id="file-input" style="display: none;">
      <div id="file-name" class="mt-2"></div>
    </div>
    <button id="register-btn" class="btn btn-primary mb-3" disabled>登録</button>
    <div id="upload-history">
      <h2>アップロード履歴</h2>
      <table class="table">
        <thead>
          <tr>
            <th scope="col">ファイル名</th>
            <th scope="col">アップロード日時</th>
            <th scope="col">状態</th>
          </tr>
        </thead>
        <tbody id="upload-history-body">
          <tr>
            <td>sample_file.txt</td>
            <td>2024-03-06 10:30:00</td>
            <td>成功</td>
          </tr>
          <!-- Sample data row, replace with dynamic content -->
        </tbody>
      </table>
    </div>
  </div>

  <script>
    const fileInput = document.getElementById('file-input');
    const registerBtn = document.getElementById('register-btn');
    const fileNameDisplay = document.getElementById('file-name');
    const uploadHistoryBody = document.getElementById('upload-history-body');

    fileInput.addEventListener('change', function () {
      if (fileInput.files.length > 0) {
        registerBtn.disabled = false;
        const fileName = fileInput.files[0].name;
        fileNameDisplay.innerHTML = `選択したファイル: ${fileName}`;
      } else {
        registerBtn.disabled = true;
        fileNameDisplay.innerHTML = '';
      }
    });

    registerBtn.addEventListener('click', function () {
      const fileName = fileInput.files[0].name;
      const currentDate = new Date().toISOString().slice(0, 19).replace('T', ' ');
      const newRow = document.createElement('tr');
      newRow.innerHTML = `
        <td>${fileName}</td>
        <td>${currentDate}</td>
        <td>アップロード中</td>
      `;
      uploadHistoryBody.insertBefore(newRow, uploadHistoryBody.firstChild);
      fileNameDisplay.innerHTML = ''; // Clear file name display
      fileInput.value = ''; // Clear file input value
      registerBtn.disabled = true; // Disable register button
    });

    // ドラッグアンドドロップイベントの処理
    const fileUpload = document.getElementById('file-upload');

    ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
      fileUpload.addEventListener(eventName, preventDefaults, false);
    });

    function preventDefaults(e) {
      e.preventDefault();
      e.stopPropagation();
    }

    ['dragenter', 'dragover'].forEach(eventName => {
      fileUpload.addEventListener(eventName, highlight, false);
    });

    ['dragleave', 'drop'].forEach(eventName => {
      fileUpload.addEventListener(eventName, unhighlight, false);
    });

    function highlight() {
      fileUpload.classList.add('border-primary');
    }

    function unhighlight() {
      fileUpload.classList.remove('border-primary');
    }

    fileUpload.addEventListener('drop', handleDrop, false);

    function handleDrop(e) {
      const dt = e.dataTransfer;
      const files = dt.files;

      fileInput.files = files;

      const fileName = files[0].name;
      fileNameDisplay.innerHTML = `選択したファイル: ${fileName}`;

      const registerBtn = document.getElementById('register-btn');
      registerBtn.disabled = false;
    }
  </script>
</body>
</html>

ポイント

初めてChatGPTでモックのHTMLを作ってみて「ここがポイント」と思ったことは以下です。

  1. コードが長いと途中でChatGPTの出力が止まることがあるので、そんな時は「続きは?」と問いかけると続きが出力される
  2. モックの要件をまとめてからChatGPTに問いかける
    • 「検索画面」は「本を検索する画面のHTMLを作って」からひたすらChatGPTと会話して追加の要望を伝えて何度も何度もHTMLを出力しました。その過程で前に伝えたことが漏れたり、ずれたりしました。
    • なので、「要件をエディタなどにまとめる」「ChatGPTにHTMLを出力してもらう」「モック(HTML)を確認する」「要件を追記する」を繰り返すほうが良いと感じました。
  3. ある程度希望のモック画面ができたら自分で微調整する
    • 細かい部分はChatGPTに伝える言葉を考えるより、JavaScriptやCSSを書いてしまったほうが早そうでした。

さいごに

ChatGPTでHTMLを出力するのは思っていたよりも簡単でした。
ただ、「要件を文字にする能力」が問われました(というか苦戦しました)。
とはいえ、ゼロからHTMLを書くことを考えれば、かなり簡単にモックのHTMLが作成できます。

この後の試行錯誤

4
1
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
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?