1. はじめに
Web サーバから非同期で取得したメッセージを Materialize の Tooltips で表示しようとしてハマったので、やり方をここに記載します。
2. 環境
Materialize 1.0.0
jQuery 3.7.1
3. やりたかったこと
特定の要素にマウスポインタが置かれた時に、Web サーバーからデータを取得し、それを Materialize のツールチップで表示しようとしました。
その際、ツールチップのメッセージを動的に変更する方法が分からず、手探りで動きを確認しながら実装を行いました。
4. デモ
「ここにマウスポインタを乗せてください」の上にマウスポインタを置くと、ローディングスピナーを載せたツールチップが表示され、2秒後にツールチップの表示内容が置き換わります。
<!DOCTYPE html>
<html lang="ja">
<meta charset="UTF-8" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Materialize Tooltips - dynamic display demo</title>
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
<script src="script.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
<link rel="stylesheet" href="style.css">
<head>
</head>
<body>
<div id="demo-container">
<span
class="btn tooltipped dynatp"
data-tooltip="<div><div class='preloader-wrapper small active'><div class='spinner-layer spinner-blue-only'><div class='circle-clipper left'><div class='circle'></div></div><div class='gap-patch'><div class='circle'></div></div><div class='circle-clipper right'><div class='circle'></div></div></div></div></div>">
ここにマウスポインタを乗せてください
</span>
</div>
</body>
</html>
$(() => {
$('.tooltipped').tooltip();
$(document).on('mouseover', '.dynatp', (event) => {
const dis = $(event.target);
// サーバーからAjaxでデータ取得(ここではタイマーで代用)
setTimeout(() => {
const tltp = $(dis.attr('data-tooltip'));
tltp.empty();
tltp.append('<div>[動的メッセージデモ]<br>これは動的に設定されたメッセージです。</div>');
dis.attr('data-tooltip', tltp.prop('outerHTML'));
// ツールチップ表示中判定
const isOpen = dis[0].M_Tooltip && dis[0].M_Tooltip.isOpen;
if (isOpen) {
dis.tooltip('close');
}
// ツールチップの再構成
dis.tooltip('destroy');
dis.tooltip();
if (isOpen) {
dis.tooltip('open');
}
}, 2000);
});
});
#demo-container {
margin: 20px;
}
動きの確認は↓
See the Pen Materialize Tooltips - dynamic display demo by Koji Hataya (@hataya_koji) on CodePen.
5. 解説
index.html と script.js について解説します。
※style.css は説明を割愛します。
5.1 index.html
ここでの注目箇所は、ツールチップが適用される <span>
要素の data-tooltip 属性になります。
この属性には HTML 文字列をセットしていますが、内容はローディングスピナーの定義です。
これにより、表示対象データの取得が完了するまで、ツールチップにはローディングスピナーが表示されるようになります。
ちなみにローディングスピナーは、Materialize が提供しているものを使用しています。
<div>
<div class='preloader-wrapper small active'>
<div class='spinner-layer spinner-blue-only'>
<div class='circle-clipper left'>
<div class='circle'></div>
</div>
<div class='gap-patch'>
<div class='circle'></div>
</div>
<div class='circle-clipper right'>
<div class='circle'></div>
</div>
</div>
</div>
</div>
5.2 script.js
$('.tooltipped').tooltip();
CSS クラス "tooltipped" を持つ要素に対し、ツールチップを適用しています。
$(document).on('mouseover', '.dynatp', (event) => {
CSS クラス "dynatp" を持つ要素のマウスオーバーイベントを拾って処理しています。
この要素は、上記のツールチップを適用された要素("tooltipped")と同じものになりますが、ツールチップ適用対象は "tooltipped"、動的メッセージ対象は "dynatp" と区別して使用しています(ツールチップ適用対象だが、動的メッセージ対象ではない要素があると困るので)。
const tltp = $(dis.attr('data-tooltip')); // (1)
tltp.empty(); // (2)
tltp.append('<div>[動的メッセージデモ]<br>これは動的に設定されたメッセージです。</div>'); // (3)
dis.attr('data-tooltip', tltp.prop('outerHTML')); // (4)
(1) data-tooltip 属性の設定値(HTML文字列)を jQuery オブジェクトに変換し、tltp 変数(定数)に代入しています。
(2) <div>~</div>
の内側の定義(ローディングスピナー)を消去しています。
(3) <div>~</div>
の内側に新規メッセージを追加しています。
(4) 書き換えた HTML 定義を対象要素の data-tooltip 属性に上書きしています。外側の <div>
要素も含めるため、~.prop('outerHTML') で HTML 文字列を出力しています。
// ツールチップ表示中判定
const isOpen = dis[0].M_Tooltip && dis[0].M_Tooltip.isOpen;
if (isOpen) {
dis.tooltip('close');
}
// ツールチップの再構成
dis.tooltip('destroy');
dis.tooltip();
if (isOpen) {
dis.tooltip('open');
}
実は data-tooltip 属性に新たな内容を書き戻しても、ツールチップに表示される内容は変わりません。
したがって、
dis.tooltip('destroy');
でツールチップを破棄して
dis.tooltip();
で作り直しています。
これが今回のキモです。
その他、ツールチップの表示中に内容が作り直されると、勝手にツールチップが閉じられたようになってしまうため、ツールチップが表示中かどうかの判定と再表示処理を入れています。
6. おわりに
今回のデモでは必要最小限のコードしか記載していないため、私が実際に使用してるコードには少し処理が加えられています。
たとえば、運用では Web サーバーから非同期で取得したデータをツールチップに表示していますが、データ取得後は同処理が実行されないようにガードしています。
また、動的メッセージ表示対象の要素が複数あり、各々取得するデータが異なるため、それを識別するキーを data-xxx
属性で持つようにしています。
いずれも簡単に実現できますので、興味のある方は試してみてください。
7. 参考にさせていただいた情報
Documentation - Materialize
https://materializecss.com/