TypeScriptで通知バッジを実装する:ベルアイコンに数字を表示する

スポンサーリンク

TypeScriptで通知バッジを実装する:ベルアイコンに数字を表示する

SNSやチャットアプリでよく見る、ベルアイコン右上に数字を表示する通知バッジの実装です。ライブラリなしの HTML + CSS + TypeScript で作れます。


HTML構造

position: relative の親要素に対して、バッジを position: absolute で重ねます。

<div class="bell-wrapper">
  <svg class="bell-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
    <path d="M12 22c1.1 0 2-.9 2-2h-4c0 1.1.9 2 2 2zm6-6v-5c0-3.07-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68C7.63 5.36 6 7.92 6 11v5l-2 2v1h16v-1l-2-2z"/>
  </svg>
  <span class="badge" id="badge"></span>
</div>

CSS:バッジの配置とアニメーション

.bell-wrapper {
  position: relative;
  display: inline-block;
}

.badge {
  position: absolute;
  top: 0;
  right: 0;
  background: #e53935;
  color: white;
  font-size: 15px;
  font-weight: 700;
  min-width: 26px;
  height: 26px;
  border-radius: 13px;
  padding: 0 7px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 2px solid white; /* アイコンとの境界を明確にする */
  opacity: 0;
  transform: scale(0);
  transition: opacity 0.15s, transform 0.15s;
}

.badge.visible {
  opacity: 1;
  transform: scale(1);
}

.badge.pop {
  animation: badge-pop 0.25s ease-out;
}

@keyframes badge-pop {
  0%   { transform: scale(1); }
  50%  { transform: scale(1.4); }
  100% { transform: scale(1); }
}

min-width を使うことで、1桁は円形、2桁以上は横長の丸角矩形になります。


TypeScript:カウントの更新

const badge = document.getElementById('badge') as HTMLSpanElement
let count = 0

function updateBadge(): void {
  if (count === 0) {
    badge.classList.remove('visible')
    badge.textContent = ''
    return
  }
  badge.textContent = count > 9 ? '9+' : String(count)
  badge.classList.add('visible')

  // クラスを一度外してから付け直すことでアニメーションをリセット
  badge.classList.remove('pop')
  void badge.offsetWidth
  badge.classList.add('pop')
}

// 通知を追加
function addNotification(): void {
  count++
  updateBadge()
}

// 通知をクリア(ベルクリック・クリアボタンどちらでも呼ぶ)
function clearNotifications(): void {
  count = 0
  updateBadge()
}

void badge.offsetWidth は DOM の再描画を強制してアニメーションをリセットするイディオムです。これがないと、連続クリック時に pop アニメーションが再生されません。


イベントの登録

const bellWrapper = document.querySelector<HTMLElement>('.bell-wrapper')!
const btnAdd = document.getElementById('btn-add') as HTMLButtonElement
const btnClear = document.getElementById('btn-clear') as HTMLButtonElement

bellWrapper.addEventListener('click', clearNotifications)
btnAdd.addEventListener('click', addNotification)
btnClear.addEventListener('click', clearNotifications)

実際のアプリではベルクリック時に通知一覧へ遷移しますが、clearNotifications() を呼ぶ位置は同じです。


10件超の表示

badge.textContent = count > 9 ? '9+' : String(count)

上限を変えたい場合は 9 の部分を変えるだけです。99+ にするなら count > 99 ? '99+' : String(count) にします。


まとめ

やること コード
バッジの配置 親に position: relative、バッジに position: absolute; top: 0; right: 0
表示・非表示 visible クラスで opacitytransform: scale を切り替える
出現アニメーション @keyframes + クラスの付け外しでリセット
10件超の表示 count > 9 ? '9+' : String(count)
アニメーションリセット void badge.offsetWidth で強制再描画