[Proto-Scripty] Event listeners on elements 'written over' by AJAX

14 views
Skip to first unread message

Junkshops

unread,
May 10, 2010, 3:39:23 PM5/10/10
to Prototype & script.aculo.us
Hi all,

I'm wondering what the appropriate thing to do is wrt event listeners
on elements that get replaced by an Ajax.Updater call or something
similar. It seems like a giant pain to have to run through all the sub
elements of the container you're updating and remove all the listeners
for every piece of AJAX on the site, but on the other hand I assume
that if you don't, you wind up with a memory leak since you accumulate
listeners on non-existent events.

Thanks in advance.

--
You received this message because you are subscribed to the Google Groups "Prototype & script.aculo.us" group.
To post to this group, send email to prototype-s...@googlegroups.com.
To unsubscribe from this group, send email to prototype-scripta...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/prototype-scriptaculous?hl=en.

Walter Lee Davis

unread,
May 10, 2010, 4:22:55 PM5/10/10
to prototype-s...@googlegroups.com
What I prefer to do is to add a single persistent observer to an
element that won't be destroyed during the life of the page, and then
delegate listening to that element. So if I had a list of things that
I wanted to observe, and one of the things I was doing was adding or
deleting those list items, what I would do is observe the UL that
holds them, since most events would bubble up from the children
(except certain form element events, like onChange).

<ul id="list">
<li id="elm_1">Element 1 <span class="delete">-</span></li>
... many more elements, all with similar structure
</ul>

$('list').observe('click',function(evt){
var elm = evt.findElement('span.delete');
if(elm){
var id = elm.id.split(/_/).last();
new Ajax.Request('delete.php',{
parameters:{'id',id},
onSuccess:function(transport){
elm.up('li').remove();
}
});
}
});

Obviously, you can handle many more elements and events inside the
same basic observer. If you like, you can use var elm = evt.element();
and then a switch statement to work out what the type of element
instigated the event and branch your logic that way.

Walter

T.J. Crowder

unread,
May 11, 2010, 3:44:38 AM5/11/10
to Prototype & script.aculo.us
Hi,

> I assume
> that if you don't, you wind up with a memory leak since you accumulate
> listeners on non-existent events.

Your assumption is correct, and it's worse than you think: The
elements themselves will stay in memory, too, not just the handlers.
That can add up to a lot of memory consumption over time.

I usually do what Walter does: Delegate rather than watching the
specific elements. If I have a good reason to, I'll watch specific
elements, but then as a by-product of my application logic, I'll
release them before doing the update. But delegation is the default
answer.

HTH,
--
T.J. Crowder
Independent Software Consultant
tj / crowder software / com
www.crowdersoftware.com
> > For more options, visit this group athttp://groups.google.com/group/prototype-scriptaculous?hl=en

Радослав Станков

unread,
May 12, 2010, 6:46:43 AM5/12/10
to Prototype & script.aculo.us
Event delegation is great solution + Prototype now have Element.on
method.

Another thing I want to mention here is that it seams that PrototypeJs
now cleans the element event handlers automatically when using
Element.update ( via new method Element.purge ) -
http://github.com/sstephenson/prototype/commit/c9d6c3ee14747179a1038bc21055f2d6ccd492c4
but this is currently in the repo and not it the official version

Junkshops

unread,
May 12, 2010, 4:11:42 PM5/12/10
to Prototype & script.aculo.us
Hey guys,

Thanks for the advice. I made the suggested changes, but now I've got
a new problem - everything works fine in the good browsers, but, as
usual, doesn't work correctly in IE8. The difference is that in IE8,
instead of the output of the script replacing the contents of a div,
the script output replaces the entire page. IE8 works fine when the
listener is set directly on the form. Code for both situations is
below. Any advice? God I hate microsoft.

Thanks in advance.

______________ IE 8 chokes. The sayThanks div is loaded along with the
form in an ajax load _____________________

function initContactListener() {
$('centercontent').observe('submit', function(evt) { // will need
switch logic if another form added to the center content area
form = evt.findElement();
$('sayThanks').update('<div style="text-align:center"><img
src="images/ajax-loader.gif"></div>');
evt.stop();
new Ajax.Updater('sayThanks', form.action, {parameters:
form.serialize()});
});
}

document.observe('dom:loaded', function() {
initContactListener();
/* removed unrelated code */
});

________________ IE8 is ok - script is eval'd by prototype during an
ajax load of the form page __________________________

<script type="text/javascript">
var formelement = $('contactForm');
var sub = formelement.findFirstElement();
sub.focus()

formelement.observe('submit', function(evt) {
$('sayThanks').update('<div style="text-align:center"><img
src="images/ajax-loader.gif"></div>');
evt.stop();
new Ajax.Updater('sayThanks', this.action, {parameters:
this.serialize()});
});
</script>

T.J. Crowder

unread,
May 13, 2010, 3:41:55 AM5/13/10
to Prototype & script.aculo.us
Hi,

The `submit` event doesn't bubble on IE[1] (and yes, it has been 10+
years since the standard said it should[2]; what's your point? ;-) ),
so you have to watch the form, not a container of the form, so your
second script is your best bet and should work with IE *and* other
browsers.

[1] http://msdn.microsoft.com/en-us/library/aa769569(VS.85).aspx
[2] http://www.w3.org/TR/DOM-Level-2-Events/events.html

HTH,
--
T.J. Crowder
Independent Software Consultant
tj / crowder software / com
www.crowdersoftware.com

Junkshops

unread,
May 13, 2010, 12:53:35 PM5/13/10
to Prototype & script.aculo.us
*shakes fist at microsoft*

So click events bubble but submit events don't. That makes loads of
sense.

Given that, the delegation solution above won't work for forms, so in
that case is the only option to remove listeners prior to ajax loads
that'd overwrite the listeners' elements? If so, I suppose the best
thing to do is to store refs to the listeners in an array and
unregister them all prior to every ajax load.

Thanks for all the responses to my posts you've written over the last
month or so, btw.

Радослав Станков

unread,
May 14, 2010, 1:46:08 AM5/14/10
to Prototype & script.aculo.us
Submit don't bubble but could be simulated. I have been working on
simulating the bubbling for submit/reset/change/focus/blur, but I
don't have any free time those days:

(function(){
function isEventSupported(eventName){
var element = document.createElement("div"), result;

eventName = "on" + eventName;
if (eventName in element){
return true;
}

element.setAttribute(eventName, "return;");
result = Object.isFunction(element[eventName]);
element = null;

return result;
}

if (!isEventSupported('submit')){
function isForm(element) {
return Object.isElement(element) &&
element.nodeName.toUpperCase() == 'FORM';
}

function skipOneSubmit(e){
Event.preventDefault(e);
Event.stopObserving(this, 'submit', skipOneSubmit);
}

function emulateSubmit(e, element){
if (element.form && element.fire('emulated:submit').stopped){
Event.observe(element.form, 'submit', skipOneSubmit);
}
}

document.on('mousedown', 'input[type=submit],input[type=image]',
emulateSubmit);
document.on('keydown',
'input[type=file],input[type=text],input[type=password],input[type=submit]',
function(e, element){
if (e.keyCode == Event.KEY_RETURN){
emulateSubmit(e, element);
}
});

Event.Handler.prototype.initialize =
Event.Handler.prototype.initialize.wrap(function(initialize, element,
eventName, selector, callback) {
if (eventName == 'submit' && !isForm(element)){
eventName = 'emulated:' + eventName;
}

initialize(element, eventName, selector, callback);
};
}
})();

This is just cut - paste from a bigger script, so it could contain
errors. I still haven't tested.
Hope this will could be some help, if try using it please send me some
feedback :)

Peter De Berdt

unread,
May 14, 2010, 12:56:44 PM5/14/10
to prototype-s...@googlegroups.com

On 13 May 2010, at 18:53, Junkshops wrote:

*shakes fist at microsoft*

So click events bubble but submit events don't. That makes loads of
sense.

Given that, the delegation solution above won't work for forms, so in
that case is the only option to remove listeners prior to ajax loads
that'd overwrite the listeners' elements? If so, I suppose the best
thing to do is to store refs to the listeners in an array and
unregister them all prior to every ajax load.

NWEvents supports bubbling of the submit, focus and blur events. We're using it to do event delegated form validation (amongst others) and it works like a charm.


You can just use NWEvents and NWMatcher to define the event handler and then use Prototype for the actual event handling.


Best regards


Peter De Berdt


T.J. Crowder

unread,
May 14, 2010, 5:42:38 PM5/14/10
to Prototype & script.aculo.us
> You can just use NWEvents and NWMatcher to define the event handler  
> and then use Prototype for the actual event handling.

...and in Prototype 1.7, you can swap NWMatcher in as the selection
engine as well. One of the cool things about Prototype 1.7[1] is that
you can use it with any of several selection engines -- its new
default Sizzle engine, NWMatcher, the legacy Prototype engine, etc.

[1] http://prototypejs.org/2010/4/5/prototype-1-7-rc1-sizzle-layout-dimensions-api-event-delegation-and-more

-- T.J.
Reply all
Reply to author
Forward
0 new messages