How a variable binds its value to the DOM? - javascript

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).

Related

Where exactly the Virtual DOM is stored?

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");

Struggling to bind and match DOM elements with vanilla JS

I prefer to bind all my DOM elements at the top of my code so that the ID or class reference is only hard coded once, even though the element is used multiple times within the code. Something like this:
var startButton = "#startButton";
var closeButtons = ".button.close";
That works well with selectors, such as document.querySelector. The challenge occurs when I'm about to compare an element ID to one of the static variables above, like for instance during a click event:
e.target.id == startButton
Since the variable startButton contains a sharp, I would have to do something like this:
e.target.id == startButton.replace('#', '')
This is obviously not the ideal way of handling things, so I'm wondering if there's a better way to bind DOM elements for later use?
Please note that I did consider retrieving the elements using a selector right away, like document.querySelector, but that prevents me from manipulating the elements later on.
EDIT: My note above turns out to be wrong. It's totally possible to manipulate the elements later on. So the question is whether it's better to store selectors in variables, or to store nodes in variables. As pointed out below, the DOM element might not exist while it's being declared, so it might be better to store the selector in a variable, like I'm doing above.
For the specific example in your question, you can use .matches():
if (e.target.matches(startButton))
The .matches() DOM API takes a selector as its argument.

Changing inner text value of tab through javascript

I'm learning Javascript right now, and attempting to change the text title of a particular tab. It's actually part of a larger Shiny dashboard project, but I want to add some custom functionality to a few tabs. Below are the tabs in question:
Simple enough. I first access my tabs in my Javascript file:
var tabScrub2 = $(document).find('[data-value="scrubTab2"]');
console.log(tabScrub2);
When I use Firefox's developer console, I see that the tab is an object:
Moreover, it looks like I need to change the innerText property of 0, whatever this is, since that corresponds to the title of my tab (the innerText of 1 corresponds to the text inside scrubTab2). However, I'm not familiar with the actual object type being returned here:
Simply put, how the heck do I access and manipulate properties from this? And am I actually accessing an array? When I type in
var scrub2 = tabScrub2["1"];
console.log(scrub2);
I get an HTML element. I'm seen the a element in CSS and jQuery, but am not super familiar with how to manipulate its properties programmatically? How do I go about accessing and manipulating the innerText properties of this via Javascript? For instance, how would I hide scrubTab2, or change its title to something else?
The first object you're seeing is jQuery's wrapper around the real DOM elements. It's not an actual array, but it does contain all of the elements that matched your query under zero-indexed properties (e.g. "0" and "1") which allows you to access to them via an array-like API (e.g. tabScrub[1]).
Your method of grabbing a node using tabScrub2["1"] is correct (see this question in the jQuery FAQ). It's more likely to see that done with a numeric key though (i.e. tabScrub[1]) because that matches the way you would access an element in a normal array.
As far as manipulating properties of the DOM node, the DOM's API is notoriously inconsistent and quirky (hence the need for things like jQuery in the first place). However, for your use case you can just assign a string to the innerText property directly (e.g. tagScrub2[1].innerText = "Tab title"). MDN is a great resource if you're looking for reference material on other parts of the DOM.
A side note: if you're looking for a specific element you should use a query that will only match that element. It's generally a bad sign if you're grabbing extra elements and then accessing the element you want at a key other than 0. If you're doing this then your code depends on other (potentially unrelated) nodes in the DOM existing before your node, and if/when you change those nodes your original code will break.
Just use jQuery eq method to get the relevant object index from the array.
For an example
//Query and get first element.
var tabScrub2 = $(document).find('[data-value="scrubTab2"]:eq(0)');
//Hide
tabScrub2.hide();
//Change title
tabScrub2.attr("title", "New Title Text");
Lean more about jQuery eq here.
https://api.jquery.com/eq/
Since you use jquery selectors tabScrub2[0] returns the native DOM element instead of another jQuery object. Therefore the hide function won't work in that object since the native DOM element doesn't implement such type of functionality for an element. That's why you have to use jQuery pseudo selector as above. Because hide will only work with a jQuery object.

Calling Method faster then Retrieving Something From Memory

So this is my question: Why would calling a method be faster then retrieving something from memory?
Noticed when an id attribute is specified on a DOM element, user agents automatically attach the element's reference on their global scope.
Since user agents already reference all elements, which have their id attribute specified, why would I need to use document.getElementById("")?
Within an application, I would:
//Retrieving the value, I could possibly write this two way.
<script>
var fromGlobalScope = myElement.value;
var documentGetById = document.getElementById("myElement").value;
</script>
<input id="myElement" value="someValue" />
Doing some research, it is supported by all major browser, but their may some browser that do not support, which will break.
However, I could simply write:
<script>
//See if the element is on the global scope.
var fromGlobalScope = myElement ||document.getElementById("myElement");
</script>
I believe patterned correctly, I can automatically have references to all elements that have an id attribute. I don't have to call document.getElementById();
Using an resident property and I wouldn't have to walk the DOM, would think there would be a good performance benefit.
I created a jsPerf to see the benefit: enter link description here
My surprise was using document.getElementById() was a lot faster?
So this is my question: Why would calling a method be faster then retrieving something from memory?
Using document.getElementById, I would be calling a method that may or may not walk the DOM. At least, I will be calling an address for the value.
With a property on the global scope that should be quickly available as it is placed in some memory location.
I have include jsPerf results below:
I created another jsPerf with another thought:
explicitly setting a property on the window object
However, I still believe learning why can help with the mechanics that are at play, which may result in something helpful.
From HTML5 spec
5.2.4 Named access on the Window object
The Window interface supports named properties. The supported property
names at any moment consist of the following, in tree order, ignoring
later duplicates:
the browsing context name of any child browsing context of the active document whose name is not the empty string,
the value of the name content attribute for all a, applet, area, embed, form, frameset, img, and object elements in the active document that have a non-empty name content attribute, and
the value of the id content attribute of any HTML element in the active document with a non-empty id content attribute.
So the browser will probably walk the DOM tree to resolve a named property. In contrast getElementById() just needs to look the id up in (say) a hash map.
While the browser could maintain a hash map of the first matching element to that algorithm, maintaining that map would impose a performance penalty that would rarely pay for itself. In contrast the browser is looking up elements by their id constantly, so it pays to keep the id map.

How to find with javascript if element exists in DOM or it's virtual (has been just created by createElement)

I'm looking for a way to find if element referenced in javascript has been inserted in the document.
Lets illustrate a case with following code:
var elem = document.createElement('div');
// Element has not been inserted in the document, i.e. not present
document.getElementByTagName('body')[0].appendChild(elem);
// Element can now be found in the DOM tree
Jquery has :visible selector, but it won't give accurate result when I need to find that invisible element has been placed somewhere in the document.
Here's an easier method that uses the standard Node.contains DOM API to check in an element is currently in the DOM:
document.body.contains(MY_ElEMENT);
CROSS-BROWSER NOTE: the document object in IE does not have a contains() method - to ensure cross-browser compatibility, use document.body.contains() instead. (or document.head.contains if you're checking for elements like link, script, etc)
Notes on using a specific document reference vs Node-level ownerDocument:
Someone raised the idea of using MY_ELEMENT.ownerDocument.contains(MY_ELEMENT) to check for a node's presence in the document. While this can produce the intended result (albeit, with more verbosity than necessary in 99% of cases), it can also lead to unexpected results, depending on use-case. Let's talk about why:
If you are dealing with a node that currently resides in an separate document, like one generated with document.implementation.createHTMLDocument(), an <iframe> document, or an HTML Import document, and use the node's ownerDocument property to check for presence in what you think will be your main, visually rendered document, you will be in a world of hurt.
The node property ownerDocument is simply a pointer to whatever current document the node resides in. Almost every use-case of contains involves checking a specific document for a node's presence. You have 0 guarantee that ownerDocument is the same document you want to check - only you know that. The danger of ownerDocument is that someone may introduce any number of ways to reference, import, or generate nodes that reside in other documents. If they do so, and you have written your code to rely on ownerDocument's relative inference, your code may break. To ensure your code always produces expected results, you should only compare against the specifically referenced document you intend to check, not trust relative inferences like ownerDocument.
Do this:
var elem = document.createElement('div');
elem.setAttribute('id', 'my_new_div');
if (document.getElementById('my_new_div')) { } //element exists in the document.
The safest way is to test directly whether the element is contained in the document:
function isInDocument(el) {
var html = document.body.parentNode;
while (el) {
if (el === html) {
return true;
}
el = el.parentNode;
}
return false;
}
var elem = document.createElement('div');
alert(isInDocument(elem));
document.body.appendChild(elem);
alert(isInDocument(elem));
You can also use jQuery.contains:
jQuery.contains( document, YOUR_ELEMENT)
Use compareDocumentPosition to see if the element is contained inside document. PPK has browser compatibility details and John Resig has a version for IE.
function isInDocument(query){
return document.querySelectorAll(query).length != 0;
}
// isInDocument("#elemid")

Categories

Resources