Always select a parent element when clicking on a child

I have a div (a tab) with 3 span inside, like this:

<div class="chat-tabs">
    <div class="chat-tabs-cont">
        <div id="chat-tab-1" class="chat-tab chat-tab-sel">
            <span class="chat-tab-n">1</span>
            <span class="chat-tab-t">Tab text 1</span>
            <span class="chat-tab-c">11:00</span>
        </div>
        <div id="chat-tab-2" class="chat-tab">
            <span class="chat-tab-n">2</span>
            <span class="chat-tab-t">Tab text 2</span>
            <span class="chat-tab-c">11:30</span>
        </div>
    </div>
</div>

These are tabs, so when I click on one tab, I have a click event in Meteor to give the new tab a class of chat-tab-sel and remove this class from old tab (standard tab behaviour).

My problem is that depending where the user clicks, my event.target is not allways the parent div chat-tab, but one of child span. And I need to add/remove classes to the parent div.

I think if the parent has display: block it may work, but in this case I need it to be display: flex because it makes sense to have flexible width on childs.

So: Is it possible to ensure that the parent div is targeted when user clicks on a child?

Upvotes: 2

Views: 2005

Answers (3)

JeremyK
JeremyK

Reputation: 3240

Building on Tiny Giant's answer on events bubbling, here is to do this in meteor:

Template.theTemplate.events({
  'click .chat-tab': function(ev) {
    $('.chat-tab').removeClass('chat-tab-sel'); 
    $(ev.currentTarget).addClass('chat-tab-sel');
  }
});

Here is a interesting info.meteor.com blogpost that goes into some detail on this:

Browser events: bubbling, capturing, and delegation

Suppose you have this HTML structure:

<body>
  <p>
    <a><span>Hello</span></a>
  </p>
</body>

Event delegation is not a browser feature, but a popular technique built into libraries like jQuery. Many blogs get confused talking about it or equate it with bubbling, but I hope the following description is clear.

Suppose you want to respond to a click on any A tag on the page, even if the set of A tags changes over time. In particular, you don't want to visit every A tag and add an event listener. So, taking advantage of bubbling, you bind a single event handler to the BODY, and from this handler you look at event.target to see if an A was clicked, and if so, which one. Be careful, though, because event.target may be the SPAN! You need to not just check if the event's target is an A tag, but also walk up the DOM tree in a simple simulation of bubbling.

This is event delegation. The BODY element is the delegate that handles events on behalf of the A tags. Conceptually, we'd like to think of the event handler as being on the A tags, so we create that illusion as much as we can. To that end, the final step in event delegation (at least in jQuery and Meteor) is to set event.currentTarget to the A tag. Further code that handles the event then sees an A tag as currentTarget and a SPAN tag as target. The BODY element is not really important, so it is nowhere to be found.

Upvotes: 0

Michel Floyd
Michel Floyd

Reputation: 20236

If you use a normal Meteor event handler in combination with @Brian Shamblen's tip it should just work.

Template.myTemplate.events({
  'click .chatTab': function(ev){
    $(".chat-tab").removeClass("chat-tab-sel"); // remove from all
    $(ev.target).closest(".chat-tab").addClass("chat-tab-sel"); // set the one you're on
  }
});

Upvotes: 1

user4639281
user4639281

Reputation:

Events bubble. This means that you can listen for events on the parent. In an event listener this is bound to the element that you bind the event listener to. So we can listen for any clicks inside that element, then set the current active tab to inactive, and the clicked tab to active.

Not sure about Meteor specifically, but this is how I would accomplish this using vanilla JavaScript.

var tabs = document.querySelectorAll('.chat-tab');

for(var i in Object.keys(tabs)) tabs[i].onclick = function() {
    document.querySelector('.chat-tab.chat-tab-sel').className = 'chat-tab';
    this.className += ' chat-tab-sel'
}
.chat-tab-sel {
  border: 1px solid #012450;
}
<div class="chat-tabs">
    <div class="chat-tabs-cont">
        <div id="chat-tab-1" class="chat-tab chat-tab-sel">
            <span class="chat-tab-n">1</span>
            <span class="chat-tab-t">Tab text 1</span>
            <span class="chat-tab-c">11:00</span>
        </div>
        <div id="chat-tab-2" class="chat-tab">
            <span class="chat-tab-n">2</span>
            <span class="chat-tab-t">Tab text 2</span>
            <span class="chat-tab-c">11:30</span>
        </div>
    </div>
</div>

Upvotes: 0

Related Questions