質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.91%

jQuery のクリックイベントの多重処理を防ぐ方法について

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,534

airulove

score 35

jQuery のクリックイベントの多重処理を防ぐ方法についてご教授お願いしたく、この場を借りて質問させて頂きました。

タイトル通り、クリックイベントの多重処理を防ぎたいのですが、まずは、問題のソースコードのサンプルをご用意しました。

<table id="priceData">
  <tr>
    <th>名前</th>
    <th>価格</th>
  </tr>
  <tr id="rowData">
    <td><input type="text" value="商品1"></td>
    <td><input type="text" value="2980"></td>
  </tr>
</table>

商品名の価格を設定するテーブルがあります。ボタンを押すと行が追加される仕組みになっています。
行を追加するプログラムは以下の通りです。

<input type="button" id="appendRowButton" value="行追加">
function appendRow(values) { // values = ['商品%i%', '0']
  var dom = '';

  ~行数を数える処理~
  ~valuesに%i%という文字列があった場合は、それを数えた行数に置換する~

  // 行追加
  dom += '<tr id="rowData">';
  for (var i = 0; i < values.length; i++) {
    dom += '<td><input type="text" value="<%- values[i] %>"></td>';
  }

  var template = _.template(dom);
  $('#priceData').append(template({
    values : values
  }));
}

商品名列の値の 「%i%」 について、
[id=rowData] のID数を数えて、%i%をID数に置換します。

テーブルの行をクリックしたら、その行を青色で塗りつぶし、選択した行のデータと行番号を変数に記憶するようにしています。

現行ですと、以下のような形になっています。

$(function () {
  setEvent();

  // 行追加ボタンをクリックした場合
  $('#appendRowButton').click(function () {
    setEvent();
  });
});
// 行のどこかをクリックした場合
function setEvent() {
  $('#priceData').children('tbody').children('[id=rowData]').click(function () {
    $('#priceData').children('tbody').children('[id=rowData]').each(function (index, element) {
      クリックした行を青色で塗りつぶし、選択した行のデータと行番号を変数に記憶する
      それ以外の行は白色で塗りつぶす。
    });
  });
}

DOM生成完了時にsetEvent()を呼び出していますが、行を追加した後にもう一度setEvent()を呼び出さないと追加した行に対してイベントが発生しないのです。なので、現状では追加する度にsetEvent()を呼び出しています。

ただ、行を追加すればするほど、行数分setEvent()を繰り返すので、処理の負担も増えます。出来れば、最初のsetEvent()だけでイベント処理を行いたいのですが、動的に追加されたDOMに対するクリックイベントは .on を使うしかないのです。
セレクタ―も機能しないので、 .each も使えないので、現状では行を追加する度に setEvent() を呼び出す方法が一番の解決策となっています。

理想としては、
行を追加したら、既に呼び出されている setEvent() を消して、新たに setEvent() を呼び出すようにしたいのですが。

もっといい方法があれば是非教えて頂きたいです。

以上よろしくお願い致します。

実際の処理画面は以下URLにあります。
座標計算 | Coordinate calculation

以下、実際のソースコード

*** coordinateCalculation-activity.js ***

$(function () {

    // コンストラクターを生成
    var listView1 = new ListView(['Name', 'X', 'Y'], 'listView1', $('#dispListView1'));

    listView1.appendRow(['Name%i%', '0', '0']);
    listView1.setListViewEvent();

    // 追加ボタンをクリックした場合
    $('#addButton').click(function () {
        listView1.appendRow(['Name%i%', '0', '0']);
        listView1.setListViewEvent();
        Calculate(listView1.getAllData(['name', 'x', 'y']));
        dispSelectedPointDetail(listView1, 'pointer1', listView1.id, ['name', 'x', 'y']);
    })

    // 削除ボタンをクリックした場合
    $('#delButton').click(function () {
        listView1.removeRow(listView1.selectedRowData);
        Calculate(listView1.getAllData(['name', 'x', 'y']));
        dispSelectedPointDetail(listView1, 'pointer1', listView1.id, ['name', 'x', 'y']);
    });

    // 実行ボタンをクリックした場合
    $('#actButton').click(function () {
        Calculate(listView1.getAllData(['name', 'x', 'y']));
        dispSelectedPointDetail(listView1, 'pointer1', listView1.id, ['name', 'x', 'y']);
    });

    // サブミットした場合
    $('#listView1Form').submit(function () {
        Calculate(listView1.getAllData(['name', 'x', 'y']));
        dispSelectedPointDetail(listView1, 'pointer1', listView1.id, ['name', 'x', 'y']);
    });
    $('#listView2Form').submit(function () {
        Calculate(listView1.getAllData(['name', 'x', 'y']));
        dispSelectedPointDetail(listView1, 'pointer1', listView1.id, ['name', 'x', 'y']);
    });

    ~省略~

});

~省略~
*** list-view.js ***

var ListView = function (columns, id, selector) {

    // 変数を定義
    this.columns = columns;
    this.id = id;
    this.selector = selector;
    this.selectedRowData;
    this.selectedRowIndex = 0;

    // メソッドを定義
    this.appendRow = ListView.appendRow;
    this.removeRow = ListView.removeRow;
    this.getCellData = ListView.getCellData;
    this.getAllData = ListView.getAllData;
    this.setListViewEvent = ListView.setListViewEvent;

    // ListViewを設置
    ListView.setListView(columns, id, selector);

};

// *****************************************************************************
// ListViewを設置
// *****************************************************************************
ListView.setListView = function (columns, id, selector) {

    var dom = '';

    dom += '<table class="list-view" id="' + id + '">';

    // ヘッダー行を生成
    dom += '<tr class="-user-select-none">';

    for (var i = 0; i < columns.length; i++) {

        dom += '<th>' + columns[i] + '</th>';

    }

    dom += '</tr>';

    dom += '</table>';

    var template = _.template(dom);

    selector.prepend(template());

}

// *****************************************************************************
// 行を追加
// *****************************************************************************
ListView.appendRow = function (defaultValues) {

    var dom = '';

    // 行数を定義
    var rowLength = $('#' + this.id).children('tbody').children('[id=listViewRowData]').length;

    // 行を追加
    dom += '<tr id="listViewRowData">';
    for (var i = 0; i < this.columns.length; i++) {

        // テキストの初期値にパラメータをセット
        var regExp = new RegExp('%i%', 'g');
        var defaultValue = defaultValues[i].replace(regExp , rowLength);

        dom += '<td>';
        dom += '<input type="text" id="listViewColumnValue" value="' + defaultValue + '" spellcheck="false">';
        dom += '</td>';

    }
    dom += '</tr>';

    var template = _.template(dom);

    $('#' + this.id).append(template());

}

// *****************************************************************************
// 選択した行を削除
// *****************************************************************************
ListView.removeRow = function (id) {

    var parentThis = this;

    id.remove();

    $('#' + parentThis.id).children('tbody').children('[id=listViewRowData]').each(function (index, element) {

        if (index == parentThis.selectedRowIndex) {
            $(this).attr('class', 'selected');
            parentThis.selectedRowData = $(this);
            parentThis.selectedRowIndex = $('#' + parentThis.id + ' [id=listViewRowData]').index(this);
        }

    });

}

~省略~

// *****************************************************************************
// ListViewにイベントを設定
// *****************************************************************************
ListView.setListViewEvent = function () {

    var parentThis = this;

    $('#' + parentThis.id).children('tbody').children('[id=listViewRowData]').click(function () {
        var clickThis = this;
        $('#' + parentThis.id).children('tbody').children('[id=listViewRowData]').each(function (index, element) {

            if (index != $('#' + parentThis.id).children('tbody').children('[id=listViewRowData]').index(clickThis)) {
                $(element).attr('class', '');
            }

        });
        $(this).attr('class', 'selected');
        parentThis.selectedRowData = $(this);
        parentThis.selectedRowIndex = $('#' + parentThis.id).children('tbody').children('[id=listViewRowData]').index(this);
    });

}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

下記のようにすれば後から追加した要素にもクリックイベントが効きます。

// tr が後から増えても増えた tr にもクリックイベントが効く
$( '#priceData' ).on( 'click', 'tr', function() {} );

【実践、jQuery - .on()と.off()を使いこなす 1 | CodeGrid】
https://app.codegrid.net/entry/practical-jquery-1


また、HTMLファイル上のidは重複が許されないため、そもそも問題があります。
'<tr id="rowData"> が複数になるなら、 '<tr class="rowData"> などに置き換える必要があります。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/05/07 19:42

    とりあえず仕様どおりにされることをお勧めはします。

    【HTML 5.1: 3. Semantics, structure, and APIs of HTML documents】
    https://www.w3.org/TR/html51/dom.html#the-id-attribute
    > The value must be unique amongst all the IDs in the element’s home subtree and must contain at least one character. The value must not contain any space characters.

    キャンセル

  • 2017/05/07 19:44

    $('[id=id]')を使えば、配列として処理してくれるので、クリックした行が何行目なのかも判断できるのです。
    classを使えば、$('.class')でも配列として処理してくれますけどね。昔、セレクタとして使うなら、idを使い、スタイルはclassを使え、と言われた事があったので、重複していようがidを使っていたのです。

    キャンセル

  • 2017/05/07 19:46

    でも、文法上よくないみたいなので、classを使って見たいと思います。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.91%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る