Reputation: 149
I have this program that can make collapsibles with content dynamically. I'm trying to save the collapsibles created in local storage. I was able to successfully save a users input in local storage, but ran into a road block when I tried to save elements with the same classname in local storage. I tried something like this:
var collapsibles = document.getElementsByClassName('collapsible');
function populateStorage() {
localStorage.setItem('collapsibles', document.getElementsByClassName('collapsible'));
setStyles();
}
function setStyles() {
var collapsibles = localStorage.getItem('collapsibles');
document.getElementById("myCollapsibles").innerHTML = collapsibles;
}
collapsibles.onchange = populateStorage;
But it didn't work, Is it possible to save dynamically created elements in local storage?
Code Snippet of Program (excluding local storage snippet):
var currentClosable;
var currentContent;
function selectedColl() {
document.getElementById("inputTaskDiv").style.display = "block";
currentClosable = event.target;
currentContent = currentClosable.nextElementSibling;
var inputTaskDiv = document.getElementById("inputTaskDiv");
$(currentContent).append(inputTaskDiv);
}
var taskCounter = 0;
function addTask() {
var text = document.getElementById("taskInput").value;
// create a new div element and give it a unique id
var newTask = $("<div class='currentTask'><input class='checkbox' type='checkbox'><label>" + text + "</label></div>");
newTask.id = 'temp' + taskCounter;
taskCounter++
// and give it some content
var newContent = document.createTextNode(text);
$(currentContent).append(newTask);
console.log("appended");
$(".currentTask").hover(
function() {
var taskCur = event.target;
$( this ).find( "a" ).last().remove();
$(taskCur).append( $( "<a class='taskX'> x</a>" ) );
function dump() {
$(taskCur).remove();
}
$( "a" ).on( "click", dump );
}, function() {
$( this ).find( "a" ).last().remove();
});
document.getElementById("taskInput").value = " ";
}
var elementCounter = 0;
var elementCounterContent = 0;
var text;
function addElement() {
text = document.getElementById("input").value;
// create a new div element and give it a unique id
var newDiv = $("<button class='collapsible' onclick='selectedColl()'></button>").text(text);
$(newDiv).append("<button class='btnDelete'>Delete</button>");
var newContentOfDiv = $("<div class='content'></div>");
newDiv.id = 'temp' + elementCounter;
newContentOfDiv.id = 'content' + elementCounterContent;
newDiv.classList = "div";
elementCounter++
elementCounterContent++
// and give it some content
var newContent = document.createTextNode(text);
// add the newly created element and its content into the DOM
document.getElementById("input").value = " ";
$("#divColl").append(newDiv, newContentOfDiv);
newDiv.click(function () {
this.classList.toggle("active");
content = this.nextElementSibling;
if (content.style.display === 'block') {
content.style.display = 'none';
} else {
content.style.display = 'block';
}
});
}
$("#divColl").on('click', '.btnDelete', function () {
$(this).closest('.collapsible').remove();
content.style.display = 'none';
});
.collapsible {
background-color: #777;
color: white;
cursor: pointer;
padding: 18px;
width: 100%;
border: none;
text-align: left;
outline: none;
font-size: 15px;
}
.active,
.collapsible:hover {
background-color: #555;
}
.collapsible:after {
content: '\002B';
color: white;
font-weight: bold;
float: right;
margin-left: 5px;
}
.active:after {
content: "\2212";
}
.content {
padding: 0 18px;
transition: max-height 0.2s ease-out;
background-color: #f1f1f1;
}
.taskX{
color:red;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>
<body>
<!---Add Step --->
<div id="addSteps">
<p>Make a collapsible:</p>
<input id="input" type="text" placeholder="title for collapsible"><button onclick="addElement()">Make</button>
</div>
<!-- Add tasks to steps --->
<div id="addTasksToSteps" style="display:none">
<div id="inputTaskDiv" style="display:none">
<input id="taskInput" type="text"><button onclick="addTask()">Add Task</button>
</div>
</div>
<!-- Final --->
<div id="scheduleDiv" style="display:none">
</div>
<div id="divColl"></div>
<header></header>
</body>
</html>
Upvotes: 1
Views: 2230
Reputation: 2022
Just for a better end result, try to separate presentation logic from the data logic. So instead of storing the whole HTML into the local storage, why don't you create a data structure that holds the tasks and you make changes to this data structure and store it into the local storage? And when you load the page you get the data from the storage and just render it to your page. That separation will make your application easy to maintain and change.
I can suggest this data structure that I would use if I were you:
[
{
title: "Task group title",
tasks: [{
status: "done",
title: "Task title"
}]
}
]
This will give you full control over your items and it will be easier to extend later. Because storing the presentation layer will cost you a lot later when you change something in your design. of course in this case it would be much easier for you to use some framework like Vuejs or React...
But for your current problem:
document.getElementsByClassName
will return an array of HTML objects, and it cannot be stored inside the local storage as a string directly you have to store the HTML string instead. local storage can store strings only
For more information about localStorage please visit: https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage
So you need to iterate over them and get their HTML. Or you can use their container and just get its content by ID and store its innerHTML and retrieve it when needed
if you use the following code in a loop with i it would be:
localStorage.setItem('collapsibles', document.getElementsByClassName('collapsible')[i].outerHTML);
so basically you can use:
localStorage.setItem('collapsibles', document.getElementById('divColl').innerHTML);
Then you set the innerHTML of the divColl when needed. like
document.getElementById('divColl').innerHTML = localStorage.getItem('collapsibles')
In this way you are storing the whole HTML into your local storage. not just an object which will be stored like: "[object HTMLDivElement]"
Upvotes: 1
Reputation: 178422
localStorage expects strings
You can add a span
var newDiv = $("<button class='collapsible'><span class='text'>"+text+"</span></button>");
using delegation too
$("#divColl").on("click","collapsible", selectedColl)
and
const arr = $("#divColl").find("button .text")
.map(function() { return this.textContent })
.get();
localStorage.setItem("arr",JSON.stringify(arr)); // stringify is mandatory for non-strings
Why mix DOM access into this when you have jQuery? Or why use jQuery if you want to use DOM access
var currentClosable;
var currentContent;
function selectedColl() {
document.getElementById("inputTaskDiv").style.display = "block";
currentClosable = event.target;
currentContent = currentClosable.nextElementSibling;
var inputTaskDiv = document.getElementById("inputTaskDiv");
$(currentContent).append(inputTaskDiv);
}
var taskCounter = 0;
function addTask() {
var text = document.getElementById("taskInput").value;
// create a new div element and give it a unique id
var newTask = $("<div class='currentTask'><input class='checkbox' type='checkbox'><label>" + text + "</label></div>");
newTask.id = 'temp' + taskCounter;
taskCounter++
// and give it some content
var newContent = document.createTextNode(text);
$(currentContent).append(newTask);
console.log("appended");
$(".currentTask").hover(
function() {
var taskCur = event.target;
$(this).find("a").last().remove();
$(taskCur).append($("<a class='taskX'> x</a>"));
function dump() {
$(taskCur).remove();
}
$("a").on("click", dump);
},
function() {
$(this).find("a").last().remove();
});
document.getElementById("taskInput").value = " ";
}
var elementCounter = 0;
var elementCounterContent = 0;
var text;
function addElement() {
text = document.getElementById("input").value;
// create a new div element and give it a unique id
var newDiv = $("<button class='collapsible'><span class='text'>"+text+"</span></button>");
$(newDiv).append("<button class='btnDelete'>Delete</button>");
var newContentOfDiv = $("<div class='content'></div>");
newDiv.id = 'temp' + elementCounter;
newContentOfDiv.id = 'content' + elementCounterContent;
newDiv.classList = "div";
elementCounter++
elementCounterContent++
// and give it some content
var newContent = document.createTextNode(text);
// add the newly created element and its content into the DOM
document.getElementById("input").value = " ";
$("#divColl").append(newDiv, newContentOfDiv);
// here store the content in localStorage
console.log($("#divColl").find("button .text").map(function() { return this.textContent }).get())
newDiv.click(function() {
this.classList.toggle("active");
content = this.nextElementSibling;
if (content.style.display === 'block') {
content.style.display = 'none';
} else {
content.style.display = 'block';
}
});
}
$("#divColl").on('click', '.btnDelete', function() {
$(this).closest('.collapsible').remove();
content.style.display = 'none';
});
$("#divColl").on("click","collapsible", selectedColl)
.collapsible {
background-color: #777;
color: white;
cursor: pointer;
padding: 18px;
width: 100%;
border: none;
text-align: left;
outline: none;
font-size: 15px;
}
.active,
.collapsible:hover {
background-color: #555;
}
.collapsible:after {
content: '\002B';
color: white;
font-weight: bold;
float: right;
margin-left: 5px;
}
.active:after {
content: "\2212";
}
.content {
padding: 0 18px;
transition: max-height 0.2s ease-out;
background-color: #f1f1f1;
}
.taskX {
color: red;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Project HOB</title>
<link href="style.css" rel="stylesheet" type="text/css" />
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<link rel="stylesheet" href="/resources/demos/style.css">
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>
<body>
<!---Add Step --->
<div id="addSteps">
<p>Make a collapsible:</p>
<input id="input" type="text" placeholder="title for collapsible"><button onclick="addElement()">Make</button>
</div>
<!-- Add tasks to steps --->
<div id="addTasksToSteps" style="display:none">
<div id="inputTaskDiv" style="display:none">
<input id="taskInput" type="text"><button onclick="addTask()">Add Task</button>
</div>
</div>
<!-- Final Schedule --->
<div id="scheduleDiv" style="display:none">
</div>
<div id="divColl">
<h1 id="assignmentTitle"></h1>
</div>
<div class="container"></div>
<header></header>
<script src="script.js"></script>
</body>
</html>
Upvotes: 1