特定の要素をHoverするとツールチップを表示する。
要件は下記
- hoverタイミングでツールチップを表示
- 特定の要素の上中央に表示する
- 200ms遅延させて表示
- hoverが終わるとツールチップを非表示
完成イメージはこんなかんじ。
目次
HTMLを定義する
今回は特定の要素をhoverした時にツールチップを表示するため、表示したいDOMに対してdata属性を追加します。
ツールチップなので「data-tooltip」としました。
このdata-tooltip属性でツールチップを表示したいテキストを設定します。
HTMLはこんな感じになります。
<div style="width: fit-content; margin-top: 80px;">
<div data-tooltip="ツールチップ">ホバーすると表示される</div>
</div>
CSSを定義する
ツールチップを装飾します。
.tooltip {
position: absolute;
background: #4b4848e0;
color: #FFFFFF;
font-size: 14px;
line-height: 20px;
padding: 8px 12px;
border-radius: 4px;
opacity: 0;
transition: opacity 0.2s;
pointer-events: none;
transform: translateY(-8px);
max-width: 184px;
}
.tooltip.show {
opacity: 1;
}
JavaScriptでtoolTipを実装する
DOMが読み込まれてから、data-tooltip属性をもつDOM一覧を取得し、それぞれのDOMにイベントをつけていきます。
document.addEventListener('DOMContentLoaded', () => {
const tooltipContainers = document.querySelectorAll('[data-tooltip]');
tooltipContainers.forEach(container => {
let tooltipTimeout;
let tooltipElement;
container.addEventListener('mouseenter', () => {
// show tooltip
});
container.addEventListener('mouseleave', () => {
// hide tooltip
});
});
});
mouseenterのイベントを書いていきます。
mouseenterの時は、setTimeoutで200msまったあとに、ホバー処理を書いていきます。
createElementでツールチップ用のDOMを生成し、ホバーした要素containerからツールチップの表示位置を計算し、ツールチップとして表示するtooltipElementのstyleを設定します。
DOMの位置によってはズレるので、必要に応じてwindow.scrollYなどを計算に加えます。
container.addEventListener('mouseenter', () => {
tooltipTimeout = setTimeout(() => {
const tooltipText = container.getAttribute('data-tooltip');
tooltipElement = document.createElement('div');
tooltipElement.className = 'tooltip';
tooltipElement.innerText = tooltipText;
document.body.appendChild(tooltipElement);
const containerRect = container.getBoundingClientRect();
const tooltipRect = tooltipElement.getBoundingClientRect();
const top = containerRect.top - tooltipRect.height - 8;
const left = containerRect.left + (containerRect.width / 2) - (tooltipRect.width / 2);
tooltipElement.style.top = `${top}px`;
tooltipElement.style.left = `${left}px`;
requestAnimationFrame(() => {
tooltipElement.classList.add('show');
});
}, 200);
});
mouseleaveのイベントを書いていきます。
ホバーされなくなったタイミングで、clearTimeoutを呼び出して、セットしたタイムアウトを解除します。その後、showクラスを削除することで非表示にします。
さらに一時的に作成したツールチップのDOMを削除しています。
container.addEventListener('mouseleave', () => {
clearTimeout(tooltipTimeout);
if (tooltipElement) {
tooltipElement.classList.remove('show');
tooltipElement.addEventListener('transitionend', () => {
if (tooltipElement.parentElement) {
tooltipElement.parentElement.removeChild(tooltipElement);
}
}, { once: true });
}
});
JavaScriptをまとめると下記になります。
document.addEventListener('DOMContentLoaded', () => {
const tooltipContainers = document.querySelectorAll('[data-tooltip]');
tooltipContainers.forEach(container => {
let tooltipTimeout;
let tooltipElement;
container.addEventListener('mouseenter', () => {
tooltipTimeout = setTimeout(() => {
const tooltipText = container.getAttribute('data-tooltip');
tooltipElement = document.createElement('div');
tooltipElement.className = 'tooltip';
tooltipElement.innerText = tooltipText;
document.body.appendChild(tooltipElement);
const containerRect = container.getBoundingClientRect();
const tooltipRect = tooltipElement.getBoundingClientRect();
const top = containerRect.top - tooltipRect.height - 8;
const left = containerRect.left + (containerRect.width / 2) - (tooltipRect.width / 2);
tooltipElement.style.top = `${top}px`;
tooltipElement.style.left = `${left}px`;
requestAnimationFrame(() => {
tooltipElement.classList.add('show');
});
}, 200);
});
container.addEventListener('mouseleave', () => {
clearTimeout(tooltipTimeout);
if (tooltipElement) {
tooltipElement.classList.remove('show');
tooltipElement.addEventListener('transitionend', () => {
if (tooltipElement.parentElement) {
tooltipElement.parentElement.removeChild(tooltipElement);
}
}, { once: true });
}
});
});
});
Summary
setTimeoutと、clearTimeoutの扱いが個人的なキモでした。