はじめに
プログラミングスクールの課題で、Railsを用いたチャットアプリを作成した際にjavascriptの処理で詰まったことがあったので、同じことで悩んでいる人もいるのではないかと思い記録に残します。
行なった内容
チャットグループを作成または編集する際、インクリメンタルサーチでユーザーの検索を行い、出現した名前の[追加]ボタンを押すことでメンバー一覧の箇所に選択したユーザーの名前が表示されるという流れを実装した。
環境
Rails 5.0.7.2
Ruby 2.5.1
詰まってしまった内容
上記のGifは正常に動作している状態なのですが、私は下の状態になってしまい困り果てていました。
一度しかクリックしていないのにtestuserという値が大量に出てきました。しかも表示されるのが2個の時や3個の時もあったのです。これは困りました。
デベロッパツールでコンソールを見てもエラーらしきものは出てきておらず、原因がよくわかりません。
該当コード
以下は該当コードを抜粋したものです。修正前の記述です。
.chat-group-form__field
.chat-group-form__field--left
= f.label :グループ名, class: 'chat-group-form__label'
.chat-group-form__field--right
= f.text_field :name, class: 'chat__group_name chat-group-form__input', placeholder: 'グループ名を入力してください'
.chat-group-form__field.clearfix
.chat-group-form__field--left
= f.label :チャットメンバーを追加, class: 'chat-group-form__label', for: 'chat_group_チャットメンバーを追加'
.chat-group-form__field--right
= f.text_field :member, class: 'chat-group-form__input', id: 'user-search-field', placeholder: '追加したいユーザー名を入力してください', type: 'text', value: ''
.chat-group-search-user
.chat-group-form__field.clearfix
.chat-group-form__field--left
%label.chat-group-form__label{for: "chat_group_チャットメンバー"} チャットメンバー
.chat-group-form__field--right
%input{name: 'group[user_ids][]', type: 'hidden', value: "#{current_user.id}"}
.chat-group-users.current-user
= current_user.name
.chat-group-users.js-add-user
- @group.users.each do |user|
- unless user == current_user
.chat-group-user.clearfix.js-chat-member
%input{name: 'group[user_ids][]', type: 'hidden', value: "#{user.id}"}
%p.chat-group-users__name
= user.name
.user-search-remove.chat-group-user__btn.chat-group-user__btn--remove.js-remove-btn{data: {user: {id: "#{user.id}", name: "#{user.name}"}}}
削除
インクリメンタルサーチ部分の記述は今回は割愛します。
$(document).on('turbolinks:load', function(){
//追加したいテンプレートを定義
function appendAddUserToHTML(user) {
var html = `<div class='chat-group-user clearfix js-chat-member'>
<input name='group[user_ids][]' type='hidden' value='${user.id}'>
<p class='chat-group-user__name'>${user.name}</p>
<div class='user-search-remove chat-group-user__btn chat-group-user__btn--remove js-remove-btn'>削除</div>
</div>`
$('.js-add-user').append(html);
}
//こちらで追加された要素のイベントを指定している
$(document).on("click", ".chat-group-user__btn--add", function(e){
e.preventDefault();
$(this.parentNode).remove();
var user = $(this).data('user-id');
$.ajax({
type: 'GET',
url: '/users/find',
data: {user_id: user},
dataType: 'json'
})
.done(function(user){
appendAddUserToHTML(user);
})
.fail(function(){
alert('ユーザーを追加できませんでした')
})
})
$(document).on("click", ".chat-group-user__btn--remove", function(e){
$(this.parentNode).remove();
})
});
追加された要素に対してのイベントについては、色々と調べてみると、$(document).on(イベント名, 追加された要素のクラス名またはid, function(){})
という書き方にするとちゃんとイベントが発火するとありました。参考にしたリンクは下に貼らせていただきます。
なぜ一度にたくさんの要素が追加されてしまったのか
ここでまた先ほどの問題に戻ります。
自分の中で一つ思いついたのは一度のクリックでイベントが複数回同時に発生しているのではないかということです。
そこで先ほどのjsファイルに
$(document).on("click", ".chat-group-user__btn--add", function(e){
e.preventDefault();
console.log("done")
$(this.parentNode).remove();
var user = $(this).data('user-id');
$.ajax({
type: 'GET',
url: '/users/find',
data: {user_id: user},
dataType: 'json'
})
.done(function(user){
appendAddUserToHTML(user);
})
.fail(function(){
alert('ユーザーを追加できませんでした')
})
})
このようにconsole.logで検証してみることに。すると...
console.logが一度のイベントで二回表示されています。これで仮説が立ちました。
間違っていたところ
仮説は立ったものの肝心の原因がわからず...
悩んだ末、質問をさせていただいたところ、$(document).on(イベント名, 追加された要素のクラス名またはid, function(){})
のセレクタに問題があるのではないかとのことでした。
そもそも自分はdocument
についてよくわかっていなかったというのもあるのですが、セレクタにdocument
を指定すると誤作動を起こす可能性があるそうです。
ちゃんと調べてみると、document
というのはそのページ全てのDOMの親のようなもので、それを指定してしまうとあまりにセレクタの範囲が広いため、同じクラスなどがあった場合に誤作動をしてしまうと解釈しました。
解決方法
以上のことを踏まえて、以下のような変更を行いました。
$('.chat-group-search-user').on("click", ".chat-group-user__btn--add", function(e){
e.preventDefault();
$(this.parentNode).remove();
var user = $(this).data('user-id');
$.ajax({
type: 'GET',
url: '/users/find',
data: {user_id: user},
dataType: 'json'
})
.done(function(user){
appendAddUserToHTML(user);
})
.fail(function(){
alert('ユーザーを追加できませんでした')
})
})
セレクタを追加される要素の直近の親要素を指定してあげることで、無事解決しました。
まとめ
今回つまづいたことでわかったことは、セレクタには追加要素の親要素を指定してあげる
こと、追加要素に対して安易にdocumentを使うと想定外の動きをする
ということ、自分の場合追加要素から一番近く、最初から存在している親要素を指定してあげると誤作動なくうまくいく
ということです。
もしこの記事が自分と同じような問題に悩んでいる人の力になれば嬉しい限りです。また、当方実務経験のない初学者のため何か間違いなどあればご指摘いただけると大変嬉しいです。
ここまでご覧くださりありがとうございます。
参考にさせていただいた記事
https://qiita.com/ayies128/items/5d044bc08b9308767f4c
https://qiita.com/negi/items/6ec0d3cedba499eac81a