Antonio Romero Oca
Antonio Romero Oca

Reputation: 1245

How to capture an event in the middle

I would like to attach a click event listener and run some action with "data-attr" at this context, as elegant as possible.

<div id="container">

    <div class="group" data-attr="info1">
        <div class="a-casual-class-1">***</div>
        <div class="a-casual-class-2">AAA</div>
    </div>

    <div class="group" data-attr="info2">
        <div class="a-casual-class-1">***</div>
        <div class="a-casual-class-2">AAA</div>
    </div>

</div>

A) Just a single event to the root (#container)

B) I could get first the nodes list of '.group', make a loop and attach the event listener to each element.

Is there a better approach to handle this situation?

Upvotes: 0

Views: 47

Answers (2)

Abhitalks
Abhitalks

Reputation: 28417

A) Just a single event to the root (#container)

If the structure is not too complex, then you could bind the event to the root and check the class of event.target to run your action.

check if event.target is a-casual-class-1 or a-casual-class-2 and get parent element to retrieve the data attribute or retrieve the .group element from the root and the data-attr.

Instead of checking for a-casual-class-1, you could check for the parent's class and then take the action.

For Example:

var target = document.getElementById('container');
target.addEventListener('click', doAction);

function doAction(e) { 
  if (e.target.className === 'group') {
    console.log(e.target.dataset.attr);
  }
  if (e.target.parentElement.className === 'group') {
    console.log(e.target.parentElement.dataset.attr);
  }	
}
#container {
  margin: 12p; padding: 12px;
  border: 4px solid #b33;
}
.group {
  margin: 12px; padding: 12px;
  border: 4px solid #33b; 
}
.group > div { background-color: #ddd; }
<div id="container">
  <div class="group" data-attr="info1">
    <div class="a-casual-class-1">***</div>
    <div class="a-casual-class-2">AAA</div>
  </div>
  <div class="group" data-attr="info2">
    <div class="a-casual-class-1">***</div>
    <div class="a-casual-class-2">AAA</div>
  </div>
</div>

B) I could get first the nodes list of '.group', make a loop and attach the event listener to each element.

If your markup and structure gets complex, then it would be better to iterate through your .groups using querySelectorAll and then bind the event to each one of those. This is what is shown in the other answer.


Bottomline: Just see if your code starts getting too complex depending on the complexity of your markup structure. Also, you need to consider if you have other event handlers bound to the siblings and/or children nested in the #container. In that case, you might find yourself fiddling with capture and/or stopPropagation.

Upvotes: 1

Jai
Jai

Reputation: 74738

A) Just a single event to the root (#container)

You can use document.querySelectorAll() to target all the internal divs.

B) I could get first the nodes list of '.group', make a loop and attach the element.

When you make a selector it will give you an array like object of dom nodes. You have to loop through them and bind the events.

So the overall idea is, as events are tend to bubble up in the DOM, you can bind the event on the #container's child divs. Now whenever you click on the child divs of .group it will always find the div which got the event bound and just executes the callback.

var divs = document.querySelectorAll('#container > div');

// divs.forEach(function(div){ // it worked at chrome 57
[].forEach.call(divs, function(div){ // changed if user is on old version of browser
  div.addEventListener('click', function(e){
    var data = this.dataset.attr;
    console.log(data);
  });
});
<div id="container">
  <div class="group" data-attr="info1">
    <div class="a-casual-class-1">***</div>
    <div class="a-casual-class-2">AAA</div>
  </div>

  <div class="group" data-attr="info2">
    <div class="a-casual-class-1">***</div>
    <div class="a-casual-class-2">AAA</div>
  </div>
</div>

Upvotes: 2

Related Questions