業務でGoogle スプレッドシートをよく使います。数字だけでなく文字もびっしりです。日々矢印キーでかちゃかちゃとセルを移動します。
変わり映えしなブラウザ画面に飽きていたある日、ふと、セルの中身に合わせて画像が表示されたら楽しいのでは?と思いました。
ざっと検索しても見つけられず、Webアプリやマクロなど長く考えていましたが、chromeの拡張機能が簡単であることに気づきました。
早速geminiとともに作成したものがこちら。
上記の例ではqiita、zennという文字列が含まれるセルが選択された場合に、ロゴ画像を表示します。セル内のテキストに一部でもその文字が含まれていればマッチ判定です(つまりqiita.comとかでも反応する)。
娯楽用途以外にもなにかに使える気がするのですが、実用的な用途は思いつかず。。。 今はただ二つの技術ブログサイトのロゴが出るだけです。
そもそも画像表示が求められるようなものをスプレッドシートで管理するのが間違っている気もします。
構成
QiitaとZennの画像表示拡張機能など、ウェブストア公開したところで需要がないと思いますので、コードを貼ります。
chromeの拡張機能は manifestファイル、javascriptのソースコード、表示を整えるcssファイルさえあれば最低限のものが作れるので、簡単です。
chromeでは chrome://extensions/ からデベロッパーモードにすることで開発中のものを取り込めるので適宜ご活用ください。

以下作成物の構成。images配下に別途画像を用意する必要があります。
logo_overlay_extension
├── content.js
├── images
│ ├── qiita.png
│ └── zenn.png
├── manifest.json
└── styles.css
(1) manifest.json
{
"manifest_version": 3,
"name": "Overlay logos",
"version": "1.0",
"description": "スプレッドシートで特定の文字の含まれたセルを選択すると、対応する画像をオーバーレイ表示します。",
"permissions": [
"activeTab"
],
"host_permissions": [
"https://docs.google.com/spreadsheets/*"
],
"content_scripts": [
{
"matches": [
"https://docs.google.com/spreadsheets/*"
],
"js": [
"content.js"
],
"css": [
"styles.css"
]
}
],
"web_accessible_resources": [
{
"resources": [
"images/*png"
],
"matches": [
"https://docs.google.com/*"
]
}
]
}
(2) content.js
// ターゲットとなる文字列一覧を格納する。
// 簡単のため、表示する画像名が同じだとする. ex)qiita.png, zenn.png
let targetTextList = [
"qiita",
"zenn"
]
let textToImagePathMap = {}
for (const e of targetTextList) {
textToImagePathMap[e] = chrome.runtime.getURL(`images/${e}.png`)
}
let overlayDiv = null;
//要素を初期化・追加
function initializeOverlay() {
if (!overlayDiv) {
overlayDiv = document.createElement('div');
overlayDiv.id = 'spreadsheet-image-overlay';
document.body.appendChild(overlayDiv);
}
}
function showOverlay(imagePath, targetText) {
if (!overlayDiv) initializeOverlay();
overlayDiv.innerHTML = `
<!--‹div class="overlay-title">${targetText} </div> -->
<img src="${imagePath}" alt="${targetText}"/>
`;
overlayDiv.classList.add('visible');
}
//オーバーレイを非表示にする関数
function hideOverlay() {
if (overlayDiv) {
overlayDiv.classList.remove('visible');
}
}
// メインの判定ロジック
function checkActiveCell() {
const activeCellElement = document.querySelector('.cell-input, .docs-formula-input-container, •grid-active-cell');
if (activeCellElement) {
let cellText = activeCellElement.innerText.trim();
cellText = cellText.replace(" (", "(")
cellText = cellText.replace(")", ") ")
cellText = cellText.replace("「", "")
cellText = cellText.replace("」", "")
let foundMatch = false;
// targetTextListの中身を増やした場合に、
// 部分一致の文言が発生することを加味して長い順に判定を行う
const sortedtargetTexts = Object.keys(textToImagePathMap).sort((a, b) => b.length - a.length);
for (const targetTextPattern of sortedtargetTexts) {
if (cellText.includes(targetTextPattern)) {
const imagePath = textToImagePathMap[targetTextPattern];
showOverlay(imagePath, targetTextPattern);
foundMatch = true;
break;
}
}
if (!foundMatch) {
hideOverlay();
}
} else {
hideOverlay();
}
}
document.addEventListener('keyup',
function (event) {
//矢印キーが押された場合のみチェックを実行
const arrowKeys = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'];
if (arrowKeys.includes(event.key)) {
//スプレッドシートの移動処理が完了するのをわずかに待つ(念のため)
setTimeout(checkActiveCell, 100);
}
});
document.addEventListener('mouseup',
function () {
setTimeout(checkActiveCell, 100);
});
//初期化
initializeOverlay();
(3) styles.css
#spreadsheet-image-overlay {
position: fixed;
top: 20px;
right: 20px;
width: 300px;
max-height: 400px;
background-color: white;
border: 1px solid #ccc;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
border-radius: 8px;
padding: 10px;
z-index: 99999;
opacity: 0;
transition: opacity 0.3s ease;
pointer-events: none;
display: none;
}
#spreadsheet-image-overlay.visible {
display: block;
opacity: 1;
}
#spreadsheet-image-overlay img {
width: 100%;
height: auto;
display: block;
border-radius: 4px;
}
#spreadsheet-image-overlay .overlay-title {
font-size: 14px;
font-weight: bold;
margin-bottom: 8px;
color: #333;
}
そのほか
名前管理台帳でその人の顔画像とか表示したら面白いかもしれない
