Entry tags:
"updatedcontent" custom event + $("*").live()
One background issue that keeps cropping up is that with our current JS code, we need to explicitly initialize handlers for any new content that's loaded on the page. That's why, for example, AJAXified polls and the cut tag expander wouldn't play nice at first. We had to tell the cut tag expander to explicitly initialize the JS for any polls in the newly loaded cut tag content.
As you can imagine (Bob) this isn't very fun. Inevitably, we end up forgetting edge cases, which we only turn up after a user has run into the issue and reported it. (We probably have three or four outstanding bugs that have to do with how we haven't called the init function on this page, or after doing that action, and well it's a pain.)
So a thought: I propose we start using jQuery's .live() function in conjunction with a custom event. For example, here is what I have for initializing polls:
Then, in various places after a successful AJAX request and update of poll content, we take the element which has the new content and trigger the "updatedcontent" event:
The resultsEle contains the HTML for the poll results; the "View Answers" link within the poll need JS applied
$answerEle contains the detailed answers view for individual questions; the links for pagination (if any!) need JS applied
replaceDiv is the container containing an entry; we'll need to apply JS for any placeholders or polls inside. We just triggered the event to notify everything else; now we don't need to care what else is out there. (the $.dw.cuttag.initLinks was from before; it's possible that we can modify to remove that call)
Contrast this with cuttag-ajax.js (old library):
We had to call the init functions of other modules directly, and any time we add new JS functionality, we'd need to remember to update everything.
The advantage to all this is that we don't need to care about what else is on the page, or what needs to be made to work on the new content; we just broadcast that there have been changes on $element, and then the listeners need only to check whether there's anything in the updated element that interests them.
Now thoughts about efficiency:
At first glance, it looks like listening for an event on all elements could be bad. Normally, we look at the element first, and then the list of events on that element, so if we're looking at all elements on the page each time any event is triggered, related or not, that'd be a pretty big hit. However, it turns out that .live is pretty clever in what it does.
The .live events aren't bound to a specific element. Instead of needing to look at all elements in order to get the element's event handlers .live() and looks up the event type first -- and then checks whether the element that triggered the event matches up to what we want to process this event.
Thoughts? I'd like to encourage moving all the init code to this form of event handling as we jQuery the files.
ETA:
It looks like .delegate() is a better alternative to .live() (thanks
jproulx!
As you can imagine (Bob) this isn't very fun. Inevitably, we end up forgetting edge cases, which we only turn up after a user has run into the issue and reported it. (We probably have three or four outstanding bugs that have to do with how we haven't called the init function on this page, or after doing that action, and well it's a pain.)
So a thought: I propose we start using jQuery's .live() function in conjunction with a custom event. For example, here is what I have for initializing polls:
jQuery(document).ready(function($){
// make all polls dynamic on page load
$(".poll-container").dynamicpoll()
// for any event triggered by an update to entry content
// or an update to poll content
// (but *not* just any other content updates)
$("*").live("updatedcontent.entry updatedcontent.poll", function(e) {
// we take the newly updated object, which may contain a poll
// or else be a poll
// and make that poll dynamic
$(this).find(".poll-container").andSelf().dynamicpoll();
});
});
Then, in various places after a successful AJAX request and update of poll content, we take the element which has the new content and trigger the "updatedcontent" event:
in jquery.poll.js
// after we submit a vote
resultsEle.trigger( "updatedcontent.poll" );
The resultsEle contains the HTML for the poll results; the "View Answers" link within the poll need JS applied
in jquery.poll.js
// when we get back results after clicking the "View Answers" link
$answerEle.trigger( "updatedcontent.poll" );
$answerEle contains the detailed answers view for individual questions; the links for pagination (if any!) need JS applied
in jquery.cuttag-ajax.js
// just expanded the cut tag
replaceDiv.trigger( "updatedcontent.entry" );
$.dw.cuttag.initLinks(replaceDiv);
replaceDiv is the container containing an entry; we'll need to apply JS for any placeholders or polls inside. We just triggered the event to notify everything else; now we don't need to care what else is out there. (the $.dw.cuttag.initLinks was from before; it's possible that we can modify to remove that call)
Contrast this with cuttag-ajax.js (old library):
CutTagHandler.initLinks(replaceDiv);
LiveJournal.initPlaceholders(replaceDiv);
LiveJournal.initPolls(replaceDiv);
We had to call the init functions of other modules directly, and any time we add new JS functionality, we'd need to remember to update everything.
The advantage to all this is that we don't need to care about what else is on the page, or what needs to be made to work on the new content; we just broadcast that there have been changes on $element, and then the listeners need only to check whether there's anything in the updated element that interests them.
Now thoughts about efficiency:
At first glance, it looks like listening for an event on all elements could be bad. Normally, we look at the element first, and then the list of events on that element, so if we're looking at all elements on the page each time any event is triggered, related or not, that'd be a pretty big hit. However, it turns out that .live is pretty clever in what it does.
The .live events aren't bound to a specific element. Instead of needing to look at all elements in order to get the element's event handlers .live() and looks up the event type first -- and then checks whether the element that triggered the event matches up to what we want to process this event.
Thoughts? I'd like to encourage moving all the init code to this form of event handling as we jQuery the files.
ETA:
It looks like .delegate() is a better alternative to .live() (thanks
![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)