スプレットシートを利用してチャット機能を作ってみた。

### Google Apps Scriptの基本

Google Apps Scriptは、Googleのスプレッドシートやその他のサービスを自動化するためのプログラムです。今回は、ブログ投稿をスプレッドシートから取得するための3つの関数を見ていきます。

### 1. `doGet` 関数

```javascript
function doGet() {
  return HtmlService.createHtmlOutputFromFile('ブログ');//ここの名前はなんでもいい!!
}
```

- **目的**: 
  - Webアプリを開いたときに最初に呼ばれる関数です。この関数は、ユーザーにHTMLページを表示します。

- **どう動くか**:
  - `HtmlService.createHtmlOutputFromFile('ブログ')` を使うことで、`ブログ.html`というファイルの内容をブラウザに表示します。

- **具体例**:
  - ユーザーがアプリのURLにアクセスすると、ブログの投稿が表示されるページが開かれます。

### 2. `getBlogPosts` 関数

```javascript
function getBlogPosts() {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  const data = sheet.getDataRange().getValues();
  const posts = [];
  for (let i = 1; i < data.length; i++) { 
    posts.push({
      id: i, 
      nickname: data[i][0],
      content: data[i][1]
    });
  }
  return posts;
}
```

- **目的**: 
  - スプレッドシートからブログ投稿のデータを取得するための関数です。

- **どう動くか**:
  - スプレッドシートのデータを取得し、ブログの投稿を「ニックネーム」と「内容」として整理します。
  - 1行目はヘッダー（タイトル）なので、2行目から始めてデータを取得します。

- **具体例**:
  - スプレッドシートに以下のようなデータがあるとします：

    | ニックネーム | 投稿内容       |
    |--------------|----------------|
    | ユーザーA    | 初めての投稿   |
    | ユーザーB    | こんにちは！   |

  - この関数が実行されると、以下のようなデータが作られます：

    ```javascript
    [
      { id: 1, nickname: 'ユーザーA', content: '初めての投稿' },
      { id: 2, nickname: 'ユーザーB', content: 'こんにちは！' }
    ]
    ```

### 3. `addBlogPost` 関数

```javascript
function addBlogPost(nickname, content) {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  sheet.appendRow([nickname, content]);
}
```

- **目的**: 
  - 新しいブログ投稿をスプレッドシートに追加する関数です。

- **どう動くか**:
  - 引数として受け取った「ニックネーム」と「内容」を、スプレッドシートの新しい行に追加します。

- **具体例**:
  - ユーザーが新しい投稿を入力した場合、たとえば「ユーザーC」と「新しい投稿内容」を追加したいとします。この関数が呼ばれると、スプレッドシートの最下部に次のような行が追加されます：

    | ニックネーム | 投稿内容       |
    |--------------|----------------|
    | ユーザーC    | 新しい投稿内容 |

### まとめ

- **doGet**: Webアプリを開いたときにHTMLページを表示する。
- **getBlogPosts**: スプレッドシートからブログ投稿データを取得し、整理して返す。
- **addBlogPost**: 新しい投稿をスプレッドシートに追加する。

もちろん、さらに詳しく説明します。各部分を深堀りし、具体的なコードを用いながら説明します。


# HTMLの基本構造
### 1. **基本構造**

```html
<!DOCTYPE html>
<html lang="ja">
```
- **`<!DOCTYPE html>`**: HTML5の文書形式を宣言します。この宣言により、ブラウザは文書をHTML5として解釈します。
- **`<html lang="ja">`**: `lang`属性により、文書が日本語で書かれていることを示します。これにより、スクリーンリーダーなどの支援技術が適切に言語を認識できます。

### 2. **メタ情報とスタイル**

```html
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Chat</title>
  <style>
    /* CSSスタイルここに続く */
  </style>
</head>
```
- **`<head>`**: この部分には、ページに関するメタデータやスタイル、スクリプトが含まれます。
- **`<meta charset="UTF-8">`**: ページの文字エンコーディングをUTF-8に設定します。これにより、日本語や他の多言語の文字が正しく表示されます。
- **`<meta name="viewport" content="width=device-width, initial-scale=1.0">`**: モバイルデバイス向けに表示を調整します。これにより、画面幅に合わせてコンテンツが適切にスケーリングされます。
- **`<title>Chat</title>`**: ページのタイトルを設定します。このタイトルは、ブラウザタブや検索結果に表示されます。

### 3. **CSSスタイル**

```css
body {
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
  margin: 0;
  padding: 0;
  background-color: #f0f4f8;
  display: flex;
  flex-direction: column;
  align-items: center;
  animation: fadeIn 1s ease;
}
```
- **`body`**: ページ全体のスタイルを設定しています。
  - **フォント**: 複数のフォントファミリーが指定されており、最初に利用可能なフォントが適用されます。`sans-serif`は、フォントが見つからない場合のバックアップフォントです。
  - **マージンとパディング**: `0`に設定することで、ブラウザデフォルトの余白を取り除きます。
  - **背景色**: `#f0f4f8`は柔らかいグレーがかった青色で、視覚的に落ち着いた印象を与えます。
  - **フレックスボックス**: `display: flex;`を用いて、子要素を縦に並べる設定をしています。`align-items: center;`で水平方向の中央に配置します。
  - **アニメーション**: `fadeIn`アニメーションを設定し、ページが読み込まれるときにスムーズに現れる効果を追加しています。

```css
@keyframes fadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}
```
- **アニメーション定義**: `fadeIn`という名前のアニメーションを定義します。`from`と`to`を使い、要素が透明から不透明になる過程を設定します。

### 4. **見出しとコンテナ**

```html
<h1>Chat</h1>
<div class="container">
```
- **`<h1>Chat</h1>`**: ページのタイトルを表示する要素です。視覚的に強調され、重要な情報を提供します。
- **`<div class="container">`**: 投稿フォームと投稿リストを格納するコンテナです。この中で両方の要素を横に並べます。

### 5. **フォーム部分**

```html
<div id="form">
  <input type="text" id="nickname" placeholder="ニックネーム" required>
  <textarea id="content" placeholder="投稿内容" required rows="8"></textarea>
  <button id="submitButton" onclick="submitPost()">投稿</button>
</div>
```
- **`<div id="form">`**: ユーザーが投稿を行うためのフォーム。
- **`<input type="text">`**: ユーザーがニックネームを入力するためのフィールドです。`placeholder`属性により、入力例が表示されます。`required`属性は、このフィールドが必須であることを示します。
- **`<textarea>`**: ユーザーが投稿内容を入力するためのフィールドです。`rows="8"`により、初期表示される行数を指定します。
- **`<button>`**: 投稿ボタン。`onclick`属性で、ボタンがクリックされたときに`submitPost()`関数を呼び出します。

### 6. **投稿リスト部分**

```html
<div id="posts"></div>
```
- **`<div id="posts">`**: 投稿が表示される領域です。この部分はJavaScriptによって動的に更新されます。初めは空ですが、投稿が追加されると内容がここに表示されます。

### 7. **JavaScript機能**

#### 7.1 投稿の表示

```javascript
function displayPosts(posts) {
  const postsContainer = document.getElementById('posts');
  postsContainer.innerHTML = ''; 
  posts.reverse(); 

  posts.forEach(post => {
    const postDiv = document.createElement('div');
    postDiv.className = 'post';
    postDiv.innerHTML = `
      <h2>${post.nickname}</h2>
      <p>${post.content}</p>
    `;
    postsContainer.appendChild(postDiv);
  });
}
```
- **`displayPosts(posts)`**: 投稿の配列を受け取り、HTMLとして表示します。
  - **`const postsContainer = document.getElementById('posts');`**: 投稿を表示するための要素を取得します。
  - **`postsContainer.innerHTML = '';`**: 既存の投稿をクリアします。これにより、古い投稿が残らないようにします。
  - **`posts.reverse();`**: 新しい投稿が上に来るように配列の順序を逆にします。
  - **`forEach`**: 各投稿に対して、新しい`div`を作成し、ニックネームと内容を設定して投稿リストに追加します。

#### 7.2 投稿の送信

```javascript
function submitPost() {
  const nickname = document.getElementById('nickname').value;
  const content = document.getElementById('content').value;

  if (nickname && content) {
    google.script.run.withSuccessHandler(() => {
      fetchPosts();
    }).addBlogPost(nickname, content);
    
    document.getElementById('nickname').value = '';
    document.getElementById('content').value = '';
  } else {
    alert('ニックネームと投稿内容を入力してください。');
  }
}
```
- **`submitPost()`**: ユーザーが入力したニックネームと内容を取得してサーバーに送信する関数。
  - **`const nickname = document.getElementById('nickname').value;`**: ニックネームを取得します。
  - **`const content = document.getElementById('content').value;`**: 投稿内容を取得します。
  - **`if (nickname && content)`**: 両方のフィールドが入力されているかを確認します。
  - **`google.script.run.withSuccessHandler(() => { fetchPosts(); }).addBlogPost(nickname, content);`**: Google Apps Scriptの`addBlogPost`関数を呼び出し、成功時に`fetchPosts()`を呼び出します。これにより、新しい投稿が表示されるようになります。
  - **`document.getElementById('nickname').value = '';`**: フィールドをクリアします。
  - **`alert('ニックネームと投稿内容を入力してください。');`**: 入力が不足している場合は警告を表示します。

#### 7.3 投稿の取得

```javascript
function fetchPosts() {
  google.script.run.withSuccessHandler(displayPosts).getBlogPosts();
}
```
- **`fetchPosts()`**: サーバーから投稿を取得するための関数です。
  - **`google.script.run.withSuccessHandler(displayPosts)`**: Google Apps Scriptの`getBlogPosts`関数を呼び出し、取得した投稿を`displayPosts`関数で表示します。

#### 7.4 定期的な更新

```javascript
setInterval(fetchPosts, 500

);
fetchPosts();
```
- **`setInterval(fetchPosts, 500)`**: 500ミリ秒ごとに`fetchPosts()`を呼び出し、リアルタイムで投稿を更新します。
- **`fetchPosts();`**: ページが読み込まれた際に即座に投稿を取得します。

### 8. **フッター**

```html
<footer>
  &copy; 2024 Chat
</footer>
```
- **`<footer>`**: ページの下部に著作権情報を表示します。`&copy;`は著作権記号を表示するHTMLエンティティです。2024年の情報を示しています。

### **全体の流れ**

1. **ページの読み込み**:
   - HTMLが読み込まれると、CSSスタイルが適用され、見た目が整えられます。
   - `fetchPosts()`が呼び出され、サーバーから既存の投稿が取得され、表示されます。

2. **投稿の追加**:
   - ユーザーがニックネームと投稿内容を入力し、「投稿」ボタンをクリックすると、`submitPost()`関数が実行されます。
   - 入力が検証され、Google Apps Scriptを介してサーバーに新しい投稿が追加されます。

3. **投稿の表示**:
   - サーバーからの応答により、新しい投稿が取得され、`displayPosts()`が呼ばれて、画面に投稿が表示されます。

4. **リアルタイム更新**:
   - `setInterval`により、500ミリ秒ごとに新しい投稿が自動的に更新され、常に最新の投稿が表示されます。

このアプリは、シンプルでありながら、リアルタイムでのインタラクションを可能にする基本的なチャットシステムの仕組みを示しています。もし特定の部分や機能についてさらに詳しく知りたいことがあれば教えてください！