Getting element properties in click handler - javascript

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.

Related

click.delegate for ul items in AureliaJS

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>

Meteor events only binding to first element

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?

OnClick get "id" attribute of "a" tag

I have list view in jQuery Mobile web app this list view is made of elements like this:
<li id='search_r'>
<a href='#' id='$id' class='s_result'></a>
</li>
<li id='search_r'>
<a href='#' id='$id' class='s_result'></a>
</li>
Number of element depends on number of search results, its not only these two.
When I click on element in list view, in this case:
<li></li>
I need 2 things to happen, one is to assign the 'id' attr from "a" tag inside this specific "li" tag (the clicked one), to the global variable I have called 'search_r'. The click event works fine, but to get attribute from "a" tag I can't manage to do.
Here is my js:
$("#cc_page").ready(function(){
$("#search_r").live('click', function(){
search_r = $(this).attr('id');
window.location.href = "http://imes.********.com/app/userpanel.html#sfpp_page";
});
});
I know "this" is not solution. I'm really new to js so that's why I'm asking such an ridiculous questions :)
The first problem you've got is duplicate search_r id attributes, which is invalid. These should be changed to classes. Also, you should be using on() with a delegate handler, as live() has been removed from the latest version of jQuery. Try this:
<li class='search_r'>
<a href='#' id='$id' class='s_result'></a>
</li>
<li class='search_r'>
<a href='#' id='$id' class='s_result'></a>
</li>
$("#cc_page").on('click', '.search_r', function(){
var search_r = $('a', this).attr('id');
console.log(search_r); // just to check it works
// I assume this is just for testing, otherwise leaving the page
// immediately on click renders getting the id completely moot.
//window.location.href = "http://imes.********.com/app/userpanel.html#sfpp_page";
});
I also assume the $id in your HTML is from some form of templating engine which gets interpreted? If not, those will also need to be made unique.
window.location.href causes the browser to make a new request. Any variables will be reset when the new page loads.
What is your goal with search_r?
.live has been deprecated in jQuery since v1.7, and has been removed in v1.9.
You should replace it with .on().
.on has 2 syntaxes for binding elements, whereas .live only had 1.
If the element exists at the time you are binding, you do it like this:
$('.element').on('click', function(){
.......
});
You can even use the shorthand:
$('.element').click(function(){
.........
});
If the element does not exist at the time, or new ones will be added (which is what .live was normally used for), you need to use "event delegation":
$(document).on('click', '.element', function(){
.............
});
NOTE: You want to bind to the closest static element, not always document.
In the meantime, the jQuery Migrate plugin can be used to restore the .live() functionality if you upgrade your jQuery to the newest version.

How to prevent multiple even firings with recursive Backbone.js views?

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. :)

does jQuery's html() remove all data attached to elements that are replaced?

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

Categories

Resources