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

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

ただいまの
回答率

88.63%

ネイティブのJSでデリゲートイベントを実装して子要素の情報を取得したいです

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 3,769

kde

score 29

前提・実現したいこと

現在、jQueryで書かれたコードをネイティブのJSに変換をしています。
その中でonイベントを使ってデリゲート処理を行っている箇所があり、
ネイティブのJSでどのように書いて再現すればよいのかわからず質問させていただきました。

実現したいことは下記です。

  • 下記の#wrapを起点にイベントを監視(.itemは動的に増える可能性があるため)
  • #wrapの子要素をクリックすると、その.itemを起点にして任意の子要素の情報を取得。  
    (item1テキストをクリックしたら、spanタグのid(この場合id1)、aタグのhref属性値(この場合1.html)を取得。
  • 取得した情報をajaxで送信(これは別件なので本質問の対象外です)

jqueryで書かれているのは下記です。

$("#wrap").on('click','.item',function(event) {
  event.preventDefault();
  event.stopPropagation();

  // idを取得
  var id = $(this).find(".child").attr("id");
  // hrefを取得
  var href = $(this).find("a").attr("href");

  console.log(id, href);
});

発生している問題

#wrapをクリックするとイベントターゲットがa要素まで行ってしまい、.itemを起点にすることできません。
どうやればシンプルに実装できるでしょうか。

該当のソースコード

<div id="wrap">
  <div class="item">
    <span class="child" id="id1"><a href="1.html">item1</a></span>
  </div>
  <div class="item">
    <span class="child" id="id2"><a href="2.html">item2</a></span>
  </div>
  <div class="item">
   <span class="child" id="id3"> <a href="3.html">item3</a></span>
  </div>
  <div class="item">
    <span class="child" id="id4"><a href="4.html">item4</a></span>
  </div>
  <div class="item">
    <span class="child" id="id5"><a href="5.html">item5</a></span>
  </div>
</div>

<button id="btn">add</button>
const wrap = document.getElementById('wrap');

wrap.addEventListener('click', function(e) {
  e.preventDefault();
  console.log(e.target); // -> <a href="1.html">item1</a>
 /*
  ここをなんとかしたい
  */
}, false);


// addボタンのイベント登録
const btn = document.getElementById('btn');
let num = 6;
btn.addEventListener('click', function() {
  const item = document.createElement('div');
  item.innerHTML = `<span class="child" id="id${num}"><a href="${num}.html">item${num}</a></span>`;
  item.classList.add('item');
  num++;
  wrap.appendChild(item);
});

お力添えいただけると幸いです。何卒よろしくお願いいたします。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 3

checkベストアンサー

+3

event.target.id === 'wrap' の時だけ処理をスキップさせて下さい。

<div id="wrap">
  <div class="item">
    <span class="child" id="id1"><a href="1.html">item1</a></span>
  </div>
  <div class="item">
    <span class="child" id="id2"><a href="2.html">item2</a></span>
  </div>
  <div class="item">
   <span class="child" id="id3"><a href="3.html">item3</a></span>
  </div>
  <div class="item">
    <span class="child" id="id4"><a href="4.html">item4</a></span>
  </div>
  <div class="item">
    <span class="child" id="id5"><a href="5.html">item5</a></span>
  </div>
</div>

<button id="btn">add</button>

<script>
'use strict';
document.getElementById('wrap').addEventListener('click', function handleClick (event) {
  var item = event.target, currentTarget = event.currentTarget;

  if (item === currentTarget) {
    return;
  }

  while (item && !item.classList.contains('item')) {
    item = item.parentNode
  }

  var child = item.firstElementChild,
      id = child.id,
      href = child.firstChild.href; // 絶対URIがNGなら getAttribute()

  console.log(id, href);
  event.preventDefault();
}, false);
</script>

Re: kde さん

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/06/07 16:41

    早々にありがとうございます!
    event.currentTargetの存在、whileでの祖先要素への移動、これらを知ることができ大変勉強になりました。このコードであればitem内に要素が増えても自分が理想とする形での情報取得が行えると感じました。
    本当にありがとうございます。

    キャンセル

+3

closest()があります。IE が必要なら Polyfill もどうぞ。
https://developer.mozilla.org/ja/docs/Web/API/Element/closest

e.target.closest('.item'); // Element or null.

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/06/07 16:59

    早々にありがとうございます!closest()メソッドの存在知りませんでした。非常に便利そうなのでじっくり見させていただきます。

    キャンセル

0

掴んでからどうしたいのかによりますが、こんな感じでどうでしょうか?

window.onload=function(){
  n2a(document.querySelectorAll('#wrap .item')).forEach(function(i){
    i.addEventListener('click',function(e){
      var t=e.target;
      console.log(t);
      e.preventDefault();
    });
  });
}
function n2a(node){
  if(!NodeList.prototype.forEach){
    node = Array.prototype.slice.call(node,0); 
  }
  return node;
}

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/06/07 16:48

    querySelectorAllは死んだnodeなので、追加した要素には効かないのではないでしょうか

    キャンセル

  • 2017/06/07 16:49

    早々にありがとうございます!少ないコードで実装でき素晴らしいと思いました!
    addボタンをクリックして動的に.itemを生成して増やしたときに再度イベントを定義する必要がありそうでしたが、大変参考になりました。ありがとうございます。

    キャンセル

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

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

関連した質問

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