Where exactly the Virtual DOM is stored? - javascript

I'm torturing myself for hours now and can't find an answer.
Where exactly, under what object/key, the React data are located? I found an object ReactRoot that seems to store all the information about components, but I have no idea where it sits in the window (I guess?) object.
It has to be somewhere under the window object, right?
If you take a memory snapshot and select the ReactRoot constructor from the list, chrome will create a reference to it under $0 ($0 in chrome).
EDIT
Is it possible that ReactRoot is declared in a way that makes it inaccessible for other objects? How is this possible in js? React isn't getting any special treatment from the browsers, is he?

There is a document explaining the DOM Level 1 fundamental methods.
See also the DOM Level 1 Core specification from the W3C.
When you create an element, a new instance of the element were created but not rendered. Not until you include them into the DOM tree.
Browser will only render elements within the document body. But everything else is just an instance of an object, the virtual DOM.
// create a new Text node for the second paragraph
var newText = document.createTextNode("This is the second paragraph.");
// create a new Element to be the second paragraph
var newElement = document.createElement("P");

Related

How does this backbone view get it's EL property set?

I'm looking at this Backbone app:
https://github.com/ccoenraets/nodecellar/blob/master/public/js/main.js
and trying to understand how it works. I see that in the main.js file he calls a WineView like this:
wineList.fetch({success: function(){
$("#content").html(new WineListView({model: wineList, page: p}).el);
I have a few questions about this:
1) Why call $("#content").... from this point? Isn't one of the points of creating a view object to let that new objects "Render" method handle the HTML injection? In fact his Wine View Object DOES have a render method (here: /public/js/views/winelist.js) so what's this call here good for?
2) Why add the EL property at the end? I thought EL was simply a single tag that the View was "attached" to. If it's just a single tag how does it then generate all the new HTML he's looking for?
3) How does the EL tag even get set in the new view object in th first place? I thought if you didn't explicitly state it then EL defaulted to an empty DIV and I can see nowhere EL defined for this View in his code.
Hope someone can clear this up!
How does the EL tag even get set here in the first place?
The Backbone code itself creates el when you don't specify it. As you noted, it defaults to an empty div:
this.el is created from the view's tagName, className, id and attributes properties, if specified. If not, el is an empty div.
Note that, if the el gets created in this way, then it will not be attached to the DOM. Hence, the code above has to take the el property (the view's root tag), and attach it to the DOM under "#content".
Isn't one of the points of calling creating a view object to let that new objects "Render" method handle the HTML injection
Maybe strictly speaking, but not necessarily. Backbone.js is agnostic about how you structure applications, and does not impose strict requirements on its models/views. You'll see lots of different approaches like this in Backbone apps.

createElement in parent vs. iframe

I have a modular Web page, each module being an iframe that can interact with the parent page. In particular the iframes add new elements to the parent (navigation, tabs, whatever). All pages are in the same domain.
So far I have always used this pattern in the iframes:
var newDiv=document.createElement("div");
// do stuff with newDiv
parent.document.body.appendChild(newDiv);
It recently occurred to me that, because the newDiv is going to be attached to the parent, it would make more sense to do this:
var newDiv=parent.document.createElement("div");
// do stuff with newDiv
parent.document.body.appendChild(newDiv);
My question: does it make any difference whether the new element is created with document.createElement vs. parent.document.createElement?
Does this work ? If it does then I think (not 100% sure) both are same because createElement is a DOM method and a member of document object and you have access to two documents (two objects) at the same time and both contains same member functions (createElement is available in both), in this case one is in the iframe and another is in it's parent. So, it looks like that you are calling a method from one source instead of another source, IMO.
Like I said this is only an opinion so wait for more answers from the experts.

Why should y.innerHTML = x.innerHTML; be avoided?

Let's say that we have a DIV x on the page and we want to duplicate ("copy-paste") the contents of that DIV into another DIV y. We could do this like so:
y.innerHTML = x.innerHTML;
or with jQuery:
$(y).html( $(x).html() );
However, it appears that this method is not a good idea, and that it should be avoided.
(1) Why should this method be avoided?
(2) How should this be done instead?
Update:
For the sake of this question let's assume that there are no elements with ID's inside the DIV x.
(Sorry I forgot to cover this case in my original question.)
Conclusion:
I have posted my own answer to this question below (as I originally intended). Now, I also planed to accept my own answer :P, but lonesomeday's answer is so amazing that I have to accept it instead.
This method of "copying" HTML elements from one place to another is the result of a misapprehension of what a browser does. Browsers don't keep an HTML document in memory somewhere and repeatedly modify the HTML based on commands from JavaScript.
When a browser first loads a page, it parses the HTML document and turns it into a DOM structure. This is a relationship of objects following a W3C standard (well, mostly...). The original HTML is from then on completely redundant. The browser doesn't care what the original HTML structure was; its understanding of the web page is the DOM structure that was created from it. If your HTML markup was incorrect/invalid, it will be corrected in some way by the web browser; the DOM structure will not contain the invalid code in any way.
Basically, HTML should be treated as a way of serialising a DOM structure to be passed over the internet or stored in a file locally.
It should not, therefore, be used for modifying an existing web page. The DOM (Document Object Model) has a system for changing the content of a page. This is based on the relationship of nodes, not on the HTML serialisation. So when you add an li to a ul, you have these two options (assuming ul is the list element):
// option 1: innerHTML
ul.innerHTML += '<li>foobar</li>';
// option 2: DOM manipulation
var li = document.createElement('li');
li.appendChild(document.createTextNode('foobar'));
ul.appendChild(li);
Now, the first option looks a lot simpler, but this is only because the browser has abstracted a lot away for you: internally, the browser has to convert the element's children to a string, then append some content, then convert the string back to a DOM structure. The second option corresponds to the browser's native understanding of what's going on.
The second major consideration is to think about the limitations of HTML. When you think about a webpage, not everything relevant to the element can be serialised to HTML. For instance, event handlers bound with x.onclick = function(); or x.addEventListener(...) won't be replicated in innerHTML, so they won't be copied across. So the new elements in y won't have the event listeners. This probably isn't what you want.
So the way around this is to work with the native DOM methods:
for (var i = 0; i < x.childNodes.length; i++) {
y.appendChild(x.childNodes[i].cloneNode(true));
}
Reading the MDN documentation will probably help to understand this way of doing things:
appendChild
cloneNode
childNodes
Now the problem with this (as with option 2 in the code example above) is that it is very verbose, far longer than the innerHTML option would be. This is when you appreciate having a JavaScript library that does this kind of thing for you. For example, in jQuery:
$('#y').html($('#x').clone(true, true).contents());
This is a lot more explicit about what you want to happen. As well as having various performance benefits and preserving event handlers, for example, it also helps you to understand what your code is doing. This is good for your soul as a JavaScript programmer and makes bizarre errors significantly less likely!
You can duplicate IDs which need to be unique.
jQuery's clone method call like, $(element).clone(true); will clone data and event listeners, but ID's will still also be cloned. So to avoid duplicate IDs, don't use IDs for items that need to be cloned.
It should be avoided because then you lose any handlers that may have been on that
DOM element.
You can try to get around that by appending clones of the DOM elements instead of completely overwriting them.
First, let's define the task that has to be accomplished here:
All child nodes of DIV x have to be "copied" (together with all its descendants = deep copy) and "pasted" into the DIV y. If any of the descendants of x has one or more event handlers bound to it, we would presumably want those handlers to continue working on the copies (once they have been placed inside y).
Now, this is not a trivial task. Luckily, the jQuery library (and all the other popular libraries as well I assume) offers a convenient method to accomplish this task: .clone(). Using this method, the solution could be written like so:
$( x ).contents().clone( true ).appendTo( y );
The above solution is the answer to question (2). Now, let's tackle question (1):
This
y.innerHTML = x.innerHTML;
is not just a bad idea - it's an awful one. Let me explain...
The above statement can be broken down into two steps.
The expression x.innerHTML is evaluated,
That return value of that expression (which is a string) is assigned to y.innerHTML.
The nodes that we want to copy (the child nodes of x) are DOM nodes. They are objects that exist in the browser's memory. When evaluating x.innerHTML, the browser serializes (stringifies) those DOM nodes into a string (HTML source code string).
Now, if we needed such a string (to store it in a database, for instance), then this serialization would be understandable. However, we do not need such a string (at least not as an end-product).
In step 2, we are assigning this string to y.innerHTML. The browser evaluates this by parsing the string which results in a set of DOM nodes which are then inserted into DIV y (as child nodes).
So, to sum up:
Child nodes of x --> stringifying --> HTML source code string --> parsing --> Nodes (copies)
So, what's the problem with this approach? Well, DOM nodes may contain properties and functionality which cannot and therefore won't be serialized. The most important such functionality are event handlers that are bound to descendants of x - the copies of those elements won't have any event handlers bound to them. The handlers got lost in the process.
An interesting analogy can be made here:
Digital signal --> D/A conversion --> Analog signal --> A/D conversion --> Digital signal
As you probably know, the resulting digital signal is not an exact copy of the original digital signal - some information got lost in the process.
I hope you understand now why y.innerHTML = x.innerHTML should be avoided.
I wouldn't do it simply because you're asking the browser to re-parse HTML markup that has already been parsed.
I'd be more inclined to use the native cloneNode(true) to duplicate the existing DOM elements.
var node, i=0;
while( node = x.childNodes[ i++ ] ) {
y.appendChild( node.cloneNode( true ) );
}
Well it really depends. There is a possibility of creating duplicate elements with the same ID, which is never a good thing.
jQuery also has methods that can do this for you.

How can I select nodes from an object and then relocate those nodes to my document in Internet Explorer 8+?

What I want to accomplish:
create an object by performing an XSL transformation (on an XML DOM object), let's call it 'fragment'
select some nodes by running an XPATH query on the resulting object
collect those nodes in, let's say, and array
append 'fragment' to a certain container in my HTML document
alter any one of those nodes, not by querying the DOM, but by referencing the said node like so: nodeCollection[x]
My motivation:
This is essentially a way of "caching" the objects that I know for sure I will have to alter later. So instead of walking the DOM to find them each time I need them, I want to "cache" them. It's much faster to select them with XPATH queries and once I do that I will no longer need to do any DOM traversing, which in my case can be rather slow (we're talking a lot of nodes here). I know this may generally come off as a very convoluted solution, but in my particular situation it pays off big time, at least in Firefox (see below).
How I did it in Firefox:
create a document fragment ('xml' and 'xsl' are XML DOM objects):
xsltProcessor=new XSLTProcessor();
xsltProcessor.importStylesheet(xsl);
fragment = xsltProcessor.transformToFragment(xml,document);
XPATH query:
var nodes = document.evaluate("//div", fragment.firstChild, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
collect nodes in an array:
nodeCollection = new Array();
i = 0;
node = nodes.iterateNext();
while(node) {
nodeCollection[i] = node;
node = nodes.iterateNext();
i++;
}
append the fragment to a container in the HTML document:
document.getElementById("container").appendChild(fragment);
alter a node in the collection:
nodeCollection[0].style.border = '1px solid red';
.. and it works as intended.
The problems I ran into in Internet Explorer:
I used
var fragmentObject = new ActiveXObject("Msxml2.DOMDocument.3.0");
xml.transformNodeToObject(xsl,fragmentObject);
to create an object on which I can perform XPATH queries like so:
var nodes = fragmentObject.selectNodes("//div");
, but after this step I don't know how to append 'fragmentObject' to my container in the HTML document and then select individual nodes from 'nodes' and manipulate them like I did on Firefox.
What I think went wrong:
'fragmentObject' is a "Msxml2.DOMDocument.3.0", which is entirely different than a document fragment, so I can't just move nodes from it to my HTML document. If I try something like
container.appendChild(nodes[1]);
I get this error: "SCRIPT5022: DOM Exception: HIERARCHY_REQUEST_ERR (3)", which is what usually happens when trying to insert a node where it doesn't belong (or at least that's the explanation I found for this particular error).
Maybe there's some type of object that I`m unaware of that supports XPATH queries and can be appended to the HTML document (?)
Instead of Msxml2.DOMDocument.3.0 try Microsoft.XMLDOM
if you are adding the node, then as you said it must be in DOM range. like document.appendChild() will give the error.
So we always do document.body.append..
Similarly, check if this container can directly add the Element..

How a variable binds its value to the DOM?

This might be crazy but it intriguing me for quite some time :)
I would like to know how a javascript variable can bind itself do the DOM after it is appended to the body, for example?
var p = document.createElement('p');
p.innerHTML = 'Hello World';
document.body.appendChild(p);
So now I have this p variable which contains an exact reference of that specific paragraph no matter where it is located inside the body.
p.innerHTML = 'new content';
will easily find the paragraph and change its value
So my question is...how this binding is made?
what If I want to re-create that after the variable is gone?
is there any way to attach that again without having to run through the DOM and find it?
I was thinking if somehow each node inside the DOM have its specific identifier that is not the id attribute but some kind of UUID that can be referred later on?
like:
console.log(p.localName); //aoi12e2kj2322444r4t
p = null;
so I can still recover that paragraph node thought this uuid?
In this environment I wouldn't have access to any external node attribute, such name, id, data, etc..
So I am quite curious to know how this binding is created between variable and DOM node?
I believe that it changes depending on the browser your using. There's no standard way to do so. Currently you either use the id or iterate over the dom until you reach the element you want.
The binding is created on the first line, where you assign the result of document.createElement to p. This is no different from any other time you assign something to a variable, which always binds the variable name to the value. As far as the script is concerned, there is no other binding occurring. The p is an HTMLElement, and that's all of the element that's exposed.
Note that for p.innerHTML = 'new content';, the element doesn't have to be found because p already refers to the element. That's what the DOM does: it exposes documents and document elements.
If you later want another reference to the same element, you'll have to use DOM methods (such as getElementById) to find it. That's what they're there for.
As for how the DOM exposes elements, that's implemented internally and varies from browser to browser or library to library (since the DOM isn't used just in browsers).

Categories

Resources