Event delegation in jQuery, how to do it? - javascript

In JavaScript i used to use event delegation like this :
someDiv.addEventListener('click', function(evt){
if(evt.target.id == "#someChild"){
// Do something..
} else if(evt.target.id == "#anotherChild"){
// Do something else..
}
}, false);
What's the equivalent of this in jQuery? i know about .on() but how to use it in event delegation ? i mean is this how is it done :
someDiv.on('click, '#someChild, #anotherChild", function(evt){
if($(this).is("#someChild")){
// Do something..
} else if($(this).is("#anotherChild")){
// Do something else ..
}
});
But wouldn't it be just the same as in vanilla JavaScript ? I want to know if there's a better way to achieve it. And if this is the only way, what's the benefit of jQuery way over JS one ?

You can do:
$(someDiv).on('click', '#someChild, #anotherChild', function(){
if(this.id ==='someChild'){
// Do something..
} else if(this.id === 'anotherChild'){
// Do something else ..
}
});
Or create two event handlers if you do different things for both elements anyway:
$(someDiv).on('click', '#someChild', function(){
// Do something..
}).on('click', '#anotherChild', function() {
// Do something else ..
});
But wouldn't it be just the same as in vanilla JavaScript ? I want to know if there's a better way to achieve it. And if this is the only way, what's the benefit of jQuery way over JS one ?
Of course it's basically the same, jQuery just makes your life a bit easier by providing a wrapper around the DOM API, but it does not change how the DOM works.
There is a difference though, namely that this will refer to the element matched by the selector, not to the element the handler is bound to.
In general, the advantage is that the jQuery code is more cross-browser compatible than using addEventListener.

With jQuery you'd do it like this:
function onButtonClick(e) {
//this = the button that was clicked
}
$(document.body).on('click','button',onButtonClick)
You can read the docs here

You could start here, if this element is created dynamically, and not there on page load
$(document).on('click', '#someChild', function(evt) {
// Do something..
});
If the element is created before the page is rendered then this will work
<input type="button" onclick="fnClickfn();" id="xxx" />
EDIT:
Oh, also .on only works in later versions on jQuery (I think 1.6 and above), so make sure you aren't using an old version that would require .live and .die.

Related

JavaScript alert callback not working inside a function

I am trying to make a image preview containing of about 5-6 images which will appear one after another when user hovers over it (not like a carousel with prev and next buttons). Here is the fiddle consisting of what I gathered so far.. i don't know if this approach is right or not.. but I am stuck as the alert callback is not working. Could someone please tell me what is wrong?
$(function()
{
var imageCount = $('#product_grid_list').find('figure')[0].getElementsByTagName('img');
for (var i = 0, n = imageCount.length; i < n; i++) {
imageCount[i].on('click', function(e)
{
alert('Everything is going fine!');
}
);
}
}
);
The root cause of click event callback can't be triggered is that you're trying to register a event handler on a "DOM" (in this case: imageCount[i]) element in jQuery way. Try to register the event handler like this if you want to use pure javascript solution:
imageCount[i].addEventListener('click', function(e){
alert('Everything is going fine!');
});
Here is a jsfiddle demo.
Note: I didn't consider the cross browser issue in this case.
BTW, try to cache the length of imageCount node list, it will improve the performance.
You are using js AND jQuery at same time. It's wrong. If you use jQuery, than click event will be like this:
$(document).('click', '#product_grid_list figure img', function(){
alert('Everything is going fine!');
});
You are using a mix of jQuery and standalone javascript. You might as well go all the way to jQuery, with something like:
$('#product_grid_list figure:first img').click(function(e) {
alert('Everything is going fine, hopefully!');
});
You did not send the corresponding HTML, so we cannot test whether the above is correct.
it's just a simple click event in jQuery, no need to use js: http://jsfiddle.net/wP3QQ/11/
$('#product_grid_list').find('figure img').click(function(e){
alert('Everything is going fine!');
e.preventDefault();
});
You want the hover effect, so click event should not be used over here. It should be mouseover.
Working Fiddle
Code Snippet:
$(document).on('mouseover','#product_grid_list figure img',function(e){
alert("now it is working");
});
You are attempting to call on(), a jQuery method, on an HTMLElement (a DOM element). You can't do that, jQuery methods can only be called on jQuery collections. It's easy to get a jQuery collection for the elements you desire:
Use .find() to match the images
There's no need for a for() loop, jQuery's .on() will handle looping for you.
You may also want to prevent the default behaviour of your anchors
$(function () {
var imageCount = $('#product_grid_list').find('figure img');
imageCount.on('click', function (e) {
e.preventDefault()
alert('Everything is going fine!');
})
});
JSFiddle

Add/Remove css class using jQuery to identify elements by a css class

I have a javascript / jQuery function below:
function hideShowHiddens(action){
$('.hidden_col').each(function(){
if(action == 'show'){
this.removeClass("hidden");
}else{
this.addClass("hidden");
}
});
}
What it should do is search a jsp for every element with the empty marker css '.hidden_col'. Then based on the parmenter entered, it will either add or remove another css class called '.hidden' which actually hides the elements.
This method will ideally save me 400 lines of javascript functions that is too slow for the amount of data I need to work with. However, when it runs I get an error from the browser saying that it doesn't support this method. Can anyone tell me why this isn't working or how to fix it?
.addClass() and .removeClass() are jQuery functions. jQuery objects are supersets of standard DOM objects. You need to wrap the DOM object to create a jQuery object. Then the functions become accessible.
function hideShowHiddens(action){
$('.hidden_col').each(function(){
if(action == 'show'){
$(this).removeClass("hidden");
}else{
$(this).addClass("hidden");
}
});
}
Example: JSFiddle
Alternative:
Here is a shorter, sweeter alternative.
function hideShowHiddens(action){
(action === "show") ? $('.hidden_col').show() : $('.hidden_col').hide();
}
Your are missing $ selector, try this $(this).removeClass("hidden") and $(this).addClass("hidden");
The concreted problem is that this is a DOM element, not a jQuery object. You can simplify your code to and just omit the .each loop:
function hideShowHiddens(action){
$('.hidden_col').toggleClass('hidden', action !== 'show');
}
Reference: https://api.jquery.com/toggleClass/#toggleClass-className-switch

Enable/Disable events of DOM elements with JS / jQuery

I stuck here with a little problem I have put pretty much time in which is pretty bad compared to its functionality.
I have tags in my DOM, and I have been binding several events to them with jQuery..
var a = $('<a>').click(data, function() { ... })
Sometimes I would like to disable some of these elements, which means I add a CSS-Class 'disabled' to it and I'd like to remove all events, so no events are triggered at all anymore. I have created a class here called "Button" to solve that
var button = new Button(a)
button.disable()
I can remove all events from a jQuery object with $.unbind. But I would also like to have the opposite feature
button.enable()
which binds all events with all handlers back to the element
OR
maybe there is a feature in jQuery that actually nows how to do that?!
My Button Class looks something similar to this:
Button = function(obj) {
this.element = obj
this.events = null
this.enable = function() {
this.element.removeClass('disabled')
obj.data('events', this.events)
return this
}
this.disable = function() {
this.element.addClass('disabled')
this.events = obj.data('events')
return this
}
}
Any ideas? Especially this rebind functionality must be available after disable -> enable
var a = $('<a>').click(data, function() { ... })
I found these sources that did not work for me:
http://jquery-howto.blogspot.com/2008/12/how-to-disableenable-element-with.html
http://forum.jquery.com/topic/jquery-temporarily-disabling-events
-> I am not setting the events within the button class
Appreciate your help.
$("a").click(function(event) {
event.preventDefault();
event.stopPropagation();
return false;
});
Returning false is very important.
Or you could write your own enable and disable functions that do something like:
function enable(element, event, eventHandler) {
if(element.data()[event].eventHandler && !eventHandler) { //this is pseudo code to check for null and undefined, you should also perform type checking
element.bind(event, element.data()[event]);
}
else (!element.data()[event] && eventHandler) {
element.bind(event, element.data()[event]);
element.data({event: eventHandler}); //We save the event handler for future enable() calls
}
}
function disable(element, event) {
element.unbind().die();
}
This isn't perfect code, but I'm sure you get the basic idea. Restore the old event handler from the element DOM data when calling enable. The downside is that you will have to use enable() to add any event listener that may need to be disable() d. Otherwise the event handler won't get saved in the DOM data and can't be restored with enable() again. Currently, there's no foolproof way to get a list of all event listeners on an element; this would make the job much easier.
I would go on this with different approach:
<a id="link1">Test function</a>
<a id="link2">Disable/enable function</a>
<script type="text/javascript">
$(function() {
// this needs to be placed before function you want to control with disabled flag
$("#link1").click(function(event) {
console.log("Fired event 1");
if ($(this).hasClass('disabled')) {
event.stopImmediatePropagation();
}
});
$("#link1").click(function() {
console.log("Fired event 2");
});
$("#link2").click(function() {
$("#link1").toggleClass("disabled");
});
});
</script>
This may not be what you require, since it may effect also other functions binded into this event later. The alternative may be to modify the functions itself to be more like:
$("#link1").click(function(event) {
console.log("Fired event 1");
if ($(this).hasClass('disabled')) {
return;
}
// do something here
});
if that is an option.
Instead of adding event handler to each element separately, you should use event delegation. It would make much more manageable structure.
http://www.sitepoint.com/javascript-event-delegation-is-easier-than-you-think/
http://cherny.com/webdev/70/javascript-event-delegation-and-event-hanlders
http://brandonaaron.net/blog/2010/03/4/event-delegation-with-jquery
This why you can just check for class(es) on clicked element , and act accordingly. And you will be able even to re-eanble them , jsut by changing the classes of a tag.
P.S. read the links carefully, so that you can explain it to others later. Event delegation is a very important technique.
You could use an <input type="button"> and then use $("#buttonID").addAttr('disabled', 'disabled'); and $("#buttonID").removeAttr('disabled');. Disabling and enabling will be handled by the browser. You can still restyle it to look like an anchor, if you need that, by removing backgrounds and borders for the button. Be aware though, that some margins and padding might still bugger u in some browsers.

A quick question about keypress and jQuery

$(document).keydown(function(e) {
Well, that's my code - I was wondering if it is possible to have something like:
$(document).not('#pinkElephant').keydown(function(e) {
(Except, that doesn't work...)
Any Ideas?
Thanks very much!
p.s. All that function has inside it, is a switch statement.
[edit] Hey guys n gals - I cannot return false; because the element I need to type in is an <input> text, so the keyboard still needs to return here.
It's really confusing me :(
Here's an alternate way to do what you require by checking to see that the target element's id isn't pinkElephant. Since this doesn't use the universal selector '*' it should perform better:
$(document).keydown(function(e) {
if (e.target.id !== "pinkElephant") {
alert("I'm not pink!");
}
});
Here's a working example.
(updated from comment)
If you really want to bind a keydown event handler to all nodes in your markup, with the exception of #pinkElephant you need to do it like this:
$(document).find('*').not('#pinkElephant').keydown(function() ..
or short
$(':not(#pinkElephant').keydown(function() ...
which implicitly uses the universal selector *.
Note, that this is never ever any good in terms of performance. I don't know your markup but I hope it's not very crouded using that kind of selector.
update
inspired by a comment, you could also do it like:
$(document).click(function(event) {
if( event.target.id === 'pinkElephant' || $.contains($('#pinkElephant')[0], event.target) )
return false;
// ...
});
Last snippet checks whether #pinkElephant itself or a child node was clicked and prevents the propagation + the default action. This is done by invoking $.contains()help
I assume you don't want elements inside pinkElephant to trigger the keydown.
Place a handler on #pinkElephant that stops propagation of the event.
$('#pinkElephant').bind('keydown',false);
Note that passing false to .bind() requires jQuery 1.4.3 or later.
If you're using an older version, do this:
$('#pinkElephant').bind('keydown',function(){return false;});
Try this instead:
$(':not(#pinkElephant)').keydown(function(e) {
// ...
});

Dynamic Click via JavaScript (or jQuery) on a Checkbox

I know I can change the attribute checked to checked, but i need to actually "click" the checkbox to fire off a live() (jQuery) click event. I know I can also make it a function, but this would just be a lot cleaner and less code, if possible.
Oh, and FYI:
$('.modal-form [name='+json[0].owners[x].name+']').click();
Doesn't work. It checks em, but doesnt actually "click" them to where jQuery fires the live() event
Here's the AJAX call if anyone is interested:
$('.agenda-modal input[type=checkbox]:not([name="emergency-item"])').live('click',function(){
iid = $('.agenda-modal').attr('data-defaultitemid');
the_type = $(this).parent().parent().attr('id');
checked_item = $(this).val();
if($(this).attr('checked')){
if(the_type == 'the-elected-list'){
$livepreview.agenda({
action:'update',
type:'owner',
id:iid,
owner:checked_item
});
}
else if(the_type == 'the-bureau-list'){
$livepreview.agenda({
action:'update',
type:'bureau',
id:iid,
bureau:checked_item
});
}
}
else{
$livepreview.agenda({
action:'get',
type:'item',
id:iid,
callback:function(json){
if(the_type == 'the-elected-list'){
for(x in json[0].owners){
if(json[0].owners[x].name == checked_item){
$livepreview.agenda({
action:'delete',
type:'owner',
id:json[0].owners[x].item_owner_id
});
}
}
}
else if(the_type == 'the-bureau-list'){
for(x in json[0].bureaus){
if(json[0].bureaus[x].name == checked_item){
$livepreview.agenda({
action:'delete',
type:'bureau',
id:json[0].bureaus[x].item_bureau_id
});
}
}
}
}
});
}
});
This will 'click' the checkbox. I'm not positive if it will fire an event bound with live().
$('#myCheck').click();
please include an example. you are most likely not finding the right element by making a complex expression. tone it down.
var el = $('.modal-form [name='+json[0].owners[x].name+']')
alert( el.length )
Then you can .trigger('click') it. Make sure the event handler is bound/set.
Obviously I don't know your code base, but I really wonder whether calling a function by faking UI interaction would be "a lot cleaner". It might be less code, but it sounds very fragile to me. What happens when the UI is redesigned? Or another developer modifies the live() event handler without realizing that your code is implicitly using it as well? Moving the code into a separate function makes it both more legible and more robust (and probably not significantly longer).

Categories

Resources