Need help understanding jquery delegate() function - events

I'm having a hard time understanding the syntax of the .delegate function of jquery. Let's say I have the following:
$(".some_element_class").delegate("a", "click", function(){
alert($(this).html());
});
I know that the a element is the element to which the click event is applied. I know that once we click on that a element, the event click will be triggered and the callback function will be called. But what is the purpose of what comes before the .delegate? In this case, what is the purpose of .some_element_class? How do I read the above including the .some_element_class? Also, in the example above, what does $(this) represent? Does it represent the a element or does it represent .some_element_class?
Please somebody, shed some light on this.
Thank you

This reduces event binding.
This basically sets an event on a tags ONLY within the elements with class .some_element_class without actually binding an event to a tags directly.
http://api.jquery.com/delegate/
http://api.jquery.com/on/
As of jQuery 1.7, .delegate() has been superseded by the .on() method.
For earlier versions, however, it remains the most effective means to
use event delegation. More information on event binding and delegation
is in the .on() method. In general, these are the equivalent templates
for the two methods:
$(elements).delegate(selector, events, data, handler); // jQuery 1.4.3+
$(elements).on(events, selector, data, handler); // jQuery 1.7+
$(".some_element_class").on("a", "click", function(){
alert($(this).html());
});

"...what is the purpose of what comes before the .delegate?"
A delegate is bound to .some_element_class element.
That delegate is triggered for every click that takes place inside .some_element_class
That delegate tests what was clicked, so your handler function will only run if...
the actual element clicked matches the "a" selector, or
any ancestor of the actual element clicked that is a descendant of .some_element_class matches the "a" selector.
<div class="some_element_class"> <!-- delegate handler is here -->
<div>won't trigger your handler</div>
<a>will trigger your handler</a>
<a><span>will trigger</span> your handler</a>
</div>
So you can see that only one handler is bound to the container. It analyzes all clicks inside the container, and if the element clicked (or one of its nested ancestors) matches the selector argument, your function will run.
Because there's just one enclosing handler, it will work for future elements added to the container...
<div class="some_element_class"> <!-- delegate handler is here -->
<div>won't trigger your handler</div>
<a>will trigger your handler</a>
<a><span>will trigger</span> your handler</a>
<!-- this element newly added... -->
<a><span>...will also trigger</span> your handler</a>
</div>
"Also, in the example above, what does $(this) represent?"
this will represent the element that matched the "a" selector.

it means delegate() is invoked on the .some_event_class. and the a is selector string, click is event type string & function() is eventhandler function. delegate() method is used to handle the "live event" and for static events bind() is used. I hope this helps. feel free to ask if you have any doubts
Differences between bind() & delegate()
//Static event handlers for static links
$("a").bind("",linkHandler);
//Live event handlers for dynamic parts of the document
$(".dynamic").delegate("a", "mouseover", linkHandler);
Summary: they are just methods that bind event handlers to specific document elements.

The a is actually just a filtering selector, what will happen is that a normal click event is bound to .some_element_class, and anytime the event fires, the event target is traversed up to .some_element_class to see if there is an element that matches the filtering selector (tagname a). If it does, your callback is fired with this set to the first element that matched a selector in the bubbling path.
You can do something similar with bind:
$(".some_element_class").bind("click", function (e) {
var matches = $(e.target).closest("a", this);
if (matches.length) {
yourcallback.call(matches[0], e);
}
});

Related

Which events can be subscribed to with Knockout's observable.subscribe function

Under the Explicitly Subscribing to Observables section of the Knockout documentation, there is a reference to an event parameter of the subscribe function, but the only two examples given on that page are change and beforeChange.
By way of example, I tried passing in "focus" as the third parameter but that didn't work. I'm not too surprised as "focus" is an event of a DOM Element rather than a knockout observable, but nonetheless it could theoretically have setup a subscription to the focus event for all elements bound to that observable.
Is there a list of all events that can be subscribed to manually using Knockout's observable.subscribe function?
It make sens to use "event" binding in your case.
Because there are only two ways to notify subscribers of observable variable: beforeChange and change.
In knockoutJs code there is simple chain of if blocks which check if event is specified, and if event is equal to beforeChange. That's basically all logic which goes there, so no other events fired.
Part form knockoutJS which implements this logic:
self["notifySubscribers"] = function(value, event) {
if (!event || event === defaultEvent) {
self._rateLimitedChange(value);
} else if (event === beforeChange) {
self._rateLimitedBeforeChange(value);
} else {
self._origNotifySubscribers(value, event);
}
};

jQuery unable select element from getJSON

I'm using the .each method with the .getJSON method to print out objects in a JSON file. This works fine, however I am unable to add a click function to an element that has been printed out. I am trying to bind a function to the div with 'click' ID.
var loadData = function () {
$.getJSON("profiles2.json", function (data) {
var html = [];
html.push("<div id='click'>Click here</div>");
$.each(data.profiles, function (firstIndex, firstLevel) {
html.push("<h2>" + firstLevel.profileGroup + "</h2>");
});
$("#data").html(html.join(''));
});
};
$(document).ready(function () {
loadData();
$("#click").click(function () {
console.log('clicked');
});
});
$.getJSON() (like other Ajax methods) is asynchronous, so it returns immediately before the results have come back. So your loadData() method also returns immediately and you then try to bind a handler to an element not yet added.
Move the .click(...) binding into the callback of $.getJSON(), after adding the element(s), and it will work.
Alternatively, use a delegated event handler:
$("#data").on("click", "#click", function() {
console.log('clicked');
});
...which actually binds the handler to the parent element that does exist at the time. When a click occurs it then tests whether it was on an element that matched the selector in the second parameter.
And as an aside, don't bind click handlers to divs unless you don't care about people who are physically unable to (or simply choose not to) use a mouse or other pointing device. Use anchor elements (styled as you see fit) so that they're "click"-accessible via the keyboard and the mouse.
$.getJSON is an asynchronous call and probably hasn't finished by the time you are trying to bind to the element that it injects into your DOM. Put your binding inside the $.getJSON call after you append the element to the page at the bottom.

how to fake a click on a dynamic element?

On a static element, to fake a click, I use
$(selector).click();
But how can I do the same thing on a dynamic element (resulted from an ajax call)?
The same...:
$(selector).click();
Why didn't you try it first?
P.S. it is not called fake a click, it's called trigger the click event.
$(selector).trigger('click'); == $(selector).click();
Update
You need to bind that element a callback to the event in order it to work:
$(selector).click(function(){...});
$(selector).click();
If you want it to have the the click callback you assigned to the static elements automaticlly, you should use on\ delegate (or live but it's deprecated) when you attach the click callback.
$('body').on('click', 'selector', function(){...})
instead if body use the closest static element the holds that selector elements.
See my DEMO
within your ajax success function try your code:
$(selector).click();
Basing this on your previous question : How can I select a list of DOM objects render from an AJAX call?
$(document).ready(function(){
var listItems = $('#myList li a');
var containers = $('#myContainer > div');
listItems.click(function(e){//do someting
});
etc...
If the elements you are trying to attach a click handler to are supposed to be inside any of the two variables above then you WILL have to update those variables after the elements are inserted into the DOM, as it is right now only elements that exists during first page load will be inside those variables.
That is the only reason I can think of why something like :
$(document).on('click', listItems, function(e) {//do something
});
will not work!
Don't know if I understand (I'm french sorry...)
But try :
$(selector).live('click',function(){}); // deprecated it seems
Demo of gdoron with live() : http://jsfiddle.net/Rx2h7/1/
use on() method of jquery,
staticElement.on('click', selector, function(){})
on method generates click event on dynamically created element by attaching it to the static element present in the DOM .
For further reference check this out -- https://api.jquery.com/on/

Add complex markup with event handlers

How to put into a grid cell not just a string <span>text</span>, but a string with an event handler?
This option does not suit me:
<span onclick='function(){...}'>click me</span>
I need to add, for example, such elements in one grid cell:
var $el1 = $('<button>clck me 1</button>').click(function(){...});
var $el2 = $('<button>clck me 2</button>').click(function(){...});
...
I use slick.dataview.
Don't.
Either handle click events via SlickGrid by using the onClick event it exposes or use event delegation on a higher level (container or document) to catch it. Add an attribute to the buttons to distinguish them later and decide which handler to execute.

Browser Memory Usage Comparison: inline onClick vs. using JQuery .bind()

I have ~400 elements on a page that have click events tied to them (4 different types of buttons with 100 instances of each, each type's click events performing the same function but with different parameters).
I need to minimize any impacts on performance that this may have. What kind of performance hit am I taking (memory etc) by binding click events to each of these individually (using JQuery's bind())? Would it be more efficient to have an inline onclick calling the function on each button instead?
Edit for clarification :):
I actually have a table (generated using JQGrid) and each row has data columns followed by 4 icon 'button' columns- delete & three other business functions that make AJAX calls back to the server:
|id|description|__more data_|_X__|_+__|____|____|
-------------------------------------------------
| 1|___data____|____data____|icon|icon|icon|icon|
| 2|___data____|____data____|icon|icon|icon|icon|
| 3|___data____|____data____|icon|icon|icon|icon|
| 4|___data____|____data____|icon|icon|icon|icon|
I am using JQGrid's custom formatter (http://www.trirand.com/jqgridwsiki/doku.php?id=wiki:custom_formatter) to build the icon 'buttons' in each row (I cannot retrieve button HTML from server).
It is here in my custom formatter function that I can easily just build the icon HTML and code in an inline onclick calling the appropriate functions with the appropriate parameters (data from other columns in that row). I use the data in the row columns as parameters for my functions.
function removeFormatter(cellvalue, options, rowObject) {
return "<img src='img/favoritesAdd.gif' onclick='remove(\"" + options.rowId + "\")' title='Remove' style='cursor:pointer' />";
}
So, I can think of two options:
1) inline onclick as I explained above
--or--
2) delegate() (as mentioned in below answers (thank you so much!))
Build the icon image (each icon type has its own class name) using the custom formatter.Set the icon's data() to its parameters in the afterInsertRow JQGrid event. Apply the delegate() handler to buttons of specific classes (as #KenRedler said below)
> $('#container').delegate('.your_buttons','click',function(e){
> e.preventDefault();
> var your_param = $(this).data('something'); // store your params in data, perhaps
> do_something_with( your_param );
> }); //(code snippet via #KenRedler)
I'm not sure how browser-intensive option #2 is I guess...but I do like keeping the Javascript away from my DOM elements :)
Because you need not only a general solution with some container objects, but the solution for jqGrid I can suggest you one more way.
The problem is that jqGrid make already some onClick bindings. So you will not spend more resources if you just use existing in jqGrid event handler. Two event handler can be useful for you: onCellSelect and beforeSelectRow. To have mostly close behavior to what you currently have I suggest you to use beforeSelectRow event. It's advantage is that if the user will click on one from your custom buttons the row selection can stay unchanged. With the onCellSelect the row will be first selected and then the onCellSelect event handler called.
You can define the columns with buttons like following
{ name: 'add', width: 18, sortable: false, search: false,
formatter:function(){
return "<span class='ui-icon ui-icon-plus'></span>"
}}
In the code above I do use custom formatter of jqGrid, but without any event binding. The code of
beforeSelectRow: function (rowid, e) {
var iCol = $.jgrid.getCellIndex(e.target);
if (iCol >= firstButtonColumnIndex) {
alert("rowid="+rowid+"\nButton name: "+buttonNames[iCol]);
}
// prevent row selection if one click on the button
return (iCol >= firstButtonColumnIndex)? false: true;
}
where firstButtonColumnIndex = 8 and buttonNames = {8:'Add',9:'Edit',10:'Remove',11:'Details'}. In your code you can replace the alert to the corresponding function call.
If you want select the row always on the button click you can simplify the code till the following
onCellSelect: function (rowid,iCol/*,cellcontent,e*/) {
if (iCol >= firstButtonColumnIndex) {
alert("rowid="+rowid+"\nButton name: "+buttonNames[iCol]);
}
}
In the way you use one existing click event handler bound to the whole table (see the source code) and just say jqGrid which handle you want to use.
I recommend you additionally always use gridview:true which speed up the building of jqGrid, but which can not be used if you use afterInsertRow function which you considered to use as an option.
You can see the demo here.
UPDATED: One more option which you have is to use formatter:'actions' see the demo prepared for the answer. If you look at the code of the 'actions' formatter is work mostly like your current code if you look at it from the event binding side.
UPDATED 2: The updated version of the code you can see here.
You should use the .delegate() method to bind a single click handler for all elements ,through jQuery, to a parent element of all buttons.
For the different parameters you could use data- attributes to each element, and retrieve them with the .data() method.
Have you considered using delegate()? You'd have one handler on a container element rather than hundreds. Something like this:
$('#container').delegate('.your_buttons','click',function(e){
e.preventDefault();
var your_param = $(this).data('something'); // store your params in data, perhaps
do_something_with( your_param );
});
Assuming a general layout like this:
<div id="container">
<!--- stuff here --->
<a class="your_buttons" href="#" data-something="foo">Alpha</a>
<a class="your_buttons" href="#" data-something="bar">Beta</a>
<a class="your_buttons" href="#" data-something="baz">Gamma</a>
<a class="something-else" href="#" data-something="baz">Omega</a>
<!--- hundreds more --->
</div>

Resources