I have the falling events hash -
events:
'click #someButton : 'someFunction'
To close the view I have tried
close:
$("#someButton").unbind("click")
and
`close:
$("#someButton").remove()`
But someFunction is still being fired more than once. How do I unbind this event from the button?
I've also tried
$(#el).find("#someButton").unbind("click") as well
Backbone.js view events are delegated to the view's el (so there is no event bound to your #someButton element but rather when a click event bubbles up to the el it checks to see if the event came from an element matching that selector), that being the case to remove the event you need to remove it from the el, for example
$(this.el).off('click', '#someButton');
If you want to remove all delegated events you can just use the view's undelegate method
Jack's explanation and answer are great. Unfortunately, his code example didn't work for me. Instead of using:
$(this.el).off('click', '#someButton');
I had to use:
this.$el.off('click', '#someButton');
which makes sense to me, because the event was bound to this.$el object.
To add further, I used this.$el.off(); inside an initialize within a subview to destroy all events tied to the subview. The same event would fire X number of times a data refresh was called.
I saw the duplicated targets using:
var $target = $(e.currentTarget);
console.log($target);
I would add a comment to an answer, but I have low reputation.
Related
I have created a small jQuery plugin that creates sticky sidebars and I've tried to create a "destroy" method which removes the stickyness. It works fine however I'm using it on multiple elements on the page (sticky sidebar within accordions) and when I destroy one, it removes the scroll events for all the others.
$parent.find('.js-opps-aside').stickAside();
var thisScroll = $(window).on('scroll', enqStick);
...
$(window).off('scroll', thisScroll);
I thought that was how to unbind specific scroll events, however, as I said it removed all the event handlers. I just want it to remove the scroll event for this specific element it was called upon, and leave the other elements with their scroll events intact.
When you use $().off(), the second parameter is a reference to the event handler, and you try to off with thisScroll but it is a jQuery collection.
You should use this :
$(window).off('scroll', enqStick);
You can use another solution that is think is better, just suffit the event name
$parent.find('.js-opps-aside').stickAside();
$(window).on('scroll.myCustomScroll', enqStick);
...
$(window).off('scroll.myCustomScroll');
$().off with only one parameter will off all event callbacks associated with the event, in our case it will of all scroll.myCustomScroll
According to jQuery stacking logic thisScroll is $(window).
What you would like to do is:
// Binding a handler
$(window).on('scroll', enqStick);
// removing handler by bound function handler
$(window).off('scroll', enqStick);
I am using jQuery each function and on click on current i.e $(this) I am executing JS code, It will be more clear if you look on following code
$('.switch-cal').each(function(){
$(this).on( 'click', function( e ){
.... CODE HERE ....
Please tell me correct way of use "this" with ".on" inside ".each" function.
Thank you.
1. Short answer: There is no need for the each:
Do not use click inside of each... you do not need to with jQuery. It handlers collections automatically for most operations (including event handlers):
e.g.
$('.switch-cal').click(function(){
//$(this) is the calendar clicked
.... CODE HERE ....
2. Try a delegated handler:
It is often more convenient to use a single delegated event handler (attached to the nearest non-changing ancestor element, or document if none is handy). The best feature of delegated events is that they can work on elements that do not even exist yet! :)
The run-time overhead is quite low as they only need to apply the selector to the elements in the bubble-chain. Unless you are processing 50,000 mouse operations per second the speed difference is negligible, so for mouse events, don't be put of by ridiculous claims of inefficiency (like the down-voter's ranting below). The benefits usually out-way any extra overhead. Nobody clicks 50,000 times per second!:
e.g.
$(document).on('click', '.switch-cal', function(){
//$(this) is the calendar clicked
.... CODE HERE ....
Delegated events work by listening for events (in this case click) bubbling up the DOM to a non-changing ancestor element. It then applies the selector. It then calls the function on each matching element that caused the event.
The closer the non-changing element is to the elements in question the better, to reduce the number of tests required, but document is your fall-back if nothing else is convenient. Do not use body as it has problems relating to styling that means events may not fire.
#TrueBlueAussie had the correct answer. You should use a single delegated event.
$(document).on('click', '.switch-cal', function(){
//$(this) is the calendar clicked
.... CODE HERE ....
Still if you want to keep your "this" scope, you may
$('.switch-cal').each(function(){
$(this).on( 'click', function( e ){
.... CODE HERE ....
}.bind(this))
});
I am trying to make simple list with ability to add and delete elements. For now I am working on adding and performing a simple action on each of list elements object (existing and added). Unfortunately I have met some difficulties with that. I am able to modify objects that are created at the beginning, but not one added during "webpage working".
First of all my idea was to add AJAX to this, but I don't think it is the easiest way.
I think that some time ago (I don't remember where) I read how to make this work, but now I don't know. I would be really glad if someone would help me with this or at least give a link to good explanation of this.
There is what I have done so far (well this is mostly just a scratch, but the main idea is in it): http://jsfiddle.net/sebap123/pAZ7H/0
$("li").click(function() {
$(this).text("new text");
});
$("#addButton").click(function() {
$(".list").append(
$('<li>').append($('<span>').append("added li")));
});
Thank you for all responses.
You just need to use event-delegation, with the on() method:
$("ul").on('click','li', function() {
$(this).text("OK");
});
JS Fiddle demo.
The problem you were experiencing is that jQuery can only directly bind events to already-present elements (present at the point of event-binding); the on() approach binds the action to the element to which the new content is added (or an ancestor element of the newly-added elements), identifies the events to listen for 'click' (a space-separated list of events), that match the selector (li) and then the function performs the required actions.
If you're using jQuery 1.7 (or later) on() should be used, for versions of jQuery < 1.7, delegate() should be used instead (which does more or less the same thing, but reverses the event-list and the selector parameters):
$("ul").delegate('li', 'click', function() {
$(this).text("OK");
});
JS Fiddle demo.
References:
delegate().
on().
Need to use event delegation here..
Try this
$("ul").on('click' , 'li', function() {
$(this).text("OK");
});
The problem with the previous click event is that events are only attched to the current element's on the page..
But when you create a new element , no events are attached to the element.
You have two options here.. Either to delegate your event i.e; add the event listerner to the element's ancestor ..
Or add the event once the element is created..
2nd Approach
var clickEvent = function() {
$('li').unbind().on('click', function() {
$(this).text("OK");
});
}
clickEvent();
$("#addButton").click(function() {
$(".list").append(
$('<li>').append($('<span>').append("Press me - I am new!")));
clickEvent();
});
In the second approach you are binding unbinding and binding the new event to all the elements once a new element is added..
Binding and again rebinding of events is a bad design pattern
FIDDLE
2nd APPROACH
As Jquery Mobile keeps some pages in the DOM when navigating around, a page may be visited multiple times when going back and forth.
If I'm binding to a page like below and inside this binding perform all my page logic, which includes "nested element bindings":
// listener for the page to show:
$(document).on('pagebeforeshow.register', '#register', function() {
// stuff
// page event bindings:
$(document).on('click.register', '.registerSubmitter', function(e) {
// do something
});
});
Going back and forth causes my nested binding to be attached multiple times.
Right now trying to work around this like so (doesn't work...):
$(document).on('click', '.registrySubmitter', function(e) {
if ( $(this).attr('val') != true ) {
$(this).attr('val') == true;
// do something
}
});
So I'm only allowing the first binding to pass and then I block every other binding attempt that comes along.
While this works, it's far from optimal.
Question:
How and when should event bindings be properly unbound/offed? Is there a general way (kill all) or do I have to do this binding per binding? Maybe more importantly: Is it better performance-wise to do a binding once and keep it or bind/unbind when the user comes to/leaves the page?
Thanks for input!
EDIT:
So I'm namespacing all my events and then listen for pageHide like so:
$(document).on('pagehide.register', '#register', function(){
$(document).off('.registryEvents');
});
While this seems to unbind, it also fires when ever I close a custom dialog/selectmenu on the page, so I'm loosing my bindings before leaving the page. So partial answer, I should use off(), but how to bind to the page really being left vs. opening and closing a select menu?
When you use .on() like that, you are delegating the event handling to the document element, meaning you can setup that delegated event binding anytime you want because the document element is always available.
I've got two suggestions:
Use the pageinit or pagecreate event to only run the page-specific bindings when pages are added to the DOM and initialized. Using this method I would not delegate the event bindings within the pageinit or pagecreate event handlers because when they fire, all the elements on the pseudo-page are in the DOM:
.
$(document).on('pageinit', '#register', function() {
//note that `this` refers to the `#register` element
$(this).find('.registerSubmitter').on('click', function(e) {
// do something
});
});
Bind the delegated event handlers once and don't worry about when pages are actually in the DOM:
.
//this can be run in the global scope
$(document).on('click.register', '.registerSubmitter', function(e) {
// do something
});
Basically when you bind an event using delegation like you are, the actual CPU hit of adding the event handler is less but each time an event is dispatched (of any kind that bubbles) it has to be checked if it matches the delegated event handler's selector.
When you bind directly to elements it generally takes more time to do the actual binding because each individual element has to be bound to rather than binding once to the document element like with event delegation. This however has the benefit that no code runs unless a specific element receives a specific event.
A quick blurb from the documentation:
Triggered on the page being initialized, after initialization occurs.
We recommend binding to this event instead of DOM ready() because this
will work regardless of whether the page is loaded directly or if the
content is pulled into another page as part of the Ajax navigation
system.
Source: http://jquerymobile.com/demos/1.1.0/docs/api/events.html
I am using jQuery v.1.7.1 where the .live() method is apparently deprecated.
The problem I am having is that when dynamically loading html into an element using:
$('#parent').load("http://...");
If I try and add a click event afterwards it does not register the event using either of these methods:
$('#parent').click(function() ...);
or
// according to documentation this should be used instead of .live()
$('#child').on('click', function() ...);
What is the correct way to achieve this functionality? It only seems to work with .live() for me, but I shouldn't be using that method. Note that #child is a dynamically loaded element.
Thanks.
If you want the click handler to work for an element that gets loaded dynamically, then you set the event handler on a parent object (that does not get loaded dynamically) and give it a selector that matches your dynamic object like this:
$('#parent').on("click", "#child", function() {});
The event handler will be attached to the #parent object and anytime a click event bubbles up to it that originated on #child, it will fire your click handler. This is called delegated event handling (the event handling is delegated to a parent object).
It's done this way because you can attach the event to the #parent object even when the #child object does not exist yet, but when it later exists and gets clicked on, the click event will bubble up to the #parent object, it will see that it originated on #child and there is an event handler for a click on #child and fire your event.
Try this:
$('#parent').on('click', '#child', function() {
// Code
});
From the $.on() documentation:
Event handlers are bound only to the currently selected elements; they
must exist on the page at the time your code makes the call to .on().
Your #child element doesn't exist when you call $.on() on it, so the event isn't bound (unlike $.live()). #parent, however, does exist, so binding the event to that is fine.
The second argument in my code above acts as a 'filter' to only trigger if the event bubbled up to #parent from #child.
$(document).on('click', '.selector', function() { /* do stuff */ });
EDIT: I'm providing a bit more information on how this works, because... words.
With this example, you are placing a listener on the entire document.
When you click on any element(s) matching .selector, the event bubbles up to the main document -- so long as there's no other listeners that call event.stopPropagation() method -- which would top the bubbling of an event to parent elements.
Instead of binding to a specific element or set of elements, you are listening for any events coming from elements that match the specified selector. This means you can create one listener, one time, that will automatically match currently existing elements as well as any dynamically added elements.
This is smart for a few reasons, including performance and memory utilization (in large scale applications)
EDIT:
Obviously, the closest parent element you can listen on is better, and you can use any element in place of document as long as the children you want to monitor events for are within that parent element... but that really does not have anything to do with the question.
The equivalent of .live() in 1.7 looks like this:
$(document).on('click', '#child', function() ...);
Basically, watch the document for click events and filter them for #child.
I know it's a little late for an answer, but I've created a polyfill for the .live() method. I've tested it in jQuery 1.11, and it seems to work pretty well. I know that we're supposed to implement the .on() method wherever possible, but in big projects, where it's not possible to convert all .live() calls to the equivalent .on() calls for whatever reason, the following might work:
if(jQuery && !jQuery.fn.live) {
jQuery.fn.live = function(evt, func) {
$('body').on(evt, this.selector, func);
}
}
Just include it after you load jQuery and before you call live().
.on() is for jQuery version 1.7 and above. If you have an older version, use this:
$("#SomeId").live("click",function(){
//do stuff;
});
I used 'live' in my project but one of my friend suggested that i should use 'on' instead of live.
And when i tried to use that i experienced a problem like you had.
On my pages i create buttons table rows and many dom stuff dynamically. but when i use on the magic disappeared.
The other solutions like use it like a child just calls your functions every time on every click.
But i find a way to make it happen again and here is the solution.
Write your code as:
function caller(){
$('.ObjectYouWntToCall').on("click", function() {...magic...});
}
Call caller(); after you create your object in the page like this.
$('<dom class="ObjectYouWntToCall">bla... bla...<dom>').appendTo("#whereeveryouwant");
caller();
By this way your function is called when it is supposed to not every click on the page.