How to find an object's DOM element in a template with the object's ID?


#1

For example, let’s say that I have a Posts collection and I’m listing the posts in a template:

{{ #each posts }}
  <div class="post">
    {{ text }}
  </div>
{{ /each }}

I can then set a click event. Inside the event’s callback function, this refers to the post object that is associated with the DOM element that I clicked on:

Template.pageName.events = {
  'click .post': function() {
    this; // refers to the post object attached to the div.post that was clicked on
  }
};

So, let’s say I obtain the ID of a post, how do I find its associated DOM element?

posts.observeChanges({
  remove: function(postID) {
    // how do I find the DOM element associated to the postID
  }
});

I know that I could just attach the post’s ID to the DOM element when iterating, like this:

{{ #each posts }}
  <div class="post" id="{{ _id }}">
    {{ text }}
  </div>
{{ /each }}

and then find the post with $('#' + postID)'.

However, I’m wondering if there is a more elegant way to do it.

Clearly, there is an association between the DOM element and the post object that’s made in the {{ #each }} loop since the post object is passed to the event callback, rather than the DOM element. How do I take advantage of this association to find the DOM element from a post’s ID?


#2

The first argument to your event callback is an event object. It has the property currentTarget and target which are DOM nodes associated with the event.

For more details see http://docs.meteor.com/#/full/eventmaps


#3

How would I do it with only the ID though, like in the callback for when a document is removed .


#4

What are you trying to accomplish?


#5

I’d suggest using nested templates for cleaner code.

<template name="postList">
    {{ #each posts }}
      {{>post}}
    {{ /each }}
</template>


<template name="post">
    <div class="post">
        {{ text }}
      </div>
</template>

Then move your click events to the post template.

Template.post.events = {
  'click': function() {
    this; // refers to the post object that was clicked
   this.firstNode; //Should be the <div class="post"> for this post, untested
  }
}

This allows for a more general click event and makes the post template more of a modular component.

This also removes your reliance on IDs which lead to jquery spaghetti. To get dom elements you can then use this.find() within the posts template event or event template.firstNode. See:Template Instances

As for your query about document removal. What do you want to do when a post is removed? Meteor will remove the DOM elements associated with that automatically and efficiently especially with my above suggestions.

I hope i got my point across which is to stop relying on dom ids and instead leverage Blaze’s many features to make modular re-usable templates.

In general i see reliance on ids and using the global jquery method($()) over template level versions (this.$()/this.find()) as a problem. Also {#each} blocks that contain too much markup should be refactored into child templates.


#6

The focus here is getting access to the DOM element for a data item from within the removed event of an observe.


#7

Which i also covered, I was just giving general pointers.


#8

You’re best option is to make use of Meteor’s _uihooks. They are still undocumented, but you can find some clear examples here.


#9

This is what I ended up doing which works nicely. Thanks for the help