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

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

新規登録して質問してみよう
ただいま回答率
85.48%
CSS3

CSS(Cascading Style Sheet)の第3版です。CSS3と略されることが多いです。色やデザインを柔軟に変更することが可能になります。

HTML5

HTML5 (Hyper Text Markup Language、バージョン 5)は、マークアップ言語であるHTMLの第5版です。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

5回答

4430閲覧

JavaScriptの一部クリックイベントが登録されない

okame

総合スコア54

CSS3

CSS(Cascading Style Sheet)の第3版です。CSS3と略されることが多いです。色やデザインを柔軟に変更することが可能になります。

HTML5

HTML5 (Hyper Text Markup Language、バージョン 5)は、マークアップ言語であるHTMLの第5版です。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

1クリップ

投稿2017/11/08 07:21

編集2017/11/08 07:43

###前提・実現したいこと
タイトルの通りで、一部のクリックイベントが正常に登録されず動作しません。

あるシステムを作っていて、その中で管理者を表に一覧で表示する画面があります。
表の一番右には削除ボタンがあって、それをクリックすると確認画面モーダルウィンドウが表示されます。
さらにそのモーダル内の削除するボタンをクリックすると削除を実行する(ajaxでGET投げる→PHP処理)としたいのですが、
いかんせんイベント自体が動作していないようなので先に進みません。

###発生している問題・該当のソースコード

html

1<!DOCTYPE html> 2<html> 3<head> 4 <meta charset="utf-8"> 5 <meta name="viewport" content="width=device-width"> 6 <title>JS Bin</title> 7</head> 8<body> 9 <ol class="breadcrumb"> 10 <li class="active">Home</li> 11 <li class="active">管理者一覧</li> 12 </ol> 13 14 <nav aria-label="..."> 15 <ul class="pager"> 16 <li class="previous"><a onclick="history.back()"><span aria-hidden="true">&larr;</span>戻る</a></li> 17 </ul> 18 </nav> 19 20 <h4 class="page-header">管理者一覧</h4> 21 22 <div style="width:100%;height:300px;overflow:auto;"> 23 <table class="table table-striped table-hover"> 24 <thead> 25 <tr> 26 <th style="width:3%">#</th> 27 <th style="width:10%">ユーザー名</th> 28 <th style="width:15%">名前</th> 29 <th>備考</th> 30 <th style="width:8%">操作</th> 31 </tr> 32 </thead> 33 <tbody> 34 {% for manager in managers %} 35 <tr> 36 <td>{{ manager.id }}</td> 37 <td>{{ manager.user_name }}</td> 38 <td>{{ manager.last_name }} {{ manager.first_name }}</td> 39 <td>{{ manager.remarks }}</td> 40 <td> 41 <button class="btn btn-default btn-sm" value="{{ manager.id }}">編集</button> 42 <button class="btn btn-default btn-sm delete" value="{{ manager.id }}">削除</button> 43 </td> 44 </tr> 45 {% endfor %} 46 </tbody> 47 </table> 48 </div> 49 50 <button class="btn btn-primary button-center">管理者を追加する</button> 51 52 <div id="modalDelete"> 53 <p></p> 54 <button class="btn btn-primary button-center">削除する</button> 55 </div> 56 57<script src="https://code.jquery.com/jquery.min.js"></script> 58<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" type="text/css" /> 59<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script> 60</body> 61</html>

css

1/* モーダルウィンドウで表示する要素(通常時は非表示) */ 2#modalDelete { 3 display: none; 4} 5/* オーバーレイ要素のスタイル */ 6#modalOverlay { 7 top: 0; 8 left: 0; 9 width: 100%; 10 position: fixed; 11 z-index: 97; 12 background: #000; 13 display: none; 14} 15/* モーダルウィンドウ全体のスタイル */ 16#modalWindow { 17 margin-top: -200px; 18 margin-left: -170px; 19 top: 50%; 20 left: 50%; 21 width: 350px; 22 height: 200px; 23 position: fixed; 24 z-index: 98; 25 background: #fff; 26 display: none; 27} 28/* モーダルウィンドウ内の閉じるボタンのスタイル */ 29#modalWindow > .modalClose { 30 top: -15px; 31 right: -15px; 32 width: 30px; 33 height: 30px; 34 line-height: 30px; 35 color: #fff; 36 font-size: 1.5em; 37 background: #757575; 38 border-radius: 30px; 39 box-shadow: 0 0 3px 0 #000; 40 position: absolute; 41 z-index: 99; 42 cursor: pointer; 43 text-align: center; 44} 45/* コンテンツエリアのスタイル */ 46#modalContents { 47 margin: 30px auto; 48 padding: 0 20px; 49 width: 100%; 50 height: 340px; 51 box-sizing: border-box; 52 overflow-y: auto; 53} 54#modalContents p { 55 padding-bottom: 2em; 56 font-size: 1em; 57 text-align: center; 58 margin-top: 30px; 59}

javascript

1// for manager function 2(function () { 3 // 削除前確認画面モーダルウィンドウの表示処理 4 document.querySelectorAll('.delete').forEach(function (deleteButton) { 5 deleteButton.addEventListener('click', function() { 6 // 取得前にモーダル内削除ボタンに必要な値を設定 7 var modalDeleteButton = document.querySelector('#modalDelete button'); 8 modalDeleteButton.setAttribute('id', 'execDelete'); // 削除実行時のトリガーとしてidをセット 9 // execDelete(); 10 modalDeleteButton.setAttribute('value', deleteButton.getAttribute('value')); // 削除するmanager.id 11 12 var td = deleteButton.parentNode.parentNode.querySelector('td:nth-child(2)'); 13 document.querySelector('#modalDelete p').textContent = td.textContent + ' を削除しますか?'; 14 15 // モーダルウィンドウ内の表示要素を取得 16 var source = document.querySelector('#modalDelete').innerHTML; 17 18 // bodyタグ直前にオーバーレイ要素とモーダルウィンドウ要素を挿入 19 var overlay = document.createElement('div'); 20 overlay.setAttribute('id', 'modalOverlay'); 21 22 var modalWindow = document.createElement('div'); 23 modalWindow.setAttribute('id', 'modalWindow'); 24 modalWindow.innerHTML = 25 '<div class="modalClose">×</div>' 26 + '<div id="modalContents">' 27 + source 28 + '</div>'; 29 30 document.body.appendChild(overlay); 31 document.body.appendChild(modalWindow); 32 33 // 非表示にしていたオーバーレイ要素とモーダルウィンドウ要素をフェードイン表示 34 var modalElements = document.querySelectorAll('#modalOverlay, #modalWindow'); 35 modalElements.forEach(function (element, index) { 36 element.style.display = 'block'; 37 element.style.opacity = 0; 38 39 if (index === 0) { // to overlay 40 element.style.height = window.innerHeight + 'px'; 41 element.style.opacity = 0.7; 42 43 } else if (index === 1) { 44 element.style.opacity = 1; 45 } 46 }); 47 48 // ブラウザウィンドウの高さ変更に合わせてオーバーレイ要素の高さを調整する 49 window.addEventListener('resize', function() { 50 var modalOverlay = document.getElementById('modalOverlay'); 51 if (modalOverlay.style.display === 'block') { 52 modalOverlay.style.height = window.innerHeight; 53 } 54 }, false); 55 56 // モーダル閉じるボタンorオーバーレイをクリック時にモーダルウィンドウを閉じる 57 document.querySelectorAll('#modalOverlay, .modalClose').forEach(function (clickElement) { 58 clickElement.addEventListener('click', function() { 59 document.querySelectorAll('#modalWindow, #modalOverlay').forEach(function (removeElement) { 60 // TODO: add fade out animation... 61 removeElement.parentNode.removeChild(removeElement); 62 }); 63 }, false); 64 }); 65 66 // 削除実行イベント 67 document.getElementById('execDelete').addEventListener('click', function() { 68 console.log('OK'); // <-- モーダル内削除ボタンをクリックしても反応なし。なぜクリックイベントが登録されない?? 69 var ajax = new XMLHttpRequest(); 70 71 ajax.onreadystatechange = function () { 72 if(ajax.readyState === XMLHttpRequest.DONE && ajax.status === 200) { 73 console.log(ajax.responseText); 74 } 75 }; 76 77 ajax.open('GET', '/admin/staff/delete.php?id=' + this.getAttribute('value'), true); 78 ajax.send(null); 79 }, false); 80 }, false); 81 }); 82})();

jsコードの下の方「// 削除実行イベント」とコメントしているところからの
クリックイベント登録コードが該当箇所です。

###テスト環境
テスト環境を以下に用意しました。
https://jsbin.com/cunirez/edit?html,css,js,output

###以上
皆様のお知恵を拝借できれば幸いです。宜しくお願い致します。

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kei344

2017/11/08 07:26

テスト環境だけでなく、コードを提示してください。
okame

2017/11/08 07:29

テスト環境に書いてあるコードがすべてです。
okame

2017/11/08 07:30

質問内にも記載していますが、jsウィンドウ内のコードの下の方「// 削除実行イベント」とコメントしているところからの クリックイベント登録コードが該当箇所です。
kei344

2017/11/08 07:33

テスト環境を見ずともteratail上でコードを確認できるようにしていただけないか、と依頼しているつもりです。
okame

2017/11/08 07:38

そうですか...。以前はteratail上に全部記載していたのですが、ある方から「ずらずらコード載せても分かんないからテスト環境用意するのがマナーだよ!」とご指摘頂いてからはJSBINに書くようにしていました。決して怠けているわけではありません。とにかく、少々お待ち下さい。いま編集しますね!
kei344

2017/11/08 07:46

https://teratail.com/questions/92611 ここでのコメントのことでしょうか。「検証環境も」と書かれているだけで「長い」と指摘されているわけではないと思います。
okame

2017/11/08 07:55

言葉としては書いていませんが、コンテキスト(文脈)からするとそういう意図かなと私は読み取りました。ところでコードをこの質問上に追記しました。いかがでしょうか??
guest

回答5

0

同じid属性値(execDelete)を持つmodalDeleteButtonが複数存在してしまっているからです.

文法上, HTML文書内のid値は常に一意である必要があります. しかるにあなたのコードでは, 途中でモーダルウィンドウの内容をテンプレートからコピーする過程で, 同一文書内に同じidexecDeleteを持つノードが複数作られてしまっています.

すると後続のgetElementByIdメソッドは先頭execDeleteノードを見つけてきますが, このノードはテンプレート内のノードであるため, ここにイベントリスナ関数を登録しても意味がないことになります.

JavaScript

1// for manager function 2(function () { 3 // 削除前確認画面モーダルウィンドウの表示処理 4 document.querySelectorAll('.delete').forEach(function (deleteButton) { 5 deleteButton.addEventListener('click', function() { 6 // 取得前にモーダル内削除ボタンに必要な値を設定 7 var modalDeleteButton = document.querySelector('#modalDelete button'); 8 modalDeleteButton.setAttribute('id', 'execDelete'); // 削除実行時のトリガーとしてidをセット 9 // execDelete(); 10 modalDeleteButton.setAttribute('value', deleteButton.getAttribute('value')); // 削除するmanager.id 11 12 var td = deleteButton.parentNode.parentNode.querySelector('td:nth-child(2)'); 13 document.querySelector('#modalDelete p').textContent = td.textContent + ' を削除しますか?'; 14 15 // モーダルウィンドウ内の表示要素を取得 16 var source = document.querySelector('#modalDelete').innerHTML; 17 18/* 19//例えばここに次のコードを挿入するととりあえず動く 20modalDeleteButton.removeAttribute('id'); 21*/ 22 23 // bodyタグ直前にオーバーレイ要素とモーダルウィンドウ要素を挿入 24 var overlay = document.createElement('div'); 25 overlay.setAttribute('id', 'modalOverlay'); 26 27 var modalWindow = document.createElement('div'); 28 modalWindow.setAttribute('id', 'modalWindow'); 29 modalWindow.innerHTML = 30 '<div class="modalClose">×</div>' 31 + '<div id="modalContents">' 32 + source 33 + '</div>';//←★★★ここでid値が重複するノードが挿入されてしまっている! 34 35 document.body.appendChild(overlay); 36 document.body.appendChild(modalWindow); 37 38 // 非表示にしていたオーバーレイ要素とモーダルウィンドウ要素をフェードイン表示 39 var modalElements = document.querySelectorAll('#modalOverlay, #modalWindow'); 40 modalElements.forEach(function (element, index) { 41 element.style.display = 'block'; 42 element.style.opacity = 0; 43 44 if (index === 0) { // to overlay 45 element.style.height = window.innerHeight + 'px'; 46 element.style.opacity = 0.7; 47 48 } else if (index === 1) { 49 element.style.opacity = 1; 50 } 51 }); 52 53 // ブラウザウィンドウの高さ変更に合わせてオーバーレイ要素の高さを調整する 54 window.addEventListener('resize', function() { 55 var modalOverlay = document.getElementById('modalOverlay'); 56 if (modalOverlay.style.display === 'block') { 57 modalOverlay.style.height = window.innerHeight; 58 } 59 }, false); 60 61 // モーダル閉じるボタンorオーバーレイをクリック時にモーダルウィンドウを閉じる 62 document.querySelectorAll('#modalOverlay, .modalClose').forEach(function (clickElement) { 63 clickElement.addEventListener('click', function() { 64 document.querySelectorAll('#modalWindow, #modalOverlay').forEach(function (removeElement) { 65 // TODO: add fade out animation... 66 removeElement.parentNode.removeChild(removeElement); 67 }); 68 }, false); 69 }); 70 71 // 削除実行イベント 72 //NOTE:↓のgetElementByIdは先頭のexecDeleteを参照してしまっているので 73 //実際のモーダルウィンドウ上のボタンは無反応となる 74 document.getElementById('execDelete').addEventListener('click', function() { 75 console.log('OK'); // <-- モーダル内削除ボタンをクリックしても反応なし。なぜクリックイベントが登録されない?? 76 var ajax = new XMLHttpRequest(); 77 78 ajax.onreadystatechange = function () { 79 if(ajax.readyState === XMLHttpRequest.DONE && ajax.status === 200) { 80 console.log(ajax.responseText); 81 } 82 }; 83 84 ajax.open('GET', '/admin/staff/delete.php?id=' + this.getAttribute('value'), true); 85 ajax.send(null); 86 }, false); 87 }, false); 88 }); 89})();

さて, この問題を解決するには次の何れかを用います.

  1. id属性値が重複しないようにコードを記述する

付け焼き刃的ですが, テンプレートのソースコードを入手したら, id属性を削除してしまう方法です. こうすると後続のgetElementByIdメソッドが正しいボタンノードを検索してくるはずです.
0. template要素を使ってモーダルウィンドウ内のコンテンツの内容をメイン文書から分離する
提示のコードのように何らかのHTML構造の「雛形」が必要な場合は, 専用のtemplate要素を用いると便利です.

参考)
https://developer.mozilla.org/ja/docs/Web/HTML/Element/template

投稿2017/11/08 08:29

編集2017/11/08 08:58
defghi1977

総合スコア4756

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

okame

2017/11/08 08:32

回答ありがとうございます! ...すでに同じご指摘を別の方からいただき本事象は解決しベストアンサー設定済みでした。 ご協力いただき感謝いたします。
defghi1977

2017/11/08 08:37

次からは「問題の内容」だけでなく「あなたはどのような考えでそのコードを記述したのか」も書いて下さい. 今回は「モーダルウィンドウのテンプレートを作り, 都度その内容をコピーしている」のがネックとなっているわけで, これがあると無しとではコードリーディングを行う際の効率が違います.
okame

2017/11/08 08:40

失礼しました。 以後気をつけたいと思います。
guest

0

ベストアンサー

まずここが問題です。

JavaScript

1 var modalDeleteButton = document.querySelector('#modalDelete button'); 2 modalDeleteButton.setAttribute('id', 'execDelete'); // 削除実行時のトリガーとしてidをセット 3 // execDelete(); 4 modalDeleteButton.setAttribute('value', deleteButton.getAttribute('value')); // 削除するmanager.id

ここであなたは「雛形として使うほう」の#modalDeleteに入っているボタンにidを割り当てています。その後、#modalWindowの中に埋め込むためにinnerHTMLをsourceとして取り出していますね。

JavaScript

1 var source = document.querySelector('#modalDelete').innerHTML;

ここでコピーされているsourceにはidとしてexecDeleteが割り当て済のbuttonがコピーされていることになります。そして、このまま#modalWindowをdocument.bodyにappendすると、button#execDeleteがコード上に2つ存在する状態になります。
もうおわかりですね?idが同じエレメントがdocument上に2つあれば、getElementByIdで得られるエレメントは「先に見つかったほう」です。つまりあなたがボタンにclickを登録しようとしているのはずっと「雛形」のほうのボタンに登録しているのです。

そもそもidがだぶること自体非推奨な状況ですが、すぐ直したいならdocument.getElementById('execDelete')のかわりにdocument.querySelector('#modalWindow #execDelete')とすれば、雛形からコピーされたほうの#execDeleteボタンにclickイベントが登録されます。

投稿2017/11/08 08:04

masaya_ohashi

総合スコア9206

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

okame

2017/11/08 08:21

回答ありがとうございます! そして、ご指摘いただいた箇所を修正し無事に解決しましたー!!自分では絶対気づけなかったと思います。。。 本当に助かりました☆m(_ _)m
guest

0

document.getElementById('execDelete')にイベントを定義しようとしている段階では、document.getElementById('execDelete')自体が存在しないためイベントが定義出来ず、削除も実行されない、という状況のようです。

モーダルを開いてから(document.getElementById('execDelete')が出現してから)、同削除イベントを定義してみるのはどうでしょうか?

投稿2017/11/08 07:48

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

okame

2017/11/08 07:51

回答ありがとうございます! 私もそれを疑ったので、既にモーダル開いた後の箇所に該当コード記載済みです。 それでも解消しないという状況だったのでteratailで質問させていただきました。
guest

0

統合環境でテストするか、もう少し簡単な仕組みでテストされる方がよいでしょう。
またテストしたブラウザも記載が必要です。
(たとえばIEはNodeListに対してforEachが発行できなかったりします)
とりあえずはローテクでconsole.logをベタベタ貼るだけでもやらないよりはマシです。

見た感じ提示されたソースもテンプレかなにかわかりませんが挙動が確認できません。

投稿2017/11/08 07:43

yambejp

総合スコア114784

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

okame

2017/11/08 07:54

回答ありがとうございます! テストした環境載せ忘れましたm(_ _)m 追記します! ちなみにテスト環境では動作が再現されるのですが、JSBinで挙動が確認できないということでしょうか? [テスト環境手順] ①プレビュー画面のテーブル一番右側の「削除」ボタンを押す ②モーダルウィンドウが表示されるので、その中の「削除する」ボタンをクリックすると、何も動作しない(window.alert()を仕込んでいるのに)。
okame

2017/11/08 07:57

ちなみに質問前に、ローテクでconsole.log()をベタベタ貼って、質問の説明にも書いている「// 削除実行イベント」以下のコードのみ正常に動作していないことを確認済みです。
yambejp

2017/11/08 08:04

とりあえずjsの 「(function () {」はじまり「})();」おわりの部分を window.addEventListener('DOMContentLoaded', function(e){ }); に切り替えてみてはどうでしょう
okame

2017/11/08 08:25

別の方の回答にて本事象は解決しました。 ご協力いただきありがとうございました!
okame

2017/11/08 08:25

別の方の回答にて本事象は解決しました。 ご協力いただきありがとうございました!
guest

0

とりあえず、下記の書き方では後から追加された項目にはイベントがつきません。後から追加された要素のイベントを取り扱う方法をまず調査されたほうが良いと思います。

JavaScript

1document.querySelectorAll('.delete').forEach(function (deleteButton) { 2 deleteButton.addEventListener('click', function() {}); 3});

jQueryで説明されたものが多いような。

【jQueryのclickとbindとliveとdelegateとonの違い - Qiita】
https://qiita.com/smzk/items/5eed5a90c4b32ca8b23a

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

【jQueryを使わずjavascriptだけで書き直した際の記法メモ - Qiita】
https://qiita.com/moriyaman/items/3b3f7878f8ecc2b76372#liveon-event


修正履歴:「(モーダルのボタンについても同じ問題です)」という部分を削除

投稿2017/11/08 08:19

編集2017/11/08 08:24
kei344

総合スコア69400

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

okame

2017/11/08 08:23

別の方の回答にて本事象は解決しました。 お手数おかけしましたがご協力いただきありがとうございました!
kei344

2017/11/08 08:25

とりあえずイベントの扱いも見直したほうがよいですよ。
okame

2017/11/08 08:30

わかりました! ご丁寧に追加コメントありがとうございます!!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問