ポップアップ
テキスト選択ポップアップ
NoteやYahooみたいに、テキストを選択してポップアップを出し、その文字を検索・コピーできると楽ちんだなと思い、実装しました。当初はGoogle検索・コトバンク検索・コピーの3つでしたが、AIに他に便利な機能あるって尋ねると、「AIに詳しく聞けばいいんじゃね?」と言うので、AI検索も追加しました。
ポップアップ
元々自分で作ってホーバーする位置がうまくいかずお蔵入りしていたコードを基にして、Geminiに聞きながら作ったのがこちら。WordPressのSingle.phpの<?php the_content(); ?>の中だけで使っています。このページでも使えるので、文字を選択してみてください。
const initTextSelection = () => {
const textArea = document.querySelector('.textSelect');
if (!textArea) return;
textArea.style.position = 'relative';
const btnData = [
{ title: 'AIで解説', url: 'https://www.perplexity.ai/search?q=' },
{ title: 'Google検索', url: 'https://www.google.com/search?q=' },
{ title: 'コトバンク検索', url: 'https://kotobank.jp/search?q=' },
{ title: 'コピー', url: '#' }
];
const removePopup = () => {
const old = document.querySelector('.selectedText');
if (old) old.remove();
};
// mouseupイベント(textArea内でのみ反応)
textArea.addEventListener('mouseup', (event) => {
const selection = window.getSelection();
const textItem = selection.toString().trim();
if (!textItem) {
removePopup();
return;
}
removePopup();
const range = selection.getRangeAt(0);
const rect = range.getBoundingClientRect();
const parentRect = textArea.getBoundingClientRect();
const newElementUl = document.createElement('ul');
newElementUl.classList.add('selectedText');
// 座標計算(親要素からの相対位置 項目を増減すると選択項目の位置がずれるので150の値を変更する)
const top = rect.top - parentRect.top - 150;
const left = rect.left - parentRect.left + (rect.width / 2);
Object.assign(newElementUl.style, {
position: 'absolute',
top: `${top}px`,
left: `${left}px`,
transform: 'translateX(-50%)',
zIndex: 100
});
btnData.forEach((item, i) => {
const li = document.createElement('li');
li.className = 'item__li';
const isCopy = item.title === 'コピー';
li.innerHTML = `<a class="item__a" href="#">${isCopy ? item.title : item.title}</a>`;
li.addEventListener('mousedown', (e) => {
e.preventDefault();
e.stopPropagation();
if (item.title === 'コピー') {
if (navigator.clipboard) {
navigator.clipboard.writeText(textItem).then(() => {
console.log('Copied!');
});
}
} else {
let query = textItem;
if (item.title === 'AIで解説') {
query = `「${textItem}」について詳しく解説して`;
}
const url = item.url + encodeURIComponent(query);
window.open(url, '_blank');
}
removePopup();
});
newElementUl.appendChild(li);
});
textArea.appendChild(newElementUl);
// GSAPアニメーション
gsap.fromTo(newElementUl,
{ opacity: 0, y: 10 },
{ opacity: 1, y: 0, duration: 0.3, ease: "power2.out" }
);
});
// 画面のどこかをクリックしたらポップアップを消す
document.addEventListener('mousedown', (e) => {
if (!e.target.closest('.selectedText')) removePopup();
}, { once: true }); // メモリ対策で一度きりに設定
};
initTextSelection();元々Barbaのdata.next.containerを使っていたので、コードにcontainerが入っていれば、documentに書き換えてください。ポップアップのアニメーション処理はGSAPで行っているだけなので、CSSのアニメーションに変えても問題ありません。以下はCSS。textSelectが<?php the_content(); ?>をラップしているクラスです。
.textSelect {
.selectedText {
position: absolute; /* JSで動的に位置指定 */
display: flex;
align-items: center;
flex-direction: column;
align-items: stretch;
gap: 3px;
list-style: none;
min-width: 120px;
margin: 0;
padding: 4px;
background-color: #333;
border-radius: 4px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
white-space: nowrap; /* 改行を防ぐ */
z-index: 30;
pointer-events: auto !important; /* 強制的にクリックを有効にする */
cursor: default;
/* 矢印(吹き出し風)をつけたい場合 */
&::after {
content: "";
position: absolute;
top: 100%; /* 下側に配置 */
left: 50%;
transform: translateX(-50%);
border-width: 6px;
border-style: solid;
border-color: #333 transparent transparent transparent;
}
.item__li {
position: relative;
width: 100%;
margin: 0;
padding: 0;
border-radius: 4px;
}
/* リンクボタン(a) */
.item__a {
display: block;
pointer-events: auto !important;
cursor: pointer; /* 指マークにする */
padding: 8px 12px;
color: #fff;
text-decoration: none;
font-size: 1.2rem;
// font-weight: bold;
line-height: 1;
text-align: center;
border-radius: 4px;
transition: background-color 0.2s ease;
&:hover {
background-color: #8b0d0d;
color: #fff;
text-decoration: none;
border-radius: 4px;
}
}
}
}コメント
選曲
The Moldau