I have a view which has a view inside it (the same view in fact, its recursive). I want only the internal view to handle the event from an 'a' onclick event. I tried to do this by specifying only the direct children in the selector, but it doesnt work.
iv tried:
events: {
'click >a': 'toggle'
},
and
events: {
'click > a': 'toggle'
},
but they dont work, any suggestions? (Note: doing things like using tags and classes wont work because the view is recursive (meaning that both the inner and outer have the same event definitions)
I have run into the same issue, and have solved it by stopping the propagation of the event in my child view. For example...
events: {
'click a': 'toggle'
},
toggle: function (e) {
// Stop event here.
e.stopImmediatePropagation();
// Do more stuff...
}
This does not answer your question of how to specify specific child selectors, but it keeps the event from propagating up to other handlers.
e.stopImmediatePropagation() is handy if you have no other events firing when you click that specific element. What you should do otherwise is compare the e.currentTarget with the element you want to select. I.e.
events: {
'click a': 'toggle'
}
toggle: function(e) {
if (e.currentTarget == this.$el.find('a')[0]) {
...
}
}
That way, if you have a parent view also firing an event when that link is clicked, it won't be stopped by e.stopImmediatePropagation();
You may have to specify the starting selector...meaning I'm not sure the base selector is assumed.
events: {
'click #parent > a': 'toggle'
}
Haven't tried this, but it may work.
Related
In this Meteor template code, when the canvas is clicked, it prints out the canvas element to the console but it is expected not to fire the event.
How can it be made so that it fires if the element which is clicked is not a canvas?
Template.myTemp.events({
'click *:not(canvas)': function(e) {
e.stopPropagation();
console.log(e.target);
});
Definitely an interesting problem as the :not selector is supported by Blaze, and works properly with other HTML elements. You might want to open an issue about this in the Blaze repo.
The above being said, there are a few different ways you can work around this. You could add a check in your event handler to make sure you don't do anything with canvas related events:
Template.myTemp.events({
'click *'(event, instance) {
if (event.target.nodeName.toLowerCase() !== 'canvas') {
// Handle non-canvas events ...
}
},
});
Another option involves chaining your event handlers, if you want to be able to filter out canvas events specifically. For example:
Template.myTemp.events({
'click canvas'(event, instance) {
event.stopImmediatePropagation();
// Handle canvas click events only ...
},
'click *'(event, instance) {
// Handle all click events except canvas click events, since they're
// captured and handled above ...
},
});
In a Backbone application, I instantiate a view for every model in a collection.
If one of these views is clicked, I want to call a function.
Unfortunately, the function is called n times, where n is the number of models/view instantiated. I’ve managed to get around this by finding out what element has been clicked on, but I still don’t feel comfortable knowing that one event might be triggered 200+ times in the very same moment.
The event is bound like this:
var Item = Backbone.View.extend({
events: {
'click .list-group-item': function(event) { this.doSomething(event); },
},
doSomething: function(event) {
$(event.currentTarget).toggleClass('active');
},
});
In the code above you can also see my workaround using event.currentTarget, but how can I avoid this? Is there a way to distinguish the .list-group-item elements without resorting to event.currentTarget, so preferable right in the moment an element is clicked?
Another approach would be to bind the event to the parent element, so it is only triggered once and then using event.currentTarget, but that also seems kind of fishy to me.
Since you want to attach to a click anywhere in the view, you don't need to specify .list-group-item. Also, you only need to specify the name of the event callback function:
var Item = Backbone.View.extend({
events: {
'click': 'doSomething'
},
doSomething: function(event) {
$(event.currentTarget).toggleClass('active');
},
});
I am trying to make a swith on / off javascript, but I have a problem: I click always comes in my first class despite my class change.
I have always the same result : on->on or off->off. I check in Firebug my html is changed correctly...
Here my simplified code :
$('.off').on('click', function(e) {
e.stopPropagation();
e.preventDefault();
alert('off');
$(this).removeClass('off');
$(this).addClass('on');
});
$('.on').on('click', function(e) {
e.stopPropagation();
e.preventDefault();
alert('on');
$(this).removeClass('on');
$(this).addClass('off');
});
if anyone has a suggestion, I would be very grateful !!
The event handlers are bound on pageload, so changing the class won't change the event handlers, as they are attach to whatever elements existed at the time of binding. To attach them to future elements, i.e. when you change the classes, you'll need delegated event handlers, but an easier solution in my opinion is to just toggle the classes or use a flag:
$('.off').on('click', function(e) {
e.preventDefault();
var state = $(this).is('.off') ? 'on' : 'off';
alert(state);
$(this).toggleClass('off on');
});
FIDDLE
It can be confusing, but even if you remove the .off class, the event handler is still bound to the same element, as it had the .off class at the time of binding!
Either .off or .on does not exist when you first bind with .on. You need to use event delegation, or you can rewrite this so that it's bound to another class and maintain on/off state. I will show you both methods.
Delegation
$(document).on('click', '.off', function(e) {
$(this).removeClass('off').addClass('on');
}).on('click', '.on', function(e) {
$(this).removeClass('on').addClass('off');
});
http://jsfiddle.net/ExplosionPIlls/2PHJg/
Internal state
$(".switch").on('click', function () {
if ($(this).is('.off')) {
$(this).removeClass('off').addClass('on');
}
else {
$(this).removeClass('on').addClass('off');
}
});
http://jsfiddle.net/ExplosionPIlls/2PHJg/1/
This problem is caused because $('.on') and $('.off') only gets evaluated once, when your javascript code runs.
You have to use event delegation to solve the problem. Modify the code the following way. I describe below what it means.
$('#container').on('click', '.off', function(e) {
e.stopPropagation();
e.preventDefault();
alert('off');
$(this).removeClass('off');
$(this).addClass('on');
});
$('#container').on('click', '.on', function(e) {
e.stopPropagation();
e.preventDefault();
alert('on');
$(this).removeClass('on');
$(this).addClass('off');
});
In the place of #container use a selector which selects a parent of your .on / .off elements. In the second parameter of your on() call, youy have to specify the element for which you want to listen for events.
This way the event handler will run properly, even if the class of your elements change.
The problem is that the event handlers are bound when the code first runs, so when the class changes, the event handlers aren't rebound. One way to fix this is to assign the element a class that doesn't change, and use that for the event handler.
Example:
$('.button').on('click', function(e) {
e.stopPropagation();
e.preventDefault();
$(this).toggleClass('off on');
});
You can see a working example at http://codepen.io/skimberk1/pen/GJhna.
I have dynamically created the buttons in a div. And binding the click and other events for the buttons. But the problem is, click event fired only one for first clicked button. It happens same for the other binding events. The sample code is
$('#divButtons input[type=button]').each(function () {
$(this).bind('mouseover', function (e) {
// some work
}).bind('mouseout', function (e) {
// some work
}).bind('click', function (e) {
// some work
});
});
It works good when bind it on document.ready() But in my case buttons created far after DOM ready.
I also want to know why it behaves like this...?
If using jQuery 1.7+ go for on(), and there's really no need for each() :
$(document).on({
mouseover: function(e) {
// some work
},
mouseout: function(e) {
// some work
},
click: function(e) {
// some work
}
}, '#divButtons input[type=button]');
replace document with nearest non dynamic element for delegated event handler.
Using a Backbone.js View, say I want to include the following events:
events: {
'click a': 'link',
'click': 'openPanel'
}
How can I avoid openPanel to be fired when I click on a link. What I want is to have a clickable box which will trigger an action, but this box can have elements which should trigger other actions, and not the parent action. Think for example Twitter.com, and links in Tweets/right hand panel.
I've been using e.stopImmediatePropagation(); in order to keep the event from propagating. I wish there was a shorter way to do this. I would like return false; but that is due to my familiarity with jQuery
The JQuery preventDefault method would also be a good option.
window.LocationViewLI = Backbone.View.extend({
tagName: "li",
template: _.template('<%= name %>'),
events: {
"click a": "handleClick"
},
handleClick: function(event) {
event.preventDefault();
console.log("LocationViewLI handleClick", this.model.escape("name") );
// debugger;
},
...
Each of your event handlers will be passed an event object when it's triggered. Inside your handler, you need to leverage jQuery's event.stopPropagation() method. For example:
link: function(event) {
//do some stuff here
event.stopPropagation();
}
Two other methods that might work for you:
1
events: {
'click a': 'link',
'click *:not(a, a *)': 'openPanel'
}
Then openPanel will not capture click events on any <a> or child of an <a> (in case you have an icon in your <a> tag).
2
At the top of the openPanel method, make sure the event target wasn't an <a>:
openPanel: function(event) {
// Don't open the panel if the event target (the element that was
// clicked) is an <a> or any element within an <a>
if (event && event.target && $(event.target).is('a, a *')) return;
// otherwise it's safe to open the panel as usual
}
Note that both of these methods still allow the openPanel function to be called from elsewhere (from a parent view or another function on this view, for example). Just don't pass an event argument and it'll be fine. You also don't have to do anything special in your link function -- just handle the click event and move on. Although you'll probably still want to call event.preventDefault().
Return "false" in your "link" function.