解説コードを読んでみよう
このサンプルでわかること
- マウスが乗った・離れたときの処理(
mouseenter/mouseleave) - クリックした場所から、同じ行の入力欄を探す方法(
closest()とfind()) - あとから追加した行でもボタンが効く「イベントの委譲」
使っているファイル
- index.phpデモのページ(HTML・CSS・JavaScript をすべてこの1ファイルに記述)
- jqueryjQuery 本体(サイト共通で読み込み済み)
1. HTML:1行分の部品を用意する
表の1行が「品詞ボタン」「意味」「単語」「削除」のセットです。品詞ボタンは ul のリストで、最初は CSS で隠しておきます。挿入する文字は data-char 属性に書いておくと、JavaScript から取り出しやすくなります。
<tr>
<td class="no">1</td>
<td>
<div class="insert">
<span class="insert__open">▲</span>
<ul class="insert__list">
<li data-char="[動]">動</li>
<li data-char="[形]">形</li>
<li data-char="[名]">名</li>
<li data-char="[副]">副</li>
<li data-char="[接]">接</li>
<li data-char="[前]">前</li>
</ul>
</div>
</td>
<td><input type="text" class="meaning" placeholder="例: [動] 走る"></td>
<td><input type="text" class="word" placeholder="例: run"></td>
<td><button type="button" class="btn row-del">削除</button></td>
</tr>
data-〇〇="…" は、HTML の要素に自由なデータを持たせるための書き方です。jQuery では $(this).data('char') で data-char の値を取り出せます。
2. CSS:ボタン一覧を最初は隠しておく
.insert__list に display: none; を指定して、最初は見えないようにします。position: absolute; で「▲」の真下に重ねて表示します。
<style>
.word-table input { width: 100%; }
.insert { position: relative; display: inline-block; }
.insert__open { display: inline-block; padding: 4px 8px; border: 1px solid #d8d3c7; border-radius: 6px; cursor: pointer; background: #fff; }
.insert__list { display: none; position: absolute; left: 0; top: 100%; z-index: 5; margin: 0; padding: 4px; list-style: none; white-space: nowrap; background: #1d2433; border-radius: 8px; }
.insert__list li { display: inline-block; width: 30px; margin: 2px; padding: 4px 0; text-align: center; font-size: 13px; color: #fff; border-radius: 5px; cursor: pointer; background: #3c4557; }
.insert__list li:hover { background: #ffd866; color: #1d2433; }
</style>
3. マウスを乗せたら表示、離れたら隠す
mouseenter はマウスが要素に入ったとき、mouseleave は出たときに起きるイベントです。.show() と .hide() で表示を切り替えます。スマホにはマウスがないので、「▲」をタップしたときにも開閉できるように click も用意しています。
// ① マウスが乗ったらボタン一覧を出し、離れたら隠す
$('.word-table').on('mouseenter', '.insert', function() {
$(this).children('ul').show();
}).on('mouseleave', '.insert', function() {
$(this).children('ul').hide();
}).on('click', '.insert__open', function() {
$(this).next('ul').toggle(); // スマホ用: タップでも開閉できるように
});
4. クリックした記号を、同じ行の入力欄に足す
ここがこのサンプルの中心です。クリックされた li($(this))から、closest('tr') で一番近い親の行までさかのぼり、find('.meaning') でその行の中の入力欄を探します。
// ② 品詞ボタンをクリックしたら、同じ行の「意味」欄の後ろに記号を足す
$('.word-table').on('click', '.insert__list li', function() {
var input = $(this).closest('tr').find('.meaning'); // 同じ行の入力欄を探す
input.val(input.val() + $(this).data('char')).focus();
});
| 書き方 | 意味 |
|---|---|
$(this) | 今クリックされた要素 |
.closest('tr') | 自分から親の方向へたどり、最初に見つかった tr(行) |
.find('.meaning') | その中にある class="meaning" の要素 |
input.val() | 入力欄の今の文字(引数なし=取得) |
input.val(文字) | 入力欄に文字をセット(引数あり=設定) |
以前のバージョンでは $(this).parent().parent().parent().next().children() のように親を何段もたどって入力欄を探していました。これだと HTML を少し変えただけで動かなくなります。closest() と find() を使うと、HTML の形が多少変わっても壊れにくくなります。
5. 行の追加と削除(イベントの委譲)
「行を追加」は1行目を .clone() でコピーし、入力欄を空にしてから表の最後に足しています。
// ③ 行を追加する(1行目をコピーして中身を空にする)
$('.row-add').on('click', function() {
var row = $('.word-table tbody tr:first').clone();
row.find('input').val('');
row.find('ul').hide();
$('.word-table tbody').append(row);
renumber();
});
// ④ 行を削除する(最後の1行は残す)
$('.word-table').on('click', '.row-del', function() {
if ($('.word-table tbody tr').length > 1) {
$(this).closest('tr').remove();
renumber();
}
});
// 行番号をふり直す
function renumber() {
$('.word-table tbody tr').each(function(i) {
$(this).find('.no').text(i + 1);
});
}
3〜5の処理はすべて $('.word-table').on('click', '.row-del', …) のように、表全体にイベントを登録して、対象を2番目の引数で指定しています。これを「イベントの委譲」といいます。$('.row-del').on('click', …) と書くと、ページを開いた時点の行にしか効かず、あとから追加した行のボタンは反応しません。
品詞ボタンの li を1つ増やして、data-char="[代]"(代名詞)を追加してみましょう。JavaScript は1行も変えずに動くはずです。