JQuery Ajax - Rebinding elements with the context param instead of delegate? - ajax

So we all know in an ajax update events must be rebound to new dom elements. Yes delegate is an option, but delegate doesn't work for all scenarios. For instance delegate won't help for something that needs to be done simply on load rather than on a click event.
Rather than split my code into delegate handlers and handlers that need to be rebound on updates, I would rather define a single method with a context parameter that gets called every time the page changes like so:
function onPageUpdate(context) {
$('a', context).click(...); // event handlers
$('.chart', context).addClass(...); // load handlers
}
On dom ready this will be called with the context parameter null. On an ajax update the context will container the new dom elements. This way I'll never have to worry about delegating or ajax updates again.
I'm having trouble getting this to work however. Given the ajax callback:
function onSuccess(data) {
// data contains new dom elements like: <div><a>Click</a><span>chart<span></div>
// replace old elements with new ones
$('a').replaceWith('a', data);
$('span').replaceWith('span', data);
// call pageUpdate with the new context
onPageUpdate(data);
}
Is it possible to make this work like I expect? The replacing works fine, but onPageUpdate isn't binding anything to these new elements, I don't know if thats because the context is just a string object or what. Can anyone think of a way to make this work?
In my mind this is a better solution than delegate, because theres only one method for all handlers and only the elements that need a binding will own it.

From jQuery() - jQuery API
jQuery( selector [, context] )
selector A string containing a selector expression
context A DOM Element, Document, or jQuery to use as context
If the context isn't the correct type - such as passing a string - it's simply going to be ignored. Try wrapping your HTML string in a jQuery object, then use that as the context for your selectors, like so:
var $context = $(data);
$('a').replaceWith('a', $context);
$('span').replaceWith('span', $context);
// call pageUpdate with the new context
onPageUpdate($context);

I won't answer to your question, Anthony Grist's answer is quite right, but there are some things you said that I don't understand. Could you explain them to me ?
For instance delegate won't help for something that needs to be done simply on load rather than on a click event
Sorry but I don't get it, do you have an example ?
In my mind this is a better solution than delegate, because theres
only one method for all handlers and only the elements that need a
binding will own it.
I don't get it too:
only one method: which method are you talking about ?
only the elements ... will own it: it's also the case with delegate, and more, if you have let's say 10 anchor element, only ONE handler would be bound, instead of 10 in your solution
On your method onPageUpdate you're mixing two things: event handling ($("a").click(...)) and DOM modification ($(".char").addClass(...)). Maybe that's why you're confusing about delegate's ability to resolve your problem.

Related

Is there a way to implement "onShow" using Prototype JS?

I searched the Prototype docs but found nothing. Did I miss something, or is there no such thing as onshow? I want to call a function when an element (a div in my case) is made visible. Is there such functionality in the Prototype framework? If not, I need a push in the right direction so I can solve it another way.
Thanks!
If you are asking if there is an event that is fired when the .show() method is called on elements there is not.
But you can test if an element is visible by using the $('myelementid').visible() method.
Also a different way I have done it in the past is using the Script.aculo.us .appear() method which has a afterFinish callback - for example
$('myelementid').appear({'afterFinish':function(effect){
//the afterFinish callback passes the effect as the first parameter
//call other function with element
otherfunction(effect.element);
});
technically you would only need to load the effects.js file of Script.aculo.us instead of all of the library to get the core effects methods.

How to use Backbone.EventBinder with views

Referring to this post on Backbone.EventBinder, I am lost on how to use EventBinder with Backbone views (which is the most popular use case). Is it still recommend to add a close() method to the Backbone.View prototype and a onClose() method to the view as suggested in this post? Also where does one store the binder object, so that binder.unbindAll() can be called on close? What is the recommended way to close child views (e.g. a parent view on a collection which has child views on the associated models). A working example would be a great addition to the Backbone.EventBinder project.
Yes, you should still add a close method to your views. The EventBinder does not negate any of what that Zombies post says. Rather, it helps to automate a lot of the process by making it easier to unbind all of your events within the view.
Take a look at the Marionette.View source code for an example of how it's used:
https://github.com/marionettejs/backbone.marionette/blob/master/src/marionette.view.js#L9
https://github.com/marionettejs/backbone.marionette/blob/master/src/marionette.view.js#L16
https://github.com/marionettejs/backbone.marionette/blob/master/src/marionette.view.js#L97
If you're using Marionette, you don't need to add the close method yourself, or add the event binder yourself. That's handled for you.
If you want to add this to your own views, it's easy:
MyView = Backbone.View.extend({
initialize: function(){
// add the event binder
this.eventBinder = new Backbone.EventBinder();
// bind some stuff
this.eventBinder.bindTo(this.model, "change:foo", this.doStuff, this);
},
close: function(){
// ... other stuff
this.eventBinder.unbindAll();
}
});

jQuery 'on' not registering in dynamically generated modal popup

I was under the impression that jQuery's on event handler was meant to be able to 'listen' for dynamically created elements AND that it was supposed to replace the behavior of live. However, what I have experienced is that using on is not capturing the click event whereas using live is succeeding!
The tricky aspect of my situation is that I am not only dynamically creating content but I'm doing it via an AJAX .get() call, and inserting the resultant HTML into a modal .dialog() jQueryUI popup.
Here is a simplified version of what I was trying to accomplish (wrapped in $(document).ready(...) ):
$.get("getUserDataAjax.php", queryString, function(formToDisplay) {
$("#dialog").dialog({
autoOpen: true,
modal: true,
buttons...
}).html(formToDisplay);
});
$(".classThatExistsInFormToDisplay").on("click", function() {
alert("This doesn't get called");
});
From the documentation for on I found this which which was how I was approaching writing my on event:
$("p").on("click", function(){
alert( $(this).text() );
});
However, for some reason, live will work as I expect -- whereas on is failing me.
This isn't a question for "how can I make it work" because I have found that on will succeed (capture clicks) if I declare it inside the function(formToDisplay) callback.
My question is: what is wrong with on that it isn't finding my dynamically created elements within a modal popup? My jQuery instance is jquery-1.7.2. jQueryUI is 1.8.21.
Here are two jsFiddles that approximate the issue. Click the word "Test" in both instances to see the different behavior. The only difference in code is replacing on for live.
Where the click is captured by live.
Where the click is NOT captured by on (click 'Test - click me' to see nothing happen).
I realize I may just be using on inappropriately or asking it to do something that was not intended but I want to know why it is not working (but if you have something terribly clever, feel free to share). Thanks for your wisdom!
Update / Answer / Solution:
According to user 'undefined', the difference is that on is not delegated all the way from the top of the document object whereas live does/is.
As Claudio mentions, there are portions of the on documentation that reference dynamically created elements and that what you include in the $("") part of the query needs to exist at runtime.
Here is my new solution: Capture click events on my modal dialog, which, although it does not have any content when the event is created at runtime, will be able to find my content and element with special class that gets generated later.
$("#dialog").on("click", ".classThatExistsInFormToDisplay", function() {
... //(success! Event captured)
});
Thanks so much!
live delegates the event from document object, but on doesn't, if you want to delegate the event using on method, you should delegate the event from one of static parents of the element or document object:
$(document).on("click", ".clickHandle", function() {
alert("Content clicked");
});
The problem is that the element to which you attach the event has to exist.
You have to use on like this to capture clicks on p tags created dynamically
$("#existingContainerId").on("click", "p", function(){
alert( $(this).text() );
});
if you have no relevant existing container to use, you could use $("body") or $(document)
If selector is omitted or is null, the event handler is referred to as direct or directly-bound. The handler is called every time an event occurs on the selected elements, whether it occurs directly on the element or bubbles from a descendant (inner) element.
When a selector is provided, the event handler is referred to as delegated. The handler is not called when the event occurs directly on the bound element, but only for descendants (inner elements) that match the selector. jQuery bubbles the event from the event target up to the element where the handler is attached (i.e., innermost to outermost element) and runs the handler for any elements along that path matching the selector.
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(). To ensure the elements are present and can be selected, perform event binding inside a document ready handler for elements that are in the HTML markup on the page. If new HTML is being injected into the page, select the elements and attach event handlers after the new HTML is placed into the page. Or, use delegated events to attach an event handler, as described next
Take a look to section Direct and delegated events here for more details

Backbone style: how to deal with the el element of a view

I find in several backbone examples different ways to do the same thing
I'd just like to know if any of them is better, and in case they're exactly the same which is the more accepted style
$('#header').html(new HeaderView().render());
vs
new HeaderView({el:$('#header')).render();
and
this.$el.html( this.template() );
vs
$(this.el).html( this.template() );
and finnally
render: function() {
[...]
return this.el;
}
vs
render: function() {
[...]
return this;
}
Send, or not to send, el in the constructor
I think always that is possible is better to send el in the constructor, this way the JS logic will be more decoupled from the DOM structure and not any DOM element name is hard-coded into the JS code.
This is also better for testing proposes so you don't need to recreate the production DOM structure in your tests and you can create a dummy DOM structure and instantiate your Views making reference to any DOM element in your dummy DOM structure.
Also the View becomes more reusable.
Using this.$el
I think in a performance point of view is better to use:
this.$el
VS
$(this.el)
Due the first option is already precompiled. jQuery consume resources transforming a simple DOM element in a jQuery DOM element and if you use this.$el this is done only once.
What to return in render()
As #muistooshort has said, the standard is to return this. Normally when you call render() what you only need from outside is el so returning el has a lot of logic but people has agreed to returning this.
In some way it has its logic due this expression looks awkward:
$('#header').html(new HeaderView().render())
But this other looks very much intuitive and self explained:
$('#header').html(new HeaderView().render().el)

how does jquery's promise method really work?

I don't really understand what delegate and promise are.
According to the docs -
delegate would bind a selector and event to some sort of wrapping container that can be used again at a later time for current and future items.
promise() would remap things back to when it was first bounded if everything newly loaded matches. Maybe I don't really understand this promise method.
What if the wrapper is still there, but the contents in the wrapper container have changed, and/or reloaded via Ajax? Why is it that the events are not triggering or working as it would the first time it is bound?
And yes, I have been to the docs page, I just don't understand their explanations completely.
I'm a bit confused by this question. I think this is because you are confused by promise and delegate. They are in fact completely unrelated features of jQuery. I'll explain each separately:
delegate
delegate is a feature of jQuery that was introduced in jQuery 1.4.2. (It is a nicer approach to the live feature that was added in jQuery 1.3). It solves a particular problem that comes with modifying the DOM, and particularly with AJAX calls.
When you bind an event handler, you bind it to a selection. So you might do $('.special').click(fn) to bind an event handler to all the members of the special class. You bind to those elements, so if you then remove the class from one of those elements, the event will still be triggered. Inversely, if you add the class to an element (or add a new element into the DOM), it won't have the event bound.
There is a feature of Javascript that mitigates this called "event bubbling". When an event is triggered, first the browser notifies the element where the event originated. Then it goes up the DOM tree, and notifies each ancestor element. This means that you can bind an event handler on an element high up the DOM tree, and events triggered on any child elements (even those that don't exist when the handler was bound).
delegate is jQuery's implementation of this. First, you select a parent element. Then you specify a selector – the handler will only be run if the originating element matches this selector. Then you specify an event type, such as click, submit, keydown, just as with bind. Then finally you specify the event handler.
$('#containingElement').delegate('a.special', 'click', function() {
alert('This will happen on all links with the special class');
});
promise
promise is another relatively recent addition to the jQuery featureset. It is part of the Deferred concept that was introduced in jQuery 1.5. (I think the similarity in sound between "deferred" and "delegate" is probably the source of confusion.) This is a way of abstracting away the complications of asynchronous code. The best example of this is with AJAX calls, as the object returned by $.ajax is a Deferred object. For instance:
$.ajax({
url: 'somepage.cgi',
data: {foo: 'bar'}
}).done(function() {
// this will be run when the AJAX request succeeds
}).fail(function() {
// this will be run when the AJAX request fails
}).always(function() {
// this will be run when the AJAX request is complete, whether it fails or succeeds
}).done(function() {
// this will also be run when the AJAX request succeeds
});
So it is in many ways the same as binding success handlers in the $.ajax call, except that you can bind more than one handler, and you can bind them after the initial call.
Another time when it would be useful to deal asynchronously is with animations. You can provide callbacks to functions, but it would be nicer to do this with similar syntax to the AJAX example I've provided above.
In jQuery 1.6, this functionality was made possible, and promise is part of this implementation. You call promise on a jQuery selection, and you'll get an object that you can bind event handlers to, when all the animations in the object have completed.
For instance:
$('div.special').fadeIn(5000).promise().then(function() {
// run when the animation succeeds
}).then(function() {
// also run when the animation succeeds
});
Again, this is similar in effect to traditional methods, but it adds flexibility. You can bind the handlers later, and you can bind more than one.
Summary
Basically, there is no significant relationship between delegate and promise, but they're both useful features in modern jQuery.

Resources