Until recently I had this set up, which was called multiple times:
$('.rsh')
.draggable('destroy')
.draggable({ blah blah details });
The destroy was there to stop multiple draggable handlers accumulating on the class. New elements are being created by AJAX, and an initial attachment of draggable to the class doesn't touch subsequently created elements.
However, when I updated to version 1.9.2 of jQuery UI, it started giving me this error:
Error: cannot call methods on draggable prior to initialization; attempted to call method 'destroy'
So I removed the destroy line, and it's sweet. Except... I suspect that I may now be adding more and more handlers to the class (which is why the destroy was there in the first place).
I tried this, but it didn't like it:
if ($('.rsh').length) {
$('.rsh').draggable('destroy');
}
Two questions: (1) Will there be more and more handlers attached to the class each time I fire the draggable set up line? (2) If so, any solutions on how to remove them?
No, there won't be extra handlers bound. jQuery registers the initialized instance to the element and won't create a new instance of the same widget for the same element.
As you're worried about handlers, here's a quick check (jQuery 1.8+ and UI 1.9+):
$('div').draggable();
console.log( $._data($('div')[0], 'events') );
$('div').draggable();
console.log( $._data($('div')[0], 'events') );
Fiddle
As you can see, the attached handlers object is not altered after trying to initialize a new draggable instance on the same element.
edit: Subsequent calls with parameters won't be ignored though, rather they will extend the existing widget as shown on #Jason Sperske's answer.
Subsequent calls to .draggable() extend previous calls when attached to the same object (and not replace them as I had originally thought). See this example (extended from Fabrício Matté's) (demo)
<div>foo</div>
<script>
$('div').draggable({
start: function () {
console.log("drag 1");
}
});
console.log($._data($('div')[0], 'events'));
$('div').draggable({
stop: function () {
console.log("drag 2");
}
});
console.log($._data($('div')[0], 'events'));
</script>
In the console.log you see only these messages:
drag 1 <- on start
drag 2 <- on stop
Related
I have a Semantic UI sidebar and I want the page NOT to be dimmed when the sidebar is shown and also I want the onShow event to be fired when the sidebar is completely visible.
$("#sidebar").sidebar onShow: =>
#onShow() if #onShow
$("#sidebar").sidebar 'setting', dimPage: false
Right now, only one of these works, depending on which comes last. Either the page gets dimmed (which is not what I want) and the onShow event gets fired OR the page doesn't get dimmed but the onShow event is never fired.
It looks like the second one is overriding the first settings.
So how do I set both dimPage settings AND set the event handler without overriding each other?
#fstanis's answer is great, but it can be also be separated into 2 separate function calls like below.
$("#sidebar").sidebar("setting", "dimPage", false);
$("#sidebar").sidebar("setting", "onShow", function() {});
This is not that useful in this particular case because we are using the same selector, but it comes in handy when you want to update the settings of 2 different lists of elements, and some elements happen to be included in both lists. Without this separation, the last settings added would override the others.
The sidebar function, like other initializer functions in Semantic UI, can also take an object as a parameter, where you can pass all the settings and events at the same time, so in your case, you can do this:
$("#sidebar").sidebar({
onShow: function() {
console.log('on show event');
},
dimPage: false
});
Further explanation: if you do it in two separate calls, one will override the other. In other words, the code you posted is kind-of equivalent to doing:
$("#sidebar").sidebar({
dimPage: false
});
$("#sidebar").sidebar({
onShow: function() {
console.log('on show event');
}
});
So the second initialization sets dimPage to its default value, which is true.
I have the following code:
var $reviewButton = $('span.review_button');
$reviewButton
.live('click',
function(){
$('#add_reviews').show();
}
)
Later in the script, I use an AJAX call to load some content and another instance of $('span.review_button') enters the picture. I updated my code above to use '.live' because the click event was not working with the AJAX generated review button.
This code works, as the .live(click //) event works on both the static 'span.review_button' and the AJAX generated 'span.review_button'
I see however that .live is depracated so I have tried to follow the jquery documentations instructions by switching to '.on' but when I switch to the code below, I have the same problem I had before switching to '.live' in which the click function works with the original instance of 'span.review_button' but not on the AJAX generated instance:
var $reviewButton = $('span.review_button');
$reviewButton
.on('click',
function(){
$('#add_reviews').show();
}
)
Suggestions?
The correct syntax for event delegation is:
$("body").on("click", "span.review_button", function() {
$("#add_reviews").show();
});
Here instead of body you may use any static parent element of "span.review_button".
Attention! As discussed in the comments, you should use string value as a second argument of on() method in delegated events approach, but not a jQuery object.
This is because you need to use the delegation version of on().
$("#parentElement").on('click', '.child', function(){});
#parentElement must exist in the DOM at the time you bind the event.
The event will bubble up the DOM tree, and once it reaches #parentElement, it is checked for it's origin, and if it matches .child, executes the function.
So, with this in mind, it's best to bind the event to the closest parent element existing in the DOM at time of binding - for best performance.
Set your first selector (in this case, div.content) as the parent container that contains the clicked buttons as well as any DOM that will come in using AJAX. If you have to change the entire page for some reason, it can even be change to "body", but you want to try and make the selector as efficient as possible, so narrow it down to the closest parent DOM element that won't change.
Secondly, you want to apply the click action to span.review_button, so that is reflected in the code below.
// $('div.content') is the content area to watch for changes
// 'click' is the action applied to any found elements
// 'span.review_button' the element to apply the selected action 'click' to. jQuery is expecting this to be a string.
$('div.content').on('click', 'span.review_button', function(){
$('#add_reviews').show();
});
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
There is a 'View' in the model with the event click. After using the Quicksand effects plug-in for jQuery, the objects loose their event handlers. I have tried to add the listener for the event with standard methods in backbone.js:
events: {
"click .objContact" : "openChat"
}
and the same tools jQuery delegate:
var self=this;
this.$el.delegate('.objContact','click', function(){
self.openChat();
});
and live:
var self=this;
this.$el.find('.objContact').live('click', function(){
self.openChat();
});
but the click event disappears.
What could be the problem? And how do I solve it?
UPD: Calling 'Quicksand' is in Backbone.Router (subject to change is obtained directly by means of jQuery, not Backbone), so changes are not handled in Backbone.View
UPD 2: The problem is solved in the following way - by moving the handling of the click event from the View-model to View-collection. And treated with live (did not work in on)
Simple Answer: instead of linking the function to the link with the classic ajax method that is
$('a.oldJqueryClass').click(function(){....
you need to make that function standalone, declaring a new function
function myfunction(params) {alert(params);}
than in the link you call that with the old school way:
Click here
In this way the cloned element will contain itself the call to the function and you can forget about restoring the dom integrity broken by the cloning of quicksand.
I did it in my project, it works fine.
Do a call to delegateEvents() after the related DOM entries have changed or become overwritten. In a traditional Backbone app this is typically done in the render method, but you probably need for figure out when and where quicksand does it's magic (I do not know anything about it), and call delegateEvents that will reactivate the events for the current elements in the DOM.
I have something like:
function init(){
$('.btn').click(function(){
//do something;
}
}
And when new content is added via ajax, I'm calling init(), so that click event applies to new buttons. But when I click it once, it captures several clicks (as many times as I called init()). It makes sense, but how to avoid it?
jsFiddle link: http://jsfiddle.net/s2ZAz/8/
Solutions:
* Use $.delegate() - http://api.jquery.com/delegate/
* Use $.live() - http://api.jquery.com/live/
Less preferred, but still, solutions:
* Use $.off() - http://api.jquery.com/off/ or $.unbind() - http://api.jquery.com/unbind/
click says, "for every object matching the selector, hook up this click listener". You probably want something more like delegate that says "for every object that will ever match this selector, hook up this listener".
$(document).delegate('button', 'click', function() {
});
You will still get double callbacks if you call init twice, but in this manner, you won't have to call init twice, because as new objects are added, they'll already be assigned to click listeners.
Note that document above should be replaced with the nearest persistent ancestor, as per Greg's comment below.
Demo.
Since jQuery 1.7, you can preferably use the .on() function to achieve the same effect.
Demo.
You can use the unbind method to remove the event handler (or the off method if you're using the new jQuery 1.7 syntax for attaching handlers)
Better yet, you can use the live method, to set up the event handler for any elements that are added to the page in the future and match the given selector. In this way you only have to call init once.
$("body").delegate("button", "click", function() {
alert('I\'m annoying!');
});
$('div').append("<button>Click me, I will alert twice</button><br/>");
$('div').append("<button>Click me, I will alert once</button><br/>");
$('div').append("<button>Click me, I will not alert at all</button><br/>");
Try it out
As mentioned by David, and as per liho's delegate example (loved the way the fiddle cascaded how many times the alert would pop!!), the problem is with multiple bindings, which can be solved with .live() (deprecated) or .delegate() (being phased out), or .on() (the preferred). However, it is a mistake to delegate listening to the document or even body node in terms of performance.
A better way to do this is identify an ancestor of the button that will not ever be destroyed. body is an easy choice, but it's almost always the case that we build our pages with wrapper elements of some sort, which are nested one or more levels deeper than body and therefore allow you to set fewer listeners.
HTML:
<div id="someWrapper">
<div class="somethingThatGetsDestroyed">
<button>Click Me</button>
</div>
</div>
JS using jQuery 1.7+:
$('#someWrapper').on('click', 'button', function() {
alert('Clickity-click!');
});