How can a DIV node been "detached" and what's the use? - javascript

In this article a detached Div node is created:
http://www.bennadel.com/blog/1008-jQuery-empty-Kills-Event-Binding-On-Persistent-Nodes.htm
I don't understand : I thought that DOM owns every nodes. How would you attach to DOM then ?
Last but not least what would be the purpose of having detached node ?

I'm not sure which answer you expect, so here are some thoughts:
I thought that DOM owns every nodes.
The document owns every node. Each node has an ownerDocument [MDN] property.
From the specification:
The Document interface represents the entire HTML or XML document. Conceptually, it is the root of the document tree, and provides the primary access to the document's data.
Since elements, text nodes, comments, processing instructions, etc. cannot exist outside the context of a Document, the Document interface also contains the factory methods needed to create these objects. The Node objects created have a ownerDocument attribute which associates them with the Document within whose context they were created.
How would you attach to DOM then?
There are various ways to insert a new node, such as appendChild [docs] or insertBefore [docs].
Last but not least what would be the purpose of having detached node ?
One advantage is that you can build complex subtrees offline so that the browser does not have to recalculate the layout every time you insert a node.
Sometimes it is also useful for parsing an HTML string. By creating an empty, detached div and assign the HTML string to innerHTML, you can parse and process the HTML string easily.
The only caveat is that document.getElementById cannot find nodes which are not part of the tree.
Also interesting in this regard might be the explanation for the Node.parentNode property. After all, a Node which does not have parent is not part of the tree:
The parent of this node. All nodes, except Attr, Document, DocumentFragment, Entity, and Notation may have a parent. However, if a node has just been created and not yet added to the tree, or if it has been removed from the tree, this is null.

with document.createElement(), you can create an element node
var p = document.createElement("p");
At this point though, it will exist in memory but will not have been attached to the DOM.
There are numerous ways in which a node can be attached to the DOM, but probably the easiest would be using element.appendChild(node)
var p = document.createElement("p");
// attach the newly created node to the document body
document.body.appendChild(p);
You may want to create the element first, manipulate it and then attach to the DOM so that your manipulations do not cause browser reflow e.g. if you're setting the background colour, border, appending child elements, etc. you want to do this in memory without each change having to be reflected as a visual change in the browser.

Modifying the DOM is expensive. You can create a detached object, set it up the way you need to (adding its attributes, binding event handlers, etc). After its setup the way you want, then append it to the DOM.

You can definitely create elements that are not part of the DOM.
var someElement = document.createElement("div");
var someOtherElement = $("<div>");
Performing operations on detached elements are far more efficient than performing operations on attached elements because detached elements do not have to be rendered.

The page you linked to uses .empty() which detaches elements from the DOM and removes all event handlers from those elements.
There is also .detach(), which may be what you mean. .detach() basically just, well, detaches the node from the DOM. It's not part of the DOM anymore; it's just hanging around in memory (as long as you keep it in a variable). This means that if you discard the variable, you've lost the detached node forever (it's not in the DOM nor in a variable).
A use case is e.g. temporarily "removing" an element from the DOM without actually dismissing it, so that you can attach it later (using .append/.after/etc).
var detached = $("...").detach();
// later:
$("body").append(detached);

Related

How to access browser's DocumentFragment nodes?

For the page chrome://history, in Developer Tools, the DOM Elements tree shows nodes of type DocumentFragment like the following #shadow-root (open) which you can see selected in the screenshot below:
My question is how to use querySelector or another Javascript method to select such nodes ?
Unlike what another answer in this site suggested, DocumentFragment.querySelector doesn't exist.
As explained on developer.mozilla.org,
The ShadowRoot interface of the Shadow DOM API is the root node of a DOM subtree that is rendered separately from a document's main DOM tree.
You can retrieve a reference to an element's shadow root using its Element.shadowRoot property, provided it was created using Element.attachShadow() with the mode option set to open.
In your case, it looks like you need to do
var shroot = document.getElementById('history-app').shadowRoot
.getElementById('history').shadowRoot;
As you can see, we (apparently) have to browse down the DOM step-by-step. I.e. trying for example to access the 'history' element directly won't work since it is itself inside a shadowRoot.

Is it possible to know in javascript if a node or its children has eventListeners attached to it?

So I have a dynamically generated but sandboxed iframe, a playground, that will execute and render user written code i.e. JavaScript. The user is able to write anything in HTML and attach events to the nodes they write using JS. Think Codepen.
On the parent, I have some interactions that determines and juggles the iframe around including removing it from the DOM completely. In this moment of removal there is a need to first detached events from the content of iframe… or do whatever is recommended to avoid memory leakage.
Any suggestions/tips on this?
You do not need to detach event listeners manually before removing your iframe.
When your iframe is removed, make sure that you null out any references pointing to it. Then, the contained document and all nodes within it (and event listeners attached to these nodes) will be automatically queued for garbage collection.

Event handlers, closures and garbage collection in Javascript

I'm not running into a memory leak in my application yet, but I'm worried about possible problems in the future. I would like to know if doing something like this:
SomeClass.prototype.someMethod= function() {
var that= this
this.$div2.click(function() {
that.someMethod2();
});
}
And lets say that this.$div2 is appended to another div this.$div1. If I call
this.$div1.remove();
and later loses the reference of my SomeClass instance does the SomeClass instance gets garbage collected? And what about the HTML element this.$div2? this.$div2 would not be inside the DOM because it is appended to this.$div1.
I ask this because the event handler in this.$div2 might keep a reference to the HTML element this.$div2 and also keeps a reference to the instance of SomeClass through the closure because of the variable "that".
So should I care about properly removing all events and HTML elements like this? Or simply removing the "root" element (this.$div1) solves the problem?
this.$div2 is appended to this.$div1. If I call this.$div1.remove(); and later lose the reference of my SomeClass instance does the SomeClass instance gets garbage collected?
Yes, when all references to it are lost - also those through event handlers , - the instance can get garbage-collected.
And what about the HTML element this.$div2? this.$div2 would not be inside the DOM because it is appended to this.$div1.
It does not matter whether it is currently attached to the DOM. If some non-collectible object references $div1, it also could access its child node $div2 and that one's event handlers, so the instance referenced from the handler would not be collectible.
I ask this because the event handler in this.$div2 might keep a reference to the HTML element this.$div2 and also keeps a reference to the instance of SomeClass through the closure because of the variable "that".
That's a circular reference and should get handled well by the engines (when none of the objects inside the circle is referenced from outside it can get collected). However, (old?) Internet Explorers fail to do this when a DOM object is involved in the circle.
For that reason the .remove jQuery method (code) internally calls the (internal) cleanData method which detaches all event listeners.
So should I care about properly removing all events and HTML elements like this? Or simply removing the "root" element (this.$div1) solves the problem?
Yes, calling remove on a jQuery wrapper automatically removes all events (from all child elements) and DOM nodes.
Should I care about properly removing all events and HTML elements
like this?
The short answer is No! at least in 99% of the cases, it will not matter in any way because the memory used by one DOM element is trivial compared to the overall memory used by a web page.
However it is always a good practice to release the memory used by disposing unneeded objects, but you cannot say that GC would definitely releases the memory utilized by the elements because garbage collection is entirely up to the browser! In theory GC should only kick in when there are no references to the DOM element, at least that's how Chrome works, but in languages like JavaScript, you don't explicitly tell the run-time you're done with the object, things get messy in JavaScript so quickly: a function might pass the object on to some more functions, the object might get saved away as a member within yet another object, an object might get referenced through the closure etc, so it's completely up to the browser how and what to collect!
In your case removing div1 frees the html document and the element would not render in the view, in fact jQuery's remove method takes care of removing all the events, expando properties, and child elements attached to the element together with the element itself, however you keep a reference of div1 and div2 in yet another object making both DOM elements Orphan elements! removing SomeClass instance variable releases all references to the DOM elements making them candidate for garbage collection but here comes the tricky that variable that causes the DOM element make a reference to the instance of SomeClass through clusure! This issue is known as Circular Reference in IE:
JavaScript Objects and DOM elements that store references to one
another cause Internet Explorer’s garbage collector to not reclaim
memory, resulting in memory leaks
You can read more about it here
This particular leak is mostly of historical interest IE<8, but a good example of breaking circular links is to avoid using the variable that, instead use proxy or delegate to change the event handler's context to some particular context.
ECMA 5th bind method is quit useful changing contexts when in comes to DOM event handlers, here's a simple handler based on your code without using variable closure:
this.$div2.click((function() {
this.someMethod2();
}).bind(this));
If you will create element dynamically, then assign to them events. i think that your code is not a good way to do that. you should follow this manner:
for fixed elements if you need an event, use these two functions; the first called in the constructor, the second in the destructor.
on_Events: function() {
$('your_form').on('event_name', {element_Selector}, callback_function)
},
off_Events: function() {
$('your_form').off('event_name', {element_Selector}, callback_function)
}
for dynamically objects. add events when creating an element and remove these events just before destroying the element.

How can I find a text node in the live DOM using its copy/clone?

I've copied some text nodes from the page (i.e., live DOM) and have stored them in an array. Is it possible to locate those exact nodes again in the live DOM? I would need to use plain JavaScript.
This is basically how my project works:
A configuration object specifies sections of the page (using CSS selectors) where there is some text content, and optionally subsections within that section (also using CSS selectors) where there is text content that should be ignored. The property names that I use, respectively, are select and exclude.
Using Sizzle (jQuery's selector engine), I generate an element collection for each of the select selectors, then clone it.
I then run the exclude selectors against the select collection, finding any elements that match, and remove them from the select collection.
Using the select collection with the removed exclude sections, I traverse it to build an array of only the text nodes.
I use this array of text nodes to do some word matching based on a supplied list of terms that may be in the original text content. To do this, I build an array of objects that include properties like the text node in which a term was found, and at what position/offset that term occurs within the text node's data.
Given the latter array, I need to be able to match the cloned text nodes in which matching terms were found to the original text nodes from which they were cloned. If I can do this, I can just iterate over my array of objects, first finding the text node that corresponds to the live DOM (from which it was originally cloned), and then linking the term at the position/offset that I have recorded in the object.
Hopefully this makes some sense - please let me know if not, and I can provide more details. This must be deterministic - i.e., I can't just search the live DOM for a text node that has the same data, as that may lead to false positives.
Again, I must use plain JavaScript here, not jQuery or any other libraries.
I appreciate any help!
In general, you can't. Once you've cloned, the new clones have a different identity to the old nodes. There is no general-purpose way to tie a node back to the node it was cloned from.
If you know the topmost cloned ancestor node, and the node in the document that it was cloned from, you can naturally use the index of each ancestor in its parent childNode list to walk down from the topmost ancestors to a particular node... but only assuming neither DOM has been mutated since cloning.
Otherwise, you'd be left with some horrid hack like walking the entire DOM of the clone immediately after cloning, writing an expando property to each node to refer to the source node.
I'm not sure why you are cloning nodes at all. In the process you describe, surely you could just store the live document nodes in your lists, without any cloning?

Determine a DOM element's document?

I am getting a reference to a DOM element from a WYSIWYG Editor.
I don't understand JS nor the WYSIWYG Editor (CKEditor) that deeply yet, but it somehow seems to be a pointer or other direct reference to the actual element that resides in an IFRAME within the WYSIWYG editor. At least, when I console.log the element, I get a link that, when clicked, opens the actual element in Firebug.
Is there a way to get a reference to this element's document object within the IFRAME?
If you have the DOM element reference, you can use the ownerDocument property:
var ownerDoc = someElement.ownerDocument;
I don't know that specific editor, but if it has a reasonably normal implementation of the DOM, each node (including the DOM element to which you get a reference) has a parentNode read-only property that references its parent node. By following the chain of parentNode references, you're moving upwards in the DOM tree and should eventually reach the document you want.
(The ownerDocument property offers a more immediate solution, but it was not supported in some old browsers such as IE 5.5 -- if you don't have to worry about such "archaeology" issues, it's fine, but parentNode works even more broadly).

Categories