XMLSerializer skips element values - javascript

I've got some Javascript code which serializes the entire DOM tree as follows:
(new XMLSerializer()).serializeToString(document)
But what I get is somewhat different than I'd expect. Specifically, I get the HTML source, but not the values of the various input/selects. If objects are inserted into the DOM, I get those objects, but again, no values.
However, if I were to walk through the DOM in Javascript for some other purpose, I would have access to every input's value.
What gives? Obviously, I'm missing some very basic concept, but I've no idea what.
Can I get the full DOM as an HTML/XML formatted string, with input/select values? If so, how?

This is due to the fact that a DOM form input maintains its value property (which reflects the current state) separately from its value attribute (which represents the initial value of the input and is used by XMLSerializer to serialize the DOM).
I've written about this several times on Stack Overflow. Here's a recent example: jQuery .attr('value', 'new_value') not working?

Related

Adding JS to HTML page, Browser differences

I have simple page that needs very small JS functionality. When i use <script> tag, i get different behaviour on different browsers, depending on where the <script> tag is placed.
On Chrome(65) and Firefox(59) the position of the <script> tag dose not matter and the code is executed before the page is rendered. lets say that i have <h1> and before it I have <script> that uses that <h1> DOM node. It works. Even if i move the <script> to the <head> of the page, it still works.
now if i try the same thing in Edge or IE11 i see what i think is the logical thing, and that is that JavaScript can not see the element if the <script> is before the <h1>.
My question is why we have such different behaviour, isn't this part of a specification? And witch of the bot is the proper way the code should work?
.getElementsByTagName() (along with .getElementsByName() and .getElementsByClassName()) return "live" node lists. These are collections that will always reflect the current state of the DOM, so even if an element gets added or removed after you've declared your collection variable, you will get the most up to date collection. It works because every time you access your variable that references the "live" collection, the DOM is re-scanned. This is great to give you up-to-date results, but not great for performance. So, if your document doesn't dynamically change very much, you are better off not using these methods and instead use .querySelectorAll(), which returns a static node list (one who's content is established at the time of the method call and not updated even if DOM changes occur later) or simply .getElementById() if you are looking for a single element that has an id or .querySelector() if you are looking for only the first element that matches a CSS selector.
Now, here's the important part, no matter what method you use to get any kind of node list (live or static), you will always get a list object back, even if that list is empty.
What you are experiencing across browsers is just the difference in how they report the object itself to you. However, if you were to access some specific aspect of the object or any members of the object, all browsers would give you the same results and if you are querying prior to the element(s) being parsed, you will have a node list with 0 items in it. This is why it's a best-practice to place your scripts that need access to DOM elements just before the closing body tag (</body>), because by that time, all the HTML has been parsed and you will definitely be able to find all elements.
As Felix Kling found, i was using document.getElementsByTagName() with returns live list. Combined that with the fact that I was verifying the result with console.log() that in Chrome and Firefox gets the value of the list when I clicked the expand button, therefore I see my element. In IE and Edge, what is printed in the console is directly displayed with the current value of the live list, that had no elements at that time.
10x a lot for the quick response to everybody :)

Where are input and dom values stored?

This is a very basic question but I am not sure how to research it. Let's say I have an html file with an input field and a javascript file that contains a function to grab the values entered in the the input:
HTML:
<input type='text' id='value' onclick='getValue()'>
JS File:
var val;
function getValue(){
val = document.getElementById('value').value;
console.log(val)
}
When a user inputs a value, that value is stored in the DOM. I then grab the value from the DOM and store it in my script file loaded in the browser.
Since this is stored within my script file now. If I were to reload the page, all the values stored would be reset to their original files.
Is it accurate to say the value taken from the DOM is stored in my loaded javascript file? Or is that stored someplace else?
Th input value gets stored in the DOM tree (if you want to know where exactly: see the code, e.g.: of Chrome and Firefox). Th JavaScript code you posted makes a copy of that value. The copy is independent of the value stored in the DOM tree, you can delete the input-element and will still have the copy in JavaScript. So the answer is threefold:
the value is in the DOM-tree first
a copy of the input value is in the JavaScript Stack when you copy it and in the DOM-tree; at least I know of no one-step way to move it.
if you delete the DOM element the copy you made in JavaScript stays in JavaScript
That makes it possible for example to run JavaScript and DOM-parsing with two distinct programs. Chrome does it with their V8-machine which you can put into some thin wrap and run it separately. You may call the result "node" if you want.
Saying that it's stored "in [your] script file" is a little misleading, but technically accurate.
Any data for DOM elements is stored within the browser's memory for that tab, which it manages itself. Since your Javascript is just a part of that memory, it is in some way stored there. The actual fiddly bits of how that all works is hidden within the browser, and is probably different between browsers.

Most efficient way of "pushing" data to Polymer elements

I have a situation in which I get data over a web socket, and performance is important. From the docs I understand that there are various ways of "pushing" the data I'm receiving to my Polymer elements, but I'm curious which one will be most efficient. So far I've created an element that can be included in a template, where the parent element will observe any changes in the data property and react accordingly. I've also been experimenting with using a Behavior to accomplish the same thing, though instead of needing to include a "data-element" in its template, it could just observe its own data property. I realize I could also use something like iron-signals to "push" the data via an event.
I'm not sure any of these methods are very efficient, since most of the time the changes to the "data" object will only apply to a small subset of all the observers. Another possible solution would be to "observe" a dynamic path, so like data.pathx instead of data.*, which would drastically reduce the number of times the observer callback gets fired, but I haven't come across anything that leads me to think that's possible, since each of my elements won't know if it should observe pathx or pathz until creation.
Like I said, performance is vital, and I feel there is way too much inefficiency if I have a small to medium sized dom-repeat of elements each observing a large data object of another element or individually holding a copy of that data on their own (like I assume a behavior would accomplish?).
I've looked at iron-meta, but I haven't been able to successfully data-bind to it, and from what I can tell from the docs, this data needs to be queried, whereas I need to be notified of changes.
Polymer doesn't really "observe" changes in elements. It just sets a setter for each property, and when it's called the UI is updated. So a dom-repeat template will not observe any change inside an object bound to it.
What could impact performance is unnecessary DOM manipulation, so if just a small subset of the data changes, re assigning all the array to the property is not ideal, and you should use notifyPath with just the sub property path and value that changed. Polymer will only update the DOM nodes affected.
If you have a way of knowing what sub properties changed in your data then you could obtain the object paths that have changed and call notifyPath for each of those and only a small number of DOM nodes will be changed.
Additional note:
If the number of elements in your array change, (added/removed) you should use the Polymer base array manipulation methods to update the property of your Polymer element, so it will change the DOM efficiently.

Storage and retrieval of DOM element

Wondering if anyone out there has ran into this before....
I'd like to use JavaScript to identify a DOM element on a page, then store it's reference in a database or cookie for later retrieval.
To get specific, what I'm looking to do is create a UI so that when the user CLICKs an element on a page, JavaScript fires the click event, passing the instance of the DOM element clicked on.
easy so far, right?
So what I want to do is store the "identity" of this DOM element, say in a database, so when I later return to this page, I can pull out all stored DOM element identities and get access to them in the page once more.
So this is quite simple if this DOM element has a unique ID. Just store the ID, then when the page comes back up, we just do a getElementByID and we've got our DOM element again.
The problem is that not everything in the DOM has a unique identifier, so there the problem lies.
I had some bad ideas initially, like iterating through the entire DOM and incrementing them with unique class names (dom-01, dom-02, etc) and this would give me an identifier. But this would cause a lot of initial overhead and if the page ever changed, the order of the DOM elements wouldn't be the same, so we wouldn't get back the correct DOM elemet.
I'mve never tried it, but another thought was to serialize the DOM element, stick it in the DB, and then on reload parse to an object, and use that object to find my original DOM element. I've never done that before, so how I can actually compare the restored (parsed) object to the one in the DOM is a big unknown.
Specifics on the serialization solution or any other original ideas for accomplishing this are welcome!!
Thanks in advance everyone!
Here's a jsFiddle solution attempt: http://jsfiddle.net/techbubble/pJgyu/7720/
The approach I took was to compute a simple hash of the HTML content of the target element, or if no such content is present, a hash of the aggregated attributes and values of the element. I have a getElementHash() function that returns a string in the format: TAG:[H | A]:Hash (the H or A indicates if the HTML content or attributes were used to calculate the hash). This produces a unique key for any element on the page that either has HTML content or has at least one attribute (miniscule risk of duplication possible).
For retrieving an element with a previously saved key, I created a getElementByHash() function. It uses the tag that is extracted from the key in a jQuery selector. For each element returned, the HTML content or attributes hash is computed (based on the value "H" or "A" specified in the key) to see if it matches the hash in the key. If there is a match, the search ends and the element is returned.
This approach is impervious to the element being moved around on the page as long as its HTML content (or attributes) remain unchanged. It does not produce a key for elements that have neither any HTML content nor any attributes (which makes them pretty useless anyway).
If you want to keep the location of the node in the DOM, why not just keep an XPath of it? XPath allows you to keep an exact location of a node in a document, as long as that location doesn't change. For instance, you can say something like
//div[#id="xpath_is"]/span[class="cool"]/a[4]
Meaning the 4th <a> tag within a <span class="cool"> within a <div id="xpath_is">
http://www.w3.org/TR/xpath/
http://www.w3schools.com/xpath/default.asp
You can get the XPath of an attribute by Inspecting it with Firebug and right clicking on the node and selecting "Copy XPath". I'm not sure how easy it is to get it from a DOM Node (there are ways, but not sure how many baked implementations there are lying around). It'd be relatively easy to simply look for an ancestor (by traversing upward with .parentNode and building one) and adding .classNames and .ids as you go - but I'm too lazy to write this right now, ;).

Is there a good way to attach JavaScript objects to HTML elements?

I want to associate a JavaScript object with an HTML element. Is there a simple way to do this?
I noticed HTML DOM defines a setAttribute method and it looks like this is defined for arbitrary attribute name. However this can only set string values. (You can of course use this to store keys into a dictionary.)
Specifics (though I'm mostly interested in the general question):
Specifically, I have HTML elements representing nodes in a tree and I'm trying to enable drag-and-drop, but the jQuery drop event will only give me the elements being dragged and dropped.
The normal pattern for getting information to event handlers seems to be to create the HTML elements at the same time as you are creating JavaScript objects and then to define event handlers by closing over these JavaScript objects - however this doesn't work too well in this case (I could have a global object that gets populated when a drag begins... but this feels slightly nasty).
JavaScript objects can have arbitrary properties assigned to them, there's nothing special you have to do to allow it. This includes DOM elements; although this behaviour is not part of the DOM standard, it has been the case going back to the very first versions of JavaScript and is totally reliable.
var div= document.getElementById('nav');
div.potato= ['lemons', 3];
If you're already using jQuery, you can use its data() method for this. This allows you to assign complex objects to the element if you want or you can leverage that method to hold a reference to an object (or some other data) at the very least.
It's worth noting that, under the hood, jQuery implements data() in precisely the way that bobince described in his answer, so you always use that directly, whether or not you're using jQuery.

Categories