Storage and retrieval of DOM element - javascript

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

Related

Why does the value attribute from an input[text] is different from what the browser renders?

I'm getting this strange behaviour in a very specific set of inputs on one my applications. I create some inputs and I can see them as I created them on the Elements panel (google chrome), but the way the browser renders it is different.
Note how the input is renders with comma instead of a point, but the value attribute uses a point
When I get a referente to that element using the selector API, I get this:
A direct reference to the Dom Element will return 11,00. The tag has 11.00 and jQuery returns the 11,00. I've removed all js that interacts with this element (masks, events, etc) and the issue still happens.
I've been swearing at the DOM for a day and a half, but I know this is most probably an issue with my application. What bothers me the most is that the browser does not honor what I see in the elements panel.
This is the small piece of code that creates the element, stopped right before the tag is created. Note the variables values in the right panel:
Could someone give me a hint about what could be causing this difference in between element, view and attributes? If possible, I'd like to know what/how this is happening in depth.
Thank you in advance

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

Differences in speed using document.getElementById(id), $(id), and containerElement.find(id)

Say I have a container element/div on a page. There are other elements outside of it, comprising page layout/navingation, and there is a fair amount of content inside of it.
In my javascript, I need to find/manipulate a few elements inside of that container. If all of these elements have id properties set, speed wise, is it better to use document.getElementById() (considering that once found, the element will need to be turned into a jQuery object for manipulation) to find them, or to select them using jQuery like so: $("#id"), or to select the container first, store it in a variable, and then select the elements to be manipulated using jQuery like so: container.find("#id")?
Which approach is faster? What does the speed of searches like these depend on?
As jQuery() with an id selector uses document.getElementById inside, using the native method directly is obviously faster. Of course the overhead is neglibible (you never need to select hundreds of elements by id), and they do different things - most likely you want to get a jQuery wrapper object containing the DOM node.
container.find(idselector) is an interesting case, though. As ids are unique all over the document, you should not need such an expression at all. Because of that, id selections are usually boosted by a O(1) lookup table inside the browser, and very fast even for full documents. I'm not sure what happens when you use it inside a container, but it might fall back to a rather slow DOM tree traversal.

Faster to manipulate existing HTML or removing the HTML and then recreating it?

This project I am working on, I have a list of elements and convert them into a "graph" (not a true graph, but you can call it a psuedo one). I have my data set, i run a for loop over the data and then create a li element for each one, create and parent ul, append the lis and then append the parent to another DOM element (we'll call it grandparent) already on the page. After that, i have to make subsequent updates to that list based on user interaction.
Also, this is all in the context of using jQuery.
Now, my question is - is it faster to create the elements once then update the resulting HTML on each subsequent call or is it faster to just recreate each element, empty() the grandparent element (which would get rid of the parent ul) and then reappend the newly created ul (which I am doing now)?
Keep in mind that when I am recreating the lis, they are not in the DOM at all so there is no repaint/reflow while recreating them. The repaint only happens when I reappend the newly created ul.
I was speaking to a coworker and he said it would be better to just update the HTML elements once they are created rather than recreating them every single time. i was thinking of going this route, but then I thought that updating the existing lis would actually cause a repaint on say 50 elements versus just doing one massive one with the empty() then reappending the newly created ul.
thoughts?
As rsp says, you'll need to profile your solutions, as which will be fastest depends both on the structure of your markup and on the particular browser its running on. It's also, of course, only worth going through all this effort if you're unhappy with the speed you're currently getting!
There are three basic approaches, however, each of which could be the fastest. The first only causes a single repaint; the others only two, if you choose your parent node well.
Construct your new content as HTML strings ([...].join("")), then apply with .html(). Pros: typically faster than method #2. Cons: not necessarily by much, can leave stale jQuery data/events (i.e. can leak, if you aren't careful).
Construct your new content with jQuery outside the document (e.g. put all your <li>s into a <ul>, then insert the <ul> into the document after construction is complete), then insert normall. Pros: tends to be easier to read than method #1. Cons: performs worse the more complex your markup gets.
Remove the existing nodes from the document (ideally, a single parent node, like the <ul>), make your changes, then reinsert them. Pros: may be clearer and more performant than method #2, especially on complex markup. Cons: becomes much more complex if the number of elements changes between renderings.
The last one only causes two repaints even though you're making a large number of changes, because only the initial removal and final insert affect the document. Changing elements outside the document doesn't cause repaints.
I would suggest profiling your code first. Premature optimization is the root of all evil.

using hidden elements in pages to store data

I need to store data in html documents that are associated with elements, and that I can get to with javascript. I think I want to avoid arbitrary attributes on elements, since after reading various posts here I don't trust them. I can't use id or class, since I those are used for other things and I don't want to mess with them (my project has to work on wide varieties of html so I can't make any assumptions as to the design of the class and id structure). Another thing I need is for whatever I do, it needs to survive round-tripping through innerHTML (of a parent or ancestor element of the element I need to tag with data), with data intact.
I have considered various hidden elements (including script tags and html comments), which I insert into the document right before the element I need to tag with data. Currently my favorite is hidden form elements (i.e. input with type "hidden"). I can stick any data I want into the "value" of the element, and I can find all such elements easily enough with getElementsByTagName(). Also importantly, it doesn't seem to affect the layout of the page.
I'm curious about the ramifications of this, and if anyone can think of any any problems with it. I need to be able to put them anywhere in body of the page, before any element. For instance I might need them associated with an option element (a child of a select element), so that rules out using a hidden div, since putting one of those inside a select element is illegal. I don't think they will affect form behavior, since they don't have a name attribute (I can avoid using id's if that helps).
Any thoughts on this, or suggestions for other ways of accomplishing the same that meets my needs?
If this is going to be data for JavaScript's purposes, why even try to put it in the HTML at all? Here's a simple example of what I mean
<script type="text/javascript">
var nodeData = {
foo: {/* Data to associate with div#foo */}
, bar: {/* Data to associate with div#bar */}
};
</script>
<div id="foo">Foo!</div>
<div id="bar">Bar!</div>
It's simple and elegant since the principle of IDs being unique per document nicely matches with javascript's dictionaries requiring unique keys per entry.

Categories