Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Clone node with events

1,854 views
Skip to first unread message

Cezary Tomczyk

unread,
Jan 10, 2013, 6:33:45 AM1/10/13
to
"Cloning a node copies all of its attributes and their values but does
not copy event listeners." -
https://developer.mozilla.org/en-US/docs/DOM/Node.cloneNode

Any idea how to in an efficient and proper way clone node with events?

--
Cezary Tomczyk
http://www.ctomczyk.pl/

Scott Sauyet

unread,
Jan 10, 2013, 10:30:43 AM1/10/13
to
Cezary Tomczyk wrote:
> "Cloning a node copies all of its attributes and their values but does
> not copy event listeners." -https://developer.mozilla.org/en-US/docs/DOM/Node.cloneNode
>
> Any idea how to in an efficient and proper way clone node with events?

One problem is that in general it's not always clear that events
should be cloned, or that if they are cloned that they will now do
what you'd expect.

If you have this structure:

Div 1
Div 2
Checkbox 2.1
EventListener 2.1.1 : toggles Div 1
Div 2.2
Checkbox 2.3
EventListener 2.3.1 : toggles Div 2.2

And want to clone Div 2, all its children and their events, it mostly
makes sense:

Div 2a
Checkbox 2.1a
EventListener 2.1.1a : toggles Div 1
Div 2.2a
Checkbox 2.3a
EventListener 2.3.1a : ??

But what would EventListener 2.3.1a do? Should it toggle Div 2.2?
That seems strange. Probably toggling Div 2.2a makes more sense, but
how we would we distinguish this from EventListener 2.1.1a, which
doesn't change it's target? And of course this doesn't even address
the fact that we don't actually have a way to convert an event
listening function that toggles the visibility of one DIV into one
that toggles the visibility of another. So we don't really know what
to do or how to do it. :-)

So event listeners that were copied would have to be smart enough to
determine their behavior based on where they were triggered. It gets
ugly.

-- Scott

Stefan Weiss

unread,
Jan 10, 2013, 11:15:44 AM1/10/13
to
On 2013-01-10 12:33, Cezary Tomczyk wrote:
> Any idea how to in an efficient and proper way clone node with events?

That doesn't seem like a good idea in the first place (for the reason
Scott mentioned), but maybe there's a better way. When you have a number
of elements that should react in a similar way to an event, consider
using event delegation instead of separate listeners for each element.

Simply put, that means attaching one listener to a common parent
element, and resolving the event target from there. This works
especially well for click events; other event types (like mouseover,
mouseout) can be a little more tricky. Some events do not bubble at all
(focus, blur, change, etc).

If you can't use event delegation, use a wrapper function to
create/clone the element and attach the listeners at the same time:

function handleFocus (evt) {}

function makeInput (node) {
var input = node ? node.cloneNode()
: document.createElement("input");
input.addEventListener("focus", handleFocus);
return input;
}

- stefan

Cezary Tomczyk

unread,
Jan 12, 2013, 4:57:50 PM1/12/13
to
W dniu 2013-01-10 16:30, Scott Sauyet pisze:
> Cezary Tomczyk wrote:
[...]
Yes, that situation shows that cloning with events is not so clear. But
consider this simple example:

http://jsfiddle.net/JpLx2/

All I need here is to just copy attached events from old_node to
new_node, but seems that is not so easy to do. This could be possible
done by, for example, adding some wrapper to event methods and then use it.

On the other hand I found
http://www.w3.org/TR/2001/WD-DOM-Level-3-Events-20010823/events.html#Events-EventListenerList,
but seems that it is not implemented, for example, in Firefox 17.0.1 and
Opera 12.12. Probably in other browsers also is not implemented :-(

> So event listeners that were copied would have to be smart enough to
> determine their behavior based on where they were triggered. It gets
> ugly.

Seems it is not so easy case ;-)

Cezary Tomczyk

unread,
Jan 12, 2013, 5:00:46 PM1/12/13
to
W dniu 2013-01-10 17:15, Stefan Weiss pisze:
> On 2013-01-10 12:33, Cezary Tomczyk wrote:
>> Any idea how to in an efficient and proper way clone node with events?
>
> That doesn't seem like a good idea in the first place (for the reason
> Scott mentioned), but maybe there's a better way. When you have a number
> of elements that should react in a similar way to an event, consider
> using event delegation instead of separate listeners for each element.

Yes, I know event delegation. They are powerful :-)

> Simply put, that means attaching one listener to a common parent
> element, and resolving the event target from there. This works
> especially well for click events; other event types (like mouseover,
> mouseout) can be a little more tricky. Some events do not bubble at all
> (focus, blur, change, etc).
>
> If you can't use event delegation, use a wrapper function to
> create/clone the element and attach the listeners at the same time:
>
> function handleFocus (evt) {}
>
> function makeInput (node) {
> var input = node ? node.cloneNode()
> : document.createElement("input");
> input.addEventListener("focus", handleFocus);
> return input;
> }

That is the only way, as I see, at this moment. But this is useful when
you know what type of events are attached. Otherwise, as I mentioned in
reply to Scott, it is better to write some wrapper to event methods and
use collected data (I mean attached events) to attach them again after
replacing element.

Thomas 'PointedEars' Lahn

unread,
Jan 13, 2013, 9:07:19 AM1/13/13
to
Cezary Tomczyk wrote:

> […]
> consider this simple example:
>
> http://jsfiddle.net/JpLx2/
>
> All I need here is to just copy attached events from old_node to
> new_node, but seems that is not so easy to do. This could be possible
> done by, for example, adding some wrapper to event methods and then use
> it.

First of all, you are _not_ copying events. You could be copying event
*listeners*, but all you would copy in ECMAScript would be *references* to
Function instances.

So there is no problem here. You have a reference to a Function instance,
and the “click” event bubbles in all know DOM implementations. So, as
already suggested, you should use event delegation, in particular use event
bubbling: Add to a common ancestor element a listener for that event that
does actions only for specific event targets. When a cloned target receives
that event, it has no listener for it; since the event bubbles, it will be
handled by the ancestor element which has a listener for it.

BTW, you should not be using attachEvent() as an alternative to
addEventListener():

<http://www.quirksmode.org/blog/archives/2005/08/addevent_consid.html>

> On the other hand I found
> http://www.w3.org/TR/2001/WD-DOM-Level-3
> Events-20010823/events.html#Events-EventListenerList,
> but seems that it is not implemented, for example, in Firefox 17.0.1 and
> Opera 12.12. Probably in other browsers also is not implemented :-(

Neither is it implemented in WebKit WebCore as of Chromium 22. You are
referring to an *old* Working Draft. The *current* Working Draft for W3C
DOM Level 3 Events is available at

<http://www.w3.org/TR/2012/WD-DOM-Level-3-Events-20120906/>.

You will notice that the EventTarget interface no longer has an
“eventListeners” attribute of type “EventListenerList”, and that the
“EventListenerList” interface has been removed:

<http://www.w3.org/TR/2012/WD-DOM-Level-3-Events-20120906/#interface-
EventTarget>

And it would seem that no replacement for that attribute or interface has
been suggested yet.

However, with proprietary event handler properties (called “traditional
event registration” on QuirksMode.org) now perhaps going to be a Web
standard with the HTML5 API, for elements and events for which only one
event listener has been added, you could use those properties to retrieve
the primary event listener for an event on an element:

<http://www.w3.org/html/wg/drafts/html/master/index.html#ix-event-handlers>
<http://www.w3.org/html/wg/drafts/html/master/webappapis.html#event-
handlers>

While that has worked even before HTML5 (jsx.dom.*EventListener() are based
on it), I recommend you use event bubbling here.

--
PointedEars

Twitter: @PointedEars2
Please do not Cc: me. / Bitte keine Kopien per E-Mail.

RobG

unread,
Jan 14, 2013, 1:07:58 AM1/14/13
to
On Thursday, January 10, 2013 9:33:45 PM UTC+10, Cezary Tomczyk wrote:
> "Cloning a node copies all of its attributes and their values but does
> not copy event listeners." -
>
> https://developer.mozilla.org/en-US/docs/DOM/Node.cloneNode

That is not true: in–line event handlers are cloned, as are those added using attachEvent (in IE at least). Only listeners added using addEventListener or as properties (e.g. node.onchange = …) are not cloned.


--
Rob

Cezary Tomczyk

unread,
Jan 16, 2013, 2:50:03 PM1/16/13
to
W dniu 2013-01-13 15:07, Thomas 'PointedEars' Lahn pisze:
> Cezary Tomczyk wrote:
[...]
> First of all, you are _not_ copying events. You could be copying event
> *listeners*, but all you would copy in ECMAScript would be *references* to
> Function instances.
>
> So there is no problem here. You have a reference to a Function instance,
> and the “click” event bubbles in all know DOM implementations. So, as
> already suggested, you should use event delegation, in particular use event
> bubbling: Add to a common ancestor element a listener for that event that
> does actions only for specific event targets. When a cloned target receives
> that event, it has no listener for it; since the event bubbles, it will be
> handled by the ancestor element which has a listener for it.

Yes, that's a good idea and I know that. But, seems that I was
misunderstood. I want to clone node where I do not know if there are
attached event listeners or not. The only one thing that can I do is
write wrapper on addEventListener and attachEvent to collect all event
listeners and reattach them to specified node after cloning.

> BTW, you should not be using attachEvent() as an alternative to
> addEventListener():

> <http://www.quirksmode.org/blog/archives/2005/08/addevent_consid.html>

Right. I should use "else if".

>> On the other hand I found
>> http://www.w3.org/TR/2001/WD-DOM-Level-3
>> Events-20010823/events.html#Events-EventListenerList,
>> but seems that it is not implemented, for example, in Firefox 17.0.1 and
>> Opera 12.12. Probably in other browsers also is not implemented :-(
>
> Neither is it implemented in WebKit WebCore as of Chromium 22. You are
> referring to an *old* Working Draft. The *current* Working Draft for W3C
> DOM Level 3 Events is available at
>
> <http://www.w3.org/TR/2012/WD-DOM-Level-3-Events-20120906/>.

Ah, yes. My link is very old :/

> You will notice that the EventTarget interface no longer has an
> “eventListeners” attribute of type “EventListenerList”, and that the
> “EventListenerList” interface has been removed:
>
> <http://www.w3.org/TR/2012/WD-DOM-Level-3-Events-20120906/#interface-
> EventTarget>
>
> And it would seem that no replacement for that attribute or interface has
> been suggested yet.
>
> However, with proprietary event handler properties (called “traditional
> event registration” on QuirksMode.org) now perhaps going to be a Web
> standard with the HTML5 API, for elements and events for which only one
> event listener has been added, you could use those properties to retrieve
> the primary event listener for an event on an element:
>
> <http://www.w3.org/html/wg/drafts/html/master/index.html#ix-event-handlers>
> <http://www.w3.org/html/wg/drafts/html/master/webappapis.html#event-
> handlers>
>
> While that has worked even before HTML5 (jsx.dom.*EventListener() are based
> on it), I recommend you use event bubbling here.

I know that I can use event bubbling, but as I mentioned, sometimes I do
not know if node has an attached any listeners or not. But I think that
I can do something like I described above.

Heh, I event found your old post
http://www.velocityreviews.com/forums/t936046-list-events-of-an-element-wired-up-with-addeventlistener.html,
but this is what I meant :-)
0 new messages