I am struggling to understand how Meteor adds event bindings to templates.
I have a template with multiple anchors for the navigation dropdowns:
<template name="user_loggedin">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Select a profile..
<b class="caret"></b></a>
<ul class="dropdown-menu">List</ul>
</li>
<li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">
<i class="glyphicon glyphicon-cog"></i>
<b class="caret"></b></a>
<ul class="dropdown-menu"> </ul>
</li>
</template>
And I try to bind a click event to show the dropdown menus:
Template.user_loggedin.events({
"click a.dropdown-toggle": function(e,tml) {
$(e.target).siblings('.dropdown-menu').toggle()
}
})
However, the event only seems to bind to the first anchor element, not all of those matching the 'a.dropdown-toggle' selector.
The same problem occurs for templates containing dynamic elements derived from collections. I'd just assumed the Meteor template events method would work in the same way as $('a.dropdown.menu').on(...
I suspect this is to do with Meteor not having rendered all the template's DOM elements before the events are bound. I've seen solutions using Meteor.template.rendered to bind events after rendering, but this seems messy considering Meteor are deprecating rendered method in the next release.
Is there another way?
No. The event handler is specified for the template and is not "bound" to an element. So your event handler will definitely be executed if any anchor element with class dropdown-toggle is clicked.
The problem you are experiencing is probably caused by the use of e.target instead of e.currentTarget in your event handler. From the Meteor docs:
target
The element that originated the event.
currentTarget
The element currently handling the event.
This is the element that matched the selector in the event map. For
events that bubble, it may be target or an ancestor of target, and its
value changes as the event bubbles.
In the first case both e.target and e.currentTarget are a.dropdown-toggle and everything works as expected. In the second case, however, e.target is i.glyphicon.glyphicon-cog while e.currentTarget is a.dropdown-toggle (which is what you want to have).
So please try to edit your event handler to look like this:
Template.user_loggedin.events({
"click a.dropdown-toggle": function(e,tml) {
$(e.currentTarget).siblings('.dropdown-menu').toggle()
}
});
About your second problem with the dynamic elements: can you provide an example?
Related
I need to attach an onClick="" event to a Vue component. I am aware that I can use #click="", but since this div is later going to be cloned (I am using a slider library that clones the divs). I cannot use #click="", since the original event handler will not be cloned (apparently this is not possible).
How can I use a vanilla onClick event handler inside a component, while also referring to the component's method? Usually I'd write:
<a href="#" #click="nextSlide()">
But I cannot use:
<a href="#" onClick="this.nextSlide()">
Since "nextSlide" will then not be defined.
Given ul list defined as below:
<ul click.delegate="onListItemClick()">
<li repeat.for="suggestion of suggestions">
${suggestion.name}
</li>
</ul>
How do I pass suggestion object to onListItemClick? I know I can put click.delegate on each individual li element and then capture current suggestion but won't it go against event delegation idea? I mean - then I will have multiple event handlers attached, and if so I could just go with click.trigger. I don't want to do this, because there may be tons of these suggestion objects.
Put the click handler on the li element. That's the whole point of event delegation with Aurelia. It allows you to do stuff just like this without the overhead of creating a bunch of event handlers. There's just one event handler that is created by the framework for you. It delegates the calls. All of the elements click event will be set to the same function and Aurelia will handle calling your VM method.
Here's a gist.run: https://gist.run/?id=406bf3bc73e415db7afa7d46d7e958d3
<template>
You clicked suggestion id "${clickedId}"
<ul>
<li repeat.for="suggestion of suggestions" click.delegate="handleClick(suggestion)">
${suggestion.name}
</li>
</ul>
</template>
In the MouseEvent class there are multiple *Target events:
target
currentTarget
relatedTarget
What is their purpose in the context of a MouseEvent?
These properties are equivalent to the JavaScript mouse events. JavaScript events traverse the DOM (called "bubbling"). target is the object on which the event was originally dispatched on. currentTarget is the object on which your event handler has been attached.
Example
You have this HTML structure:
<ul id="list">
<li>Entry 1</li>
<li>Entry 2</li>
</ul>
and you add a click handler to the <ul> element (either via JavaScript or Dart, the concept is the same).
When you then click on "Entry 2", your click handler will be called (because the event "bubbles up" to it). target will be the <li> element, while currentTarget will be the <ul> element. Which one you have to use depends on what you want to do in your handler - for example, you could hide "Entry 2" itself by using target, or the whole list by using currentTarget.
The element referenced by relatedTarget depends on the type of your MouseEvent - a list can be found here: event.relatedTarget. In the above example, it would be null, because click events don't have any related target.
Related MDN links:
event.currentTarget,
event.target
I have an application with a nested list I am trying to manage with backbone. there is a view for each individual <li> element, but each list element has it's own nested <ul> tag, which has its own view instantiated. here is an example:
HTML
<ul class='elements'>
<li class='element'>
<div>
<a class='edit-element'>Edit</a>
</div>
<ul class='elements'>
<li class='element'>
<div>
<a class='edit-element'>Edit</a>
</div>
</li>
</ul>
</li>
</ul>
JavaScript
element_view = Backbone.view.extend({
events: {
'click .edit-element' : 'edit_element'
},
edit_element : function(event) {
//code to handle editing of an element
}
});
The issue is that if I click on a child element, both views will fire there edit_element event, which is problematic for several reasons. How can I structure this so that when I click a link in a child <li>, it fires for that <li>, and not any <li>s that contain it?
Backbone's event handlers are just plain old jQuery event handlers so you can stop propagation in the usual ways using stopPropagation:
edit_element: function(event) {
event.stopPropagation()
//...
}
Demo: http://jsfiddle.net/ambiguous/Yb8rg/1/
Or by returning false:
edit_element: function(event) {
//...
return false;
}
Demo: http://jsfiddle.net/ambiguous/QvaGM/1/
In addition to what "mu is too short" already said, I can think of three ways to solve this. In my personal order of preference:
Use a more-specific selector. In this particular case, click .edit-element:first would probably do the trick, since the edit button comes before the child list's edit button.
Instead of using the Backbone events collection, bind directly to the element you're interested in. So before you render the child lists, do something like this.$(".edit-element").click(_.bind(myClickHandler, this)).
Put code inside the handler function to look at what element was clicked, and see if it's the one you're interested in. The code for this would be a little fiddly, and I don't think this is a particularly good solution, so no sample code. :)
Forewarning: I'm a newbie to JQuery.
I have a collection of links and lists that look something like so. They're dynamically generated by my back end framework...
<ul>
<li>
<a id="link1" class="special" href="#">...</a>
<ul id="list1"> ... </ul>
</li>
<li>
<a id="link2" class="special" href="#">...</a>
<ul id="list2"> ... </ul>
</li>
<li>
<a id="link3" class="special" href="#">...</a>
<ul id="list3"> ... </ul>
</li>
</ul>
I'm trying to learn JQuery and Unobtrustive Javascript, so I want to add a click handler on all the links with the class "special" that will toggle the related lists.
So...
$(document).ready(function() {
$("a.special").click(function() {
id = *magicFunctionToGetOwnID*
$(convertListIDtoLinkID(id)).toggle
})
})
What is the magic function that I need? What about if I want to get other properties of the thing I'm attaching a handler to? Say, in the future, I add a "toggle-target" property and I would want to get that?
Not sure If I get you right, but it looks like you want something like this:
$(function() {
$('ul:first').delegate('a.special', function( event ) {
$(this).next('ul').toggle();
});
});
You don't should bind an event handler to all nodes in a situation like that. It's enough to bind one handler to a shared parent and make use of the event bubbling.
Update
.delegate() does exactly what I described above. It'll bind the event handler to a node. The trick is, that event if that event is triggered on a child node from the delegated node, the events will "reach" our destination by bubbling up the DOM tree (unless explicitly prohibit event bubbling). However, jQuery is nice and will always bind this to the target node (the node which originally received the event).
Have a read: http://api.jquery.com/delegate/
Try $(this).parent().find("ul").toggle(). It is rather self-explanatory.
http://jsfiddle.net/gLu9a/
The way to get an element's own id is with the id property. The element that was clicked is set as this within the handler, so you can use this.id.
That said, this is not an optimal technique. Use DOM relationships, rather than setting ids, wherever you can. This will make your code more flexible and intuitive. In this case, you can use the next method, to get the DOM element that follows the element that was clicked:
$(this).next().toggle();
See the API "traversing" category for more ways of adjusting a selection.