無限に連打できるいいねボタンなので実装するにはもうちょっと手を加えないといけないんですが、 いいねボタンを押すとデータベースとやり取りして値がリアルタイムに更新されて返ってくるという機能がすごく簡単に出来た。

Firebaseを初めて扱ったので、その練習と覚書。

制限あっても無料のSparkプランで十分に使えるのがいいですね。

Like Button Using JavaScript and Firebase

の記事とだいたい一緒なんですが、Javascript SDK v8からv9に上がって記事内にあるコードでは動かないのでちょっと修正しています。あとFirebase Authentication で匿名認証を追加しています。

アカウント取得とプロジェクトの作成

Googleアカウントを持っていたらすぐに始められます。

プロジェクトは作成済とします。

index.html を作成

HTML code

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" rel="stylesheet" />
    <title>Count Likes</title>
</head>
<!--CSS code-->
<body>

<div class="CountLike" id="count-1">
    <button class="button button1"><i class="fa fa-heart"></i> Like <span class="counterStat">...</span></button>
</div>

<div class="CountLike" id="count-2">
    <button class="button button1"><i class="fa fa-heart"></i> Like <span class="counterStat">...</span></button>
</div>

<!--Javascript Code-->
</body>
</html>

データ構造

いいねボタンを同一ページ内に複数配置しているので、idを取得して階層を作ることにした。イメージはこんな感じ。

{
  "count-1": {
    "likes": value
  },
  "count-2": {
    "likes": value
  }
}

いいねボタンの下にlikes以外のkeyは持たないことにする。

ユーザーの情報を取得するなら、もっとちゃんと考えないといけないだろうし……。

for Firebase code

初期化

まずはJavascript SDKをCDNから読み込み。npm推奨されてるけども。

参考:JavaScriptプロジェクトにFirebaseを追加する別の方法

そして firebaseConfig に作成したFirebaseプロジェクト設定を記入してinitializeApp()で初期化します。 databaseとauthも使用するのでそれらも追加。

</body> の前で読み込みます。

<script type="module">

import { initializeApp } from 'https://www.gstatic.com/firebasejs/9.12.1/firebase-app.js'

// Add Firebase products that you want to use
// https://firebase.google.com/docs/web/alt-setup
import { getDatabase, ref, child, runTransaction, onValue } from 'https://www.gstatic.com/firebasejs/9.12.1/firebase-database.js'
import { getAuth, signInAnonymously, onAuthStateChanged } from 'https://www.gstatic.com/firebasejs/9.12.1/firebase-auth.js'

// Your web app's Firebase configuration
const firebaseConfig = {
    apiKey: "Paste Your API Key provided by Firebase",
    authDomain: "Paste Your authDomain provided by Firebase",
    projectId: "Paste Your projectId provided by Firebase",
    databaseURL: "Create a database and then paste the url of that database here",
    storageBucket: "Paste Your storageBucket provided by Firebase",
    messagingSenderId: "Paste Your messagingSenderId provided by Firebase",
    appId: "Paste Your appId provided by Firebase"
};

const app = initializeApp(firebaseConfig);
const database = getDatabase();
const auth = getAuth();

</script>

匿名認証

Firebase Authentication も有効にします。今回は匿名認証で。

参考:JavaScript を使用して Firebase 匿名認証を行う

// Authenticate with Firebase Anonymously Using JavaScript
// https://firebase.google.com/docs/auth/web/anonymous-auth?hl=ja
signInAnonymously(auth).catch((error) => {
    const errorCode = error.code;
    const errorMessage = error.message;
    console.error(errorCode, errorMessage)
});

onAuthStateChanged(auth, (user) => {
    if (user) {
        // User is signed in, see docs for a list of available properties
        // https://firebase.google.com/docs/reference/js/firebase.User
        const uid = user.uid;
        const dbRef = ref(getDatabase());
        // Like Counter
        const dCounters = document.querySelectorAll('.CountLike');

        [].forEach.call(dCounters, function(dCounter) {

            let cId = dCounter.id;

            // get firebase data
            onValue(child(dbRef, "/" + cId + "/likes"), (snapshot) => {
                let data = snapshot.val() || 0;
                dCounter.querySelector('span').innerHTML = data;
            });

            // set firebase data
            dCounter.querySelector('button').addEventListener('click', function() {
                runTransaction(child(dbRef, "/" + cId + "/likes"), (dCount) => {
                    return (dCount || 0) + 1;
                });
            });
        });

    }
});

いいねカウント数の表示やボタンクリックした時の処理は onAuthStateChanged でユーザー認証した後に書いたらいい(っぽい)。

querySelectorAll('.CountLike')でNodelistを取り出して [].forEach.call で関数走らせてる(っぽい)。

読み取りと書き込み

データベースから参照 ref(getDatabase()) したデータを持ってくるときは snapshot.val() とか使えばいいらしい。 innerHTMLで取得した値を入れる。

参考:ウェブでのデータの読み取りと書き込み  |  Firebase Realtime Database

runTransaction で値をupdateしている。

多分ここはもっといい方法がある……(というかちゃんと理解していないでいる)。

セキュリティルール

参考:Firebase セキュリティ ルール

{
  "rules": {
    ".read": "null !== auth.uid",
    ".write": "null !== auth.uid",
      "$iine" : {
        "likes": { ".validate": "newData.isNumber() && newData.val() >= 0" },
        "$other": { ".validate": false }
  	}
  }
}
  • (匿名)認証していないユーザーにはreadもwriteの権限も与えない
  • 変数 $iine の下の likes はデータを検証(ゼロ以上の数値のみが許可される)
  • 変数 $other を使って、既にあるlikes以外の値の書き込みを許可しない

もっとより良くすることはできると思いますが、現状はここまで。

いいねボタンいろいろ

2022/10/23時点で見つけたもの

サーバー設置型

サービス型

その他

探してみればいろいろあるんだなと思いました。

実際に利用してはいないんですが……シンプルそうなLyketはNotionにも使えるようでちょっと興味があります。 あと、APIサーバーを立てて実装するApplause Buttonみたいなことは自分でもやってみたくなりました。