動作環境
Ruby 2.6.5
Rails 6.0.3.2
以前、addEventListenerによって同じ処理が何度も発生していたことにより、エラーが起きてしまい、思うような処理ができないことがあったので、投稿してみました。
##addEventListenerにより重複している処理が発生してしまう例
<% @hugas.each do |huga| %>
<div class="huga" >
<%= huga.content %>
</div>
<% end %>
上記のコードは、hugaのcontentカラムを繰り返し表示させており、その表示されたcontentのclassはhugaであるということを表しています。
function hoge() {
const hugas = document.querySelectorAll(".huga");
hugas.forEach(function (huga) {
huga.addEventListener("click", () => {
//クリックすることで発生する処理
});
});
};
setInterval(hoge, 1000);
function hoge()を1行目として、2行目からこのコードの解説をしていきます。
2行目でclassがhugaである要素をすべてhugasに代入しています。
3行目でhugasを1つずつに分けて、それらの名前をhugaとしています。
4行目でhugaをクリックすると5行目の処理を発生するようにしています。
最終行により、以上の動作を毎秒発生させています。
つまり、上記の2つのコードはcontentをクリックすると、huga.jsの5行目に書かれた処理が発生するということを表しています。
しかし、このままではエラーが起きてしまいます。なぜなら、huga.jsの最終行により毎秒2行目と3行目の動作が発生しているからです。それによって何が起こるのかというと、例えばそのページに遷移してから10秒後にcontentをクリックした場合、5行目の処理が10回同時に発生してしまうというようなことが起きてしまいます。
この問題を解決するために、この記事のタイトルである「addEventListenerにより重複している処理を防ぐ方法」が必要となります。
※そもそもsetIntervalではなく、window.addEventListener('load',hoge);にすれば良いのではという意見が出ると思いますが、その通りです。しかし、index.html.erbのhuga.contentにおいて非同期通信が使われている場合はsetIntervalを使う必要があります。非同期通信だとページのロードが行われないからです。
##addEventListenerにより重複している処理を防ぐ方法
function hoge() {
const hugas = document.querySelectorAll(".huga");
hugas.forEach(function (huga) {
if (huga.getAttribute("baz") != null) {
return null;
}
huga.setAttribute("baz", "true");
huga.addEventListener("click", () => {
//クリックすることで発生する処理
});
});
};
setInterval(hoge, 1000);
重複している処理は、上記のコードの4行目から7行目を追記することで防ぐことができる。なぜなら、この追記によって何秒経過してからcontentをクリックしても、1つのhugaには1回の処理しか行わないという意味になるからです。
追記した部分を詳しく解説していきます。
まず、1秒経過すると1回目の処理が行われ、if文によって条件分岐が起きます。hugaはbazという属性(Attribute)は持っていないのでnullとなり、条件式はfalseとなりますが、falseの場合の処理は記載されていないため、そのまま次の処理に移ります。7行目によって、hugaにbazという属性が与えられ、それはtrueとなります。つまり、1回目の処理は記載する前と変わっていません。
2秒経過した場合を見ていきます。2秒経過すると2回目の処理が行われ、再度if文によって条件分岐が起きます。1回目と違い、hugaにはbazという属性を持っているため、nullではありません。そのため、条件式はtrueとなり、trueの場合の処理が行われ、return nullが実行されます。return nullとは、処理を抜け出すという意味なので、この記載以降の処理は行われなくなります。つまり、2秒経過してから、contentをクリックしても、処理が1回しか行われないようになります。
当然ですが3秒後以降も同じであるため、重複している処理を防ぐことができます。