Element reference breaks on modification of innerHTML property of container - javascript

When creating elements via code, I have encountered an issue where modifying the innerHTML property of an element breaks any references to other elements that are injected into the modified element prior to the modification.
I have a test case here: http://jsfiddle.net/mJ7bF/1/ in which I would expect the link1 reference to behave exactly as link2 does.
This second test case is the same code, but instead of using the innerHTML property to add the <br> tag, I create the line break with an object. This test behaves as expected: http://jsfiddle.net/K4c9a/2/
My question is not regarding this specific code, but the concept behind it: what happens to the link1 reference in that first test case? If it doesn't refer to the HTML/DOM node that is visible when the cont node is injected into the document, what DOES it refer to, and how does this fit in with the ByReference nature of javascript objects?

few things here.
first of all. strings are immutable hence doing element.innerHTML += "<br>" acts as a complete read and rewrite.
second, why that is bad:
aside from performance, mootools (and jquery, for that matter) assigns special unique sequential uids to all referenced elements. you reference an element by calling a selector on it or creating it etc.
then consider that SPECIFIC element with uid say 5. the uid is linked to a special object called Storage that sits behind a closure (so its private). it has the uid as key.
element storage then works on a element.store("key", value") and element.retrieve("key")
and finally, why that matters: events are stored into element storage (eg, Storage[5]['events']) - do element.retrieve("events") and explore that in fireBug if you're curious.
when you rewrite the innerHTML the old element stops existing. it is then recreated but the event handler AND the reference to the function that you bound earlier will no longer work as it will now get a NEW uid.
that's about it, hope it makes sense.
to add a br just do new Element("br").inject(element) instead or create a templated fragment for the lot (fastest) and add in 1 big chunk, adding events after.

HTML is represented internally by a DOM object structure. Kind of like a Tree class in traditional programming languages. If you set innerHTML, the previous nodes in the parent node are destroyed, the new innerHTML is parsed, and new objects are created. The references are no longer the same.
div
|-- a..
The div object above contains an Anchor object as a child. Now set a variable link1 as a reference to the address of this Anchor object. Then the .innerHTML is += "<br />", which means all of the nodes of div are removed, and recreated dynamically based on the parsed result of the new value of .innerHTML. Now the old reference is no longer valid because the Anchor tag was re-created as a new object instance.

Related

Why can't I use element.remove() on an element created with createElement() if I do something in between in the DOM? [duplicate]

When creating elements via code, I have encountered an issue where modifying the innerHTML property of an element breaks any references to other elements that are injected into the modified element prior to the modification.
I have a test case here: http://jsfiddle.net/mJ7bF/1/ in which I would expect the link1 reference to behave exactly as link2 does.
This second test case is the same code, but instead of using the innerHTML property to add the <br> tag, I create the line break with an object. This test behaves as expected: http://jsfiddle.net/K4c9a/2/
My question is not regarding this specific code, but the concept behind it: what happens to the link1 reference in that first test case? If it doesn't refer to the HTML/DOM node that is visible when the cont node is injected into the document, what DOES it refer to, and how does this fit in with the ByReference nature of javascript objects?
few things here.
first of all. strings are immutable hence doing element.innerHTML += "<br>" acts as a complete read and rewrite.
second, why that is bad:
aside from performance, mootools (and jquery, for that matter) assigns special unique sequential uids to all referenced elements. you reference an element by calling a selector on it or creating it etc.
then consider that SPECIFIC element with uid say 5. the uid is linked to a special object called Storage that sits behind a closure (so its private). it has the uid as key.
element storage then works on a element.store("key", value") and element.retrieve("key")
and finally, why that matters: events are stored into element storage (eg, Storage[5]['events']) - do element.retrieve("events") and explore that in fireBug if you're curious.
when you rewrite the innerHTML the old element stops existing. it is then recreated but the event handler AND the reference to the function that you bound earlier will no longer work as it will now get a NEW uid.
that's about it, hope it makes sense.
to add a br just do new Element("br").inject(element) instead or create a templated fragment for the lot (fastest) and add in 1 big chunk, adding events after.
HTML is represented internally by a DOM object structure. Kind of like a Tree class in traditional programming languages. If you set innerHTML, the previous nodes in the parent node are destroyed, the new innerHTML is parsed, and new objects are created. The references are no longer the same.
div
|-- a..
The div object above contains an Anchor object as a child. Now set a variable link1 as a reference to the address of this Anchor object. Then the .innerHTML is += "<br />", which means all of the nodes of div are removed, and recreated dynamically based on the parsed result of the new value of .innerHTML. Now the old reference is no longer valid because the Anchor tag was re-created as a new object instance.

The reasoning behind direct link between DOM Element and the variable it is assigned?

I am kinda newbie and I really want to understand why the variables we create don't just get the values inside the DOM element, but becomes the DOM element?
Here is some basic code;
var elementValues = document.getElementById("p1");
elementValues.style.color = "red";
// I don't understand why we don't need this step in order to manipulate the DOM.
// document.getElementById.getElementById("p1") = elementValues;
Aren't we basically saying to copy the values from DOM element with an id of p1 and paste them into elementValues?
But why the color of DOM element changes when I change the color of elementValues? From what I understand it acts like a pointer.
In Javascript, Object variables store a reference to the objects. Hence, document.getElementById returns a reference. So when you modify the values of elementsValues, your are editing the referenced object.
Have a look on Working with Objects - Comparing Object. You could read the whole page also to have an overview.
Yes, it is like a pointer.
By using var elementValues = document.getElementById("p1"); you are assigning a reference to the DOM element to the variable. Nothing about the element gets saved to the variable, but "where to find it".

How does storing JQuery Fields in an array compare to just storing the selection string?

I am having to store a large array of JavaScriptobjects, these objects need to contain a message, and a way of selecting an associated HTML element.
Currently, I've been using the the following code to add to this array:
if (field[0].id != ""){
this.selectorString = "#" + field[0].id;
}
else if (field.attr("name") != ""){
this.selectorString = "[name='" + field.attr("name") + "']";
}
I didn't want to store the entire field, as I wasn't confident on JavaScripts/JQuery memory management or how it worked. Selection strings seemed the safer option, as opposed to a large array of fields. I could then just use the stored string to perform a JQuery selection statement and manipulate the field.
The Big Question
If I store the fields, will this take up a large amount of memory, or is it purely reference to an object that is already stored somewhere in the big black hole of JavaScript of which I know little?
Is there an alternative that anyone can think of that would enable me to achieve what I'm going for. The 'fields' can be divs/spans/input fields/anything, that might not necessarily have an ID/Name - which will cause problems if I'm not storing the field.
Many thanks.
If I store the fields, will this take up a large amount of memory, or is it purely reference to an object that is already stored somewhere in the big black hole of JavaScript of which I know little?
Just a reference (the "big black hole" is called the "DOM" or "DOM tree" or sometimes "page"). But note that if you remove the element you're referring to from the DOM at some point, but still have a reference to it from a JavaScript variable, the element is kept in memory (just not in the DOM tree) until/unless you assign a new value to that variable or that variable goes out of scope.
Concrete example: Say we have this on our page:
<div id="foo">....</div>
And we have this code:
var f = $("#foo");
Now we have a jQuery object wrapped around a reference to that element. It's not a copy of the element, just a reference to it.
Now suppose we did this:
f.remove();
That div is no longer in the DOM (on the page), but it still exists because we still have a reference to it from the f variable (indirectly; f refers to a jQuery object which, in turn, refers to the element). But if that variable goes out of scope, or we assign some other value to it (for instance, f = null;), then the element is not referenced from anywhere and can be reclaimed.
If you're not removing elements from the DOM (directly, or indirectly by replacing the contents of an an ancestor of theirs), then very little memory is used to simply refer to the existing element.

What is the difference between getElementById and simply using an element's ID as a variable?

Can someone tell me the difference between calling an HTML elment with id="myDomObect"?:
var myObj = document.getElementById('myDomObect');
&
var myObj = myDomObect;
Use the first form or a wrapper such as jQuery. The second form,
var myObj = myDomObect;
Translates to
var myObj = window["myDomObect"];
This "works" because of an old, old hack in which ID's were exposed as global window properties (IIRC this was a misfeature from the start) and thus we are still blessed with the behavior 20 years later.. and yes, it will work in the very latest Chrome.
However, such a shorthand should not be used for multiple reasons:
It will not work as originally written in "strict mode" (but it will work with the second form)
It does not convey the operation - namely that a DOM element is requested/fetched (by ID).
It does not work for IDs that collide with window properties; eg. <div id=history></div> would result in "unexpected behavior" if accessed this way. (This doesn't affect getElementById code that correctly uses local var variables in a function.)
Behavior is not defined when duplicate IDs exist in the document (which is allowed); the behavior for getElementById has been codified in by DOM 4: "getElementById(elementId) method must return the first element [with the ID], in tree order.."
See also:
Do DOM tree elements with ids become global variables?
The first is how the "real" DOM API works (another option is document.querySelector("#myDomObject")). The second is how browsers have started implementing automatic hoisting of id'd elements, since ids are supposed to be unique. In a twist of "what were you thinking", this can lead to hilarious conflicts where variables that have the same name as HTML elements with an id don't take precedence and the variable you though you were using is suddenly an HTML element.

Assigned property appears as undefined

I am doing ajax. At a certain point, I assign someproperty to a DOM object selected by id (suppose it is 12345), and I confirm that the value has been assigned by using alert():
window.document.getElementById('12345').someproperty = true;
alert(window.document.getElementById('12345').someproperty);
At this point, the alert correctly shows true. Then, at some point later, I invoke a javascript command that looks up the value of someproperty for the object:
alert(window.document.getElementById('12345').someproperty);
and this time, it shows undefined. Why is the value not defined?
I have a feeling that you're doing some nasty .innerHTML somewhere between your to lookups of .someproperty.
Assigning to .innerHTML destroys the current DOM, and replaces it with a new DOM that is obtained by parsing the HTML string you provided. So I'm guessing you're destroying an entire section, and replacing it with a nearly identical new section. This naturally wipes out the stateful information in the original DOM.
You should modify the individual DOM elements that need updating instead of wiping them out entirely using .innerHTML.

Categories