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>
Related
I often need to setup jQuery events on li tags that don't have a class or ID associated with them like in this example:
<div id="menu">
<ul id="nav">
<li>Nav Item 1</li>
<li>Nav Item 2</li>
<li>Nav Item 3</li>
<li>Nav Item 4</li>
<li>Nav Item 5</li>
</ul>
</div>
My understanding is that jQuery selecting reads from right to left so if I were to use a $('#nav > li) jquery selector it would have to search through all of the li tags and then evaluate whether the li tag had a parent with the id of nav.
Assuming this page has 30 li tags but I only want to select the 5 in the example, would it be more efficient to use $('#nav').children('li') ? My logic is that it will quickly find the #nav ID and then will only have 5 elements inside of it to search through.
An additional question I have is If there were more than 30 child elements to #nav would it then be more efficient to use the child selector $('#nav > li')?
Thanks in advance
After having to try and create a custom selector, I was surprised to find that selectors are indeed evaluated right to left.
That means $('#nav > li') will first find all LIs in the page, then filter those to retain any having id="nav" in their immediate parent.
$('#nav').children('li') will be faster for the reasons above and what you assumed (which was correct).
The other side of the coin is "do you care?". If, for instance, you are only interested in mouse events, then the speed is driven by human interaction (at most a few times per second), whereas the speed difference in your selector methods would only be noticeable if you could do it 50,000 times per second. basically you will not normally care, but you should err on the side of efficiency when you can :)
if you were just after this to add mouse events, say click, using a single delegated event handler, attached to an ancestor element, will be very efficient:
e.g.
$("#nav").on('click', 'li', function() {
// DO SOMETHING HERE - "this" IS THE LI CLICKED
});
This is efficient because it works as follows:
Listen for click events bubbling up the DOM to the single id="nav" element we listen to.
Then apply the jQuery selector against the elements in the bubble chain.
Then apply the function to the matching element that caused the event.
This technique also means it will work for dynamically added objects as they only need to match the selector at event time (not event-registration time).
Notes:
If no convenient ancestor is available to target, use document as the default for bubbled events.
Do not use 'body' for delegated event handlers as styling can stop body receiving bubbled mouse events (if it has a calculated height of zero). Use document as your fallback.
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?
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.
I'm displaying a tabbed interface with the help of jQuery. When you click a tab, a ajax call will replace all html from a $(".content") element with new html, using something like
$(".content").html(response);
When I do this, are all jquery events and functions that are attached to elements inside the .content div removed? Is it ok to fire these events and functions again after I replace the HTML ? If I click the tabs 324523452354 times, will it duplicate jQuery data every time?
Yes. They will be removed. You can use the live event to attach to elements that dont exist yet.
$(".myElementClass").live("click", function (e) {
e.preventDefault();
//do stuff
});
In this case, this function will always be called on myElement no matter when it is injected into the DOM.
All HTML inside of your selector is replaced with the parameter you pass in, implying it is completely removed from the DOM. Meaning if you have:
<div id="mine">
<ul>
<li>One thing</li>
</ul>
</div>
And I do a call as such:
$('div#mine').html("hey");
My HTML will then be:
<div id="mine">
hey
</div>
As you can see the is completely removed and all its bound events mean nothing. If you use the jQuery.live() binding instead however, then elements that don't yet exist can have events associated with them. Meaning if you add some elements to the DOM then they events will still work, without you have to rebind if you add more, or replace them.
**.live** events are binded at the document level , read the following document which is really useful
http://www.bennadel.com/blog/1751-jQuery-Live-Method-And-Event-Bubbling.htm