Saving DOM Element into localStorage - javascript

I am trying to save a variable that contains HTML Element, into localStorage, this way I can call that variable again (from localStorage) anytime I want.
I am executing the javascript in the background & content script of chrome extension
So I create the variable and load it with the HTML Element that I want to save:
var btnelm = document.getElementById("ELM_ID");
I then attempt to save that variable into localStorage (hoping to be able to use that variable later)
localStorage["btnelm"] = btnelm;
But when I called the localStorage which I need it to contain the html element in my variable
localStorage["btnelm"]
I get this string text (and not the actual html element that was in my btnelm variable:
[object HTMLInputElement]
Which of course means, when I treat localStorage["btnelm"] as a variable containing my html element, it does not work:
localStorage["btnelm"].innerHTML returns undefined
I understand that localStorage stores only string values, so what are my options to be able to store my
btnelm and when called, be able to treat it as when I first created (i.e. accessing it with .childern for example)
Thanks in advance

You can't save the DOM element itself... But you can save its outerHTML.
<div id="ELM_ID">My Div</div>
var btnelm = document.getElementById("ELM_ID").outerHTML; // <== here
localStorage["btnelm"] = btnelm;
console.log(localStorage["btnelm"]);
The outputs is :
"<div id='ELM_ID'>My Div</div>"
EDIT
To use the outerHTML once retreived... And look for its properties as is was a real DOM element, simply use the .createElement() method to "recreate" it...
let tempElement = document.createElement("div")
tempElement.innerHTML = localStorage["btnelm"]
console.log(tempElement.children[0].innerText) // Will output "My Div"

Related

JS sessionStorage to store HTML node and then re-use it later

I need to clone some HTML content, store it in sessionStorage, to then re-deploy the stored HTML back into the DOM on another page. For the moment with my testing I'm just doing it all on one page and summoning the sessionStorage with a page refresh.
So, here is what I have come up with so far.
var exp561CardFour = document.getElementById('dashboard_item_four');
var clnCardFour = exp561CardFour.cloneNode(true);
sessionStorage.setItem('storedCardFour', clnCardFour);
When I go to grab the HTML with this bit of code in the console...
var grabCardFour = sessionStorage.getItem('storedCardFour');
...I end up with this:
Please help :)
edit:
FYI clnCardFour just contains some HTML and it works ok
edit with Parking Masters suggestion in the console:
The Storage API (sessionStorage/localStorage) only stores strings. When you call:
sessionStorage.setItem('storedCardFour', clnCardFour);
the API uses the .toString() method against the object clnCardFour - which returns [object HTMLLIElement].
So you need the string representation of that Node. You can achieve that by getting the OuterHTML of the Node like this:
sessionStorage.setItem('storedCardFour', clnCardFour.outerHTML);
When you need to restore that Node, simply use the parent Node's .innerHTML property to place it back into the DOM.
let div = document.querySelector('div');
let exp561CardFour = document.getElementById('dashboard_item_four');
sessionStorage.setItem('storedCardFour', exp561CardFour.outerHTML);
let clonedFromStorage = sessionStorage.getItem('storedCardFour');
div.innerHTML += clonedFromStorage
<div>
<i id="dashboard_item_four">Hello there!</i>
</div>
And here is a JS Bin Example
The HTMLIElement is an <i> element, you're storing the entire element object in the storage.
You can use JSON (JavaScript Object Notation) .stringify({}) to stringify the element and then parse it again to get the element.
Here's an example of that:
var myEl = document.getElementById("my-el");
alert("This will not print the object: " + myEl);
alert("This will print the object: " + JSON.stringify(myEl));
<i id="my-el"></i>
Overflowed objects are not stringified as the object is too long.
With jQuery:
var myEl = $("my-el");
alert("This will not print the jQuery object: " + myEl);
alert("This will print the jQuery object: " + JSON.stringify(myEl));
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<i id="my-el"></i>
More about JSON.stringify and JSON.parse at MDN.
(Be careful what you store in sessionStorage, you can only store up to 2kB at a time).
Now to get the parsed object using JSON.parse:
var myObj = "{}";
myObj; // "{}"
JSON.parse(myObj); // Object { ... }
I've put together a live example of everything:
var exp561CardFour = document.getElementById('dashboard_item_four');
var clnCardFour = exp561CardFour.cloneNode(true);
sessionStorage.setItem('storedCardFour', JSON.stringify(clnCardFour));
// When in an <iframe>, it'll say The operation is insecure."
var grabCardFour = JSON.parse(sessionStorage.getItem('storedCardFour'));
<i id="dashboard_item_four"><i>
The code snippet is from stacksnippets.net and will not work because of iframe security issues.
However, try it on your local server / file, and good luck.

How can I save changes to the context in a jQuery each loop?

I have the following code, which preprocesses some response data from an AJAX call before displaying it (the displaying part is not shown). In particular, it sets the src attribute of the image in each li element of the response.
$(response.items).filter('li').each(function(i){
$('img', this).attr('src', 'images/Picture.jpg');
if (i==0){
console.log(this);
console.log(response.items);
}
});
The output of console.log(this) shows that the src attribute gets set correctly in the context represented by this, but the output of console.log(response.items) shows that response.items is unchanged.
Is there a (preferably non-hacky) way to persist all changes to the li elements to response.items?
I think the problem here is that you're using the filter method. Filter (and also map) don't modify the original array, they essentially make a copy of it. So if you would check the return value of this whole code block like this:
var processed = $(response.items).filter('li').each(function(i){
$('img', this).attr('src', 'images/Picture.jpg');
if (i==0){
console.log(this);
}
});
console.log(processed);
It should properly show the changed values. Depending on what you want to do you could also use a map method after the each.

Prevent element from updating - JQuery

I select an element of the page:
$mainSection = $('#main');
then I add more Elements via AJAX into the <div id="main"></div> element. Next time I call $mainSection, the newly added elements are also in it. But I don't want that. I would like that the variable $mainSection only has the content in it from the initial rendering of the page. I can't find a way to prevent jQuery from updating.
I tried this:
$(document).ready(function(){
$mainSection = $('#main').clone(true);
Then I add new elements to #main and then I check if they get found via:
$foundElement = $($mainSection + ":not(:has(*)):not(script):contains('"+newlyAddedContent+"')");
On page load, they are not there. But after I add them, they get found.
I also tried:
$mainSection = $('#main').html();
$mainSection = $($mainSection);
didn't work also.
Here is a jsFiddle to illustrate my point:
http://jsfiddle.net/VEQ2E/2/
The problem is somewhere burried in this line:
$foundElement = $($mainSection + ":not(:has(*)):not(script):contains('"+newlyAddedContent+"')");
It somehow always searches through the whole document, when I do it like this.
You can use .clone(true):
$mainSection = $('#main').clone(true);
It every time takes the clone/copy of the initial state of this div.
Note:
.clone( [withDataAndEvents ] [, deepWithDataAndEvents ] )
withDataAndEvents : Boolean (default: false)
deepWithDataAndEvents : Boolean (default: value of withDataAndEvents)
A Boolean indicating whether event handlers and data for all children of the cloned element should be copied.
Your problem was not that your clone was getting changed, but rather the selector you were using to try finding something within the clone. Your code was like this:
$($mainSection + ":not(:has(*)):not(script):contains('"+newlyAddedContent+"')");
Concatenating an object with a string will turn the object into a string, simply "[object Object]", then your selector will just look at the ":not(:has..."
Instead, you should use filter:
$foundElement = $mainClone.filter(":not(:has(*)):not(script):contains('world')");
This will now only look within your $mainClone for items matching that filter.
JSFiddle
You can take several approaches:
Cache the contents of #main before the update. This gives you just the contents of the element without the element.:
mainsectionContents = $('#main').html();
Or Cache a copy of #main before the update. This will give you the content together with the element, and depending on whatever else you may want to copy feel free to check the api docs:
$mainsectionCopy = $('#main').clone();

Adding custom data attribute for a new node in Jquery does not work

I tried to use the method data (jQuery 1.7.1) in this code:
var q = '<div class="form-error-marker"></div>';
var t = $(q).data('message', message).insertAfter(el);
and it does not work.
Note that this works:
var t = $(q).attr('data-message', message).insertAfter(el);
Why does the first variant not work?
EDIT: insertAfter works correctly and new div is added after el (which is instance of one element which I get by getElementById() function; long story short I have a library that I extend).
When I say 'it does not work' I mean that the attribute 'data-message' is not stored.
Using data like that sets an arbitrary piece of data for this node; it doesn't add a new data- attribute. Just add the attribute with the attr function, and then access it with data
var q = $('<div class="form-error-marker"></div>').attr("data-message", message);
Now access it like this:
var message = q.data("message");
Here's a fiddle
When you use jQuery.data you don't change element attributes, instead your data saved in $.cache.
So if you want to change element attributes use jQuery.attr, when you want to save some info use jQuery.data

Trying to reduce repetition of javascript using a variable

I am trying to reduce the repetition in my code but not having any luck. I reduced the code down to its simplest functionality to try and get it to work.
The idea is to take the last two letters of an id name, as those letters are the same as a previously declared variable and use it to refer to the old variable.
I used the alert to test whether I was getting the right output and the alert window pops up saying "E1". So I am not really sure why it wont work when I try and use it.
E1 = new Audio('audio/E1.ogg');
$('#noteE1').click(function() {
var fileName = this.id.slice(4);
//alert(fileName); used to test output
fileName.play();
$('#note' + fileName).addClass('active');
});
The code block works when I use the original variable E1 instead of fileName. I want to use fileName because I am hoping to have this function work for multiple elements on click, instead of having it repeated for each element.
How can I make this work? What am I missing?
Thanks.
fileName is still a string. JavaScript does not know that you want to use the variable with the same name. You are calling the play() method on a string, which of course does not exist (hence you get an error).
Suggestion:
Store your objects in a table:
var files = {
E1: new Audio('audio/E1.ogg')
};
$('#noteE1').click(function() {
var fileName = this.id.slice(4);
//alert(fileName); used to test output
files[fileName].play();
$('#note' + fileName).addClass('active');
});
Another suggestion:
Instead of using the ID to hold information about the file, consider using HTML5 data attributes:
<div id="#note" data-filename="E1">Something</div>
Then you can get the name with:
var filename = $('#note').data('filename');
This makes your code more flexible. You are not dependent on giving the elements an ID in a specific format.

Categories