Editing Local Storage code to add unique ID to each element - javascript

I'm working on a piece of Local Storage functionality based on this example:
https://scriptscodes.com/codes/CrocoDillon/pIlKB/preview/index.html
CSS:
/* https://scriptscodes.com/codes/CrocoDillon/pIlKB/preview/index.html */
.list li{
cursor:pointer;
}
.list li:before,.list li.fav:before {
font-family: FontAwesome;
content:"\f08a";
color:white;
margin-right:10px;
font-size:14px;
}
.list li:hover:before {
font-family: FontAwesome;
content:"\f004";
color:white;
margin-right:10px;
font-size:14px;
}
.list li.fav:hover:before,.list li.fav:before {
font-family: FontAwesome;
content:"\f004";
color:red;
margin-right:10px;
font-size:14px;
}
Sample HTML:
<div class="list" style="background:#ccc; padding:20px;">
<ul class="list-unstyled">
<li id="petty">petty</li>
<li id="bedfordshire">bedfordshire</li>
<li id="rearing">rearing</li>
<li id="jane">jane</li>
<li id="furore">furore</li>
</ul>
</div>
Then I can add each item to local storage with this:
JS:
var favorites = JSON.parse(localStorage.getItem('favorites')) || [];
document.querySelector('.list').addEventListener('click', function(e) {
var id = e.target.id,
item = e.target,
index = favorites.indexOf(id);
if (!id) return;
if (index == -1) {
favorites.push(id);
item.className = 'fav';
} else {
favorites.splice(index, 1);
item.className = '';
}
localStorage.setItem('favorites', JSON.stringify(favorites));
});
A test page using this code is here: https://jimpix.co.uk/testing/test3.asp
So the "favorites" array looks like this:
favorites:"["petty","rearing","jane"]"
I need to work out how to edit the JS so that each element is given a unique ID, so the array looks like this:
favorites:"[{"id":"1","name":"petty"},{"id":"2","name":"rearing"},{"id":"3","name":"jane"}]"
Is it possible to do that?
I'd prefer not to use jQuery as I'd have to rewrite the JS used in this code, and also on another page where I output the local storage data.
Thanks

I can't see the problem just push favorites.push({ id: 1, name: "petty" } into your array and stringify it afterwards? For a unique Id take the index of the for loop or try something like this: Math.floor(window.performance.now() * 1000)
For finding if an element is in the array just do the following:
function isIdInArray(id) { // param id is the id youre looking for in the array
var isInArray = false;
favorites.forEach(function(favorite){
if(favorite.id === id){
isInArray = true;
}
});
return isInArray;
}
To clarify if you make index = favorites.indexOf(id); this wil search a string for a string.
Eg. "apple".indexOf("ple"); will return 2 because it found "ple" starting in the string at position 2. If the string does not contain the searched string Eg: "apple.indexOf("tomato"); the return value will be -1. You can't execute indexOf on an array. You could search your stringified array for the index value with indexOf() but i would not reccomend you to do that.

Related

Dynamically grow array item in JavaScript

Suppose I have 3 items and I would like to add them one by one in array.
Firstly, I pass abc and it adds abc in array, second time, passed cde, then array removes the first item and add second item cde.
But, I would like to add them like array={abc,cde,...}
It seems that I need to store previous value. First time, when I add abc it looks like array ={abc}. Second time, when I add cde, array should store the previous abc value before adding the new one cde which looks like array ={abc,cde,..}.
Please see this screenshot:
There is no array of the items where I can loop through and add those in another array.
This line always var array= Arr.push(Arr[0]); add one item.
var Arr ="";
var TD;
function AddData(sVal) {
var AddRow = true;
sVal = Replace(sVal, "~", "%");
Arr = sVal.split("^");
TD.innerHTML = "<INPUT TYPE='HIDDEN' value='" + Arr[0] + "'>";
TD.innerHTML = Arr[0];
var array= Arr.push(Arr[0]);
}
}
New approach
I separated the previous approach from the new one...
Having a source list that feeds the destination list when its items get clicked:
Here I show how you can have a source list with items feeding a destination list:
When you click on a source list item, it gets appended to the destination as is (cloned)
At the same time, during the adding operation, the item text content gets appended to a buffer array
There's also a function that dumps on console an array that lists all the items coming from a given list element (at page load)
Clicking on an item on destination, it gets removed from the list and from the buffer array
Items cannot be duplicated on destination so that if you click the first time on a list item in source, it gets appended on destination. The second time it won't have any effect. But after that said item gets removed from destination, it can be added again
const source = document.getElementById('source');
const destination = document.getElementById('destination');
//print on console the list of items in #source as an array of string values
console.log('source list items:', getListItems(source));
//adds the click event listener to the #source list
source
.addEventListener('click',(event)=>{
//clicked item
const target = event.target;
//if the clicked item is actually a <li>
if(target.tagName == 'LI' && !target.classList.contains('alreadyadded')){
//calls addItemToList on destination with the current target list item
addItemToList(destination, target);
target.classList.add('alreadyadded');
}
});
//adds the click event listener to the #destination list
destination
.addEventListener('click',(event)=>{
//clicked item
const target = event.target;
//if the clicked item is actually a <li>
if(target.tagName == 'LI'){
//removes the class alreadyadded from the corresponding item in the source
const sourceIndex = target.dataset.sourceIndex;
source.querySelectorAll('li')[sourceIndex].classList.remove('alreadyadded');
//calls addItemToList on destination with the current target list item
removeItemFromList(target);
}
});
//the array that gets populated by the function addItemToList and cleared by removeItemFromList
const destinationList = [];
//adds a list item to the target list element
function addItemToList(target, item){
//gets the index of the item compared to its parent list
const index = [...item.parentNode.children].indexOf(item);
//clones deep the item passed
const newItem = item.cloneNode(true);
//sets its data attribute (sourceIndex) with the index of the item in the source list
newItem.dataset.sourceIndex = index;
//appends it to target element
target.append(newItem);
//updates the buffer array pushing the content of the new item
destinationList.push(newItem.textContent);
//logs on console the buffer array
console.log('buffer array:', destinationList);
}
//removes a list item from its parent
function removeItemFromList(item){
//gets the index of the item compared to its parent list
const index = [...item.parentNode.children].indexOf(item);
//removes the element from dom
item.remove();
//removes the index element from the destinationList array
destinationList.splice(index, 1);
//logs on console the buffer array
console.log('buffer array:', destinationList);
}
//returns the array of item contents from the target list element
function getListItems(target){
const listItems = target.querySelectorAll('li');
return [...listItems].map(li => li.innerText);
}
ul{
list-style: none;
padding: 0;
width: fit-content;
border-top: solid 1px gray;
}
li{
border: solid 1px gray;
border-top: none;
padding: 0 1em;
}
#source li{
cursor: pointer;
}
#destination li::before{
content: 'x';
color: red;
cursor: pointer;
border: solid 1px blue;
padding: 0 2px 2px 2px;
margin-right: 7px;
display: inline-block;
line-height: 1ch;
border-radius: 3px;
margin-bottom: 4px;
}
<label>Source:</label>
<ul id="source">
<li>test1</li>
<li>test2</li>
<li>test3</li>
<li>test4</li>
</ul>
<label>Destination:</label>
<ul id="destination">
</ul>
Previous approach
Here follows the previous approach...
Pushing an array of values into a target array:
At first it seemed like you needed to push a list of values inside an array.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push
The push() method adds one or more elements to the end of an array and
returns the new length of the array.
push(element0)
push(element0, element1)
push(element0, element1, /* … ,*/ elementN)
const target = [1, 2, 3];
const elementsToAdd = [4, 5, 6];
target.push(...elementsToAdd);
console.log(target); //[ 1, 2, 3, 4, 5, 6 ]
Adding a new row to a target table using values from a serialized string:
Trying to further understand your code, I tried to rewrite your addData function to limit its action doing what its name suggests (adding data).
Well actually such name could suggest several different actions anyway to shed some light on what exactly did you expect I stripped down its core so that now it just limits itself to append a new row to the target table using the values coming from a serialized string for its cells.
Those cells will contain an <input type=hidden> with the value of the corresponding index from the serialized string. To better show off the otherwise invisible data, I decided also to set the cell innerText with that same value.
Of course that's not the whole story yet
//appends 10 rows to the target table
const table = document.getElementById('Table1');
for(let i=0;i<10;i++){
addData(table, 'col~1^col~2^col~3^col~4');
}
//appends a row in target table, with the content coming from raw
function addData(target, raw) {
//replaces the ~ with % on raw,
const processed = raw.replaceAll('~', '%');
console.log(processed);
//and splits its values delimited by ^
const values = processed.split("^");
//creates and append a new row in target table
const newRow = target.insertRow(-1);
//for each value in values
for(value of values){
//creates a new <input type=hidden>
const input = document.createElement('input');
input.setAttribute('type', 'hidden');
//sets its value as the current value of the array
input.value = value;
//creates a new row cell appending the input to its children
newCell = newRow.insertCell(-1);
newCell.append(input);
//since the only cell content is an hidden input I show it on its content also
newCell.textContent = value;
}
}
body{
padding: 1rem;
}
table{
border-collapse: collapse;
width: 100%;
}
table tr{
height: 2rem;
}
table td{
border: solid 1px;
}
<table id="Table1">
<tr id="TR01">
<td>header #1</td>
<td>header #2</td>
<td>header #3</td>
<td>header #4</td>
</tr>
</table>

How to remove <li> list items and make the remaining ones change their position and id?

Tricky question and far too advanced for my level (js student).
Lets say I use the append method to generate <li> items inside an <ol>, and I need each one of those li items to have a unique id, so I thought to get the amount of <li> (length) items I generated by saying this : var index = document.getElementById("ol1").getElementsByTagName("li").length+1; and use this number to create unique id's for every item I generate by doing this: li.id="li"+index; so the first one I generate becomes #li1 (since the amount of li items is one), the next one #li2 and so on. *btw, is it the right approach to do this?
Now lets say I want to remove #li1, then #li2 would replace it in position 1 of the list, but its id will still be #li2 since it has gotten it already.
For example what I ultimately want is when I remove #li1, then #li2 becomes #li1, #li3 becomes #li2, #li4 becomes #li3.....and so on.
What would be the right logic approach to do such a gimmick?
function append() {
var index = document.getElementById("ol1").getElementsByTagName("li").length + 1;
var ol = document.getElementById("ol1");
var li = document.createElement("li");
li.id = "li" + index;
li.innerHTML = (`LIST ITEM <input value=this is id: #li${index}><button class=remove id= button${index} onclick=remove${index}()>REMOVE</button>`);
ol.append(li)
}
function remove1() {
var rem = document.getElementById("li1");
rem.remove();
}
function displayIndex() {
var index = document.getElementById("ol1").getElementsByTagName("li").length;
alert(index);
}
#li1 {
color: red;
}
#li2 {
color: green;
}
#li3 {
color: blue;
}
ol button {
color: red;
visibility: hidden;
}
#button1 {
color: red;
visibility: visible;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<html>
<head>
<button id="btn1" onclick="append()">Append</button>
<button id="btn2" onclick="displayIndex()">Index</button>
</head>
<body>
<ol id="ol1">
</ol>
</body>
</html>
First off:
There are no "right" approaches. Only working implementations. How it is achieved might differ from developer to developer, and as long as it works, it should be considered the "right" way (however, not necessarily the most efficient or optimized or any other way).
One way would be to use a remove() function that re-assigns the ID's of the remaining <li>s (after the removal, obviously) as a side-effect.
function removeItemOf(list, listItem) {
if (!list.contains(listItem)) return;
listItem.remove();
indexItemsOf(list);
// For displaying the removed ID on-screen
document.querySelector('div').append(
document.createElement('br'),
document.createTextNode(`Removed <li> with id '${listItem.id}'`)
);
}
function indexItemsOf(list) {
for (var i = 0; list.children[i]; ++i) {
list.children[i].id = (list.id || list.tagName) + '-li' + i;
// For displaying the <li>'s ID on-screen
list.children[i].textContent = `With ID '${list.children[i].id}'`;
}
}
var list = document.querySelector('ol');
indexItemsOf(list);
setTimeout(() => {
removeItemOf(list, list.children[0]);
}, 2000);
setTimeout(() => {
removeItemOf(list, list.children[1]);
}, 4000);
<ol>
<li></li>
<li></li>
<li></li>
<li></li>
</ol>
<div></div>
However, re-assigning a previously used ID makes it not unique to one element, as it now identifies another element it hasn't identified before. For environments where this would be important (e.g. relying on an element to reference certain other elements, or to have a certain event-listener, etc.), re-using an ID would break the environment.
To circumvent this problem, one could keep track of how many unique items a list had over its entire lifetime, and create the next item with an ID using that amount, and then increase the amount by one.
Here is an example:
var list = document.querySelector('ol');
list.uniqueItems = 0;
// Here: Using Event-Delegation for removing a <li>
list.addEventListener('click', function(evt) {
if (evt.target.classList.contains('btn-delete'))
evt.target.closest('li').remove();
});
document.querySelector('button').addEventListener('click', function() {
var item = document.createElement('li');
item.id = 'li-' + list.uniqueItems;
item.textContent = `Has the ID '${item.id}' `;
var btnDelete = document.createElement('button');
btnDelete.classList.add('btn-delete');
btnDelete.textContent = 'Delete Item';
item.append(btnDelete);
list.append(item);
list.uniqueItems++;
});
<button>New Item</button>
<ol></ol>
You can just loop through each list item after your remove one and regenerate the IDs for each.
ol = document.getElementById("ol1");
function indexLIs(){
i = 1;
ol.querySelectorAll("li").forEach(function(li){
id = "li" + i;
li.setAttribute("id",id);
li.querySelector("input").value = "this is id: #" + id;
i++;
});
}
function append() {
var index = document.getElementById("ol1").getElementsByTagName("li").length + 1;
var li = document.createElement("li");
li.id = "li" + index;
li.innerHTML = (`LIST ITEM <input value=this is id: #li${index}><button class=remove id= button${index} onclick=remove${index}()>REMOVE</button>`);
ol.append(li)
}
function remove1() {
var rem = document.getElementById("li1");
rem.remove();
indexLIs();
}
function displayIndex() {
var index = document.getElementById("ol1").getElementsByTagName("li").length;
alert(index);
}
#ol1 li:nth-child(1) {
color: red;
}
#ol1 li:nth-child(2) {
color: green;
}
#ol1 li:nth-child(3) {
color: blue;
}
ol button {
color: red;
visibility: hidden;
}
#button1 {
color: red;
visibility: visible;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<html>
<head>
<button id="btn1" onclick="append()">Append</button>
<button id="btn2" onclick="displayIndex()">Index</button>
</head>
<body>
<ol id="ol1">
</ol>
</body>
</html>
Your approach is fine. It would be easier for you to keep counting from the last element without changing the other elements even if you remove an element. In that situation use a global variable and initialize it with the length of your initial list. Something like that should do the work:
var index = 1; // or the index you wish to start from
function append(){
var ol = document.getElementById("ol1");
var li = document.createElement("li");
li.id="li"+index;
li.innerHTML = (`LIST ITEM <input value=this is id: #li${index}><button class=remove id= button${index} onclick=remove${index}()>REMOVE</button>`);
ol.append(li);
// now we will increment our index for the next iteration
index++;
}
Hope this was useful.

Convert unordered list into array for saving to localstorage

I have an unordered list which I can append li elements onto. I want to know how to convert the list into something that can be stored and remade through localstorage? I believe this entails the conversion of the list into an array and then appending it back to the ul...
Here is the code I'm working with (currently I'm just having an alert() because I'm at a loss on how to append the stored data).
function A() {
var list = document.getElementById("list").innerHTML;
localStorage.setItem("list", JSON.stringify(list));
}
function B() {
var saved = JSON.parse(localStorage.getItem("list"));
alert(saved);
}
function addLi() {
var ul = document.getElementById("list");
var li = document.createElement("li");
li.appendChild(document.createTextNode("Addition to list."));
ul.appendChild(li);
}
<button onclick="A()">A()</button>
<button onclick="B()">B()</button>
<button onclick="addLi()">addLi()</button>
<div id="listCover">
<ul id="list">
<li>one</li>
<li>two</li>
<li>three</li>
</ul>
</div>
What you're doing, using .innerHTML, should work just fine. Since that's a string, no need for JSON.stringify and JSON.parse.
Alternately, you could loop through the li elements and add their contents to an array, then store the array (with JSON.stringify and JSON.parse, since web storage only stores strings).
That latter option looks something like this:
var items = document.querySelectorAll("#list li");
var array = Array.prototype.map.call(items, function(item) {
return item.textContent;
});
localStorage.setItem("list", JSON.stringify(array));
and
var saved = JSON.parse(localStorage.getItem("list")) || [];
saved.forEach(addLi);
...if we modify addLi to accept the text to add:
function addLi(text) {
var ul = document.getElementById("list");
var li = document.createElement("li");
li.appendChild(document.createTextNode(text));
ul.appendChild(li);
}

Multiple div selection onclick

I have multiple divs with their id's, and onclick i store the id of the div in an input value, but it only takes one id, i want to have multiple selection and store all the selected div id's in the same input, here is my code:
function storeId (el) {
$('input').val(el.id);
}
div{
background-color:red;
height: 50px;
width: 50px;
margin-bottom: 15px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="div-1" onclick="storeId(this);">
</div>
<div id="div-2" onclick="storeId(this);">
</div>
<div id="div-3" onclick="storeId(this);">
</div>
<div id="div-4" onclick="storeId(this);">
</div>
<input>
Instead of setting the input's value directly, store the id in an array and then upon each click, update the input with the array's contents.
Also, don't use inline HTML event attributes. There are many reasons not to use this ancient technique that just will not die.
let ids = [];
$("div").on("click", function(){
// If the id is not already in the array, add it. If it is, remove it
ids.indexOf(this.id) === -1 ? ids.push(this.id) : ids.splice(ids.indexOf(this.id),1);
$('input').val(ids.join(", ")); // populate the input with the array items separated with a comma
});
div{
background-color:red;
height: 50px;
width:50px;
margin-bottom: 15px;
display:inline-block; /* Just for the SO space */
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="div-1"></div>
<div id="div-2"></div>
<div id="div-3"></div>
<div id="div-4"></div>
<input>
You have two options based on your requirements:
Append newly clicked id's to the existing value of your input.
Push newly clicked id's into an array which is used to build the value of your input.
Option 1
function storeId (el) {
var currentValue = $('input').val();
$('input').val(currentValue + ', ' + el.id);
}
(Or with newer syntax)
function storeId (el) {
const currentValue = $('input').val();
$('input').val(`${currentValue}, ${el.id}`);
}
Option 2
var storedIds = [];
function storeId (el) {
var index = storedIds.indexOf(el.id);
if (index === -1) {
storedIds.push(el.id);
} else {
storedIds.splice(index, 1);
}
$('input').val(storedIds.join(', '));
}
Edit: Only the array example above checks if the id being stored has already been stored or not.
Please try it:
function storeId (el) {
if ($('input').val().indexOf(el.id) >= 0){
$('input').val($('input').val().replace(el.id + ",", ""));
return
}
$('input').val($('input').val() + el.id + ",");
}

JavaScript: how to give an HTML content to a variable?

I'm studying JavaScript basics and today I built a simple html page which let the user to add/remove a list item. Well, I think I could be there (I know that there are a lot of better solutions, but hey, I'm just learning).
// the function that adds a list item
function addListItem () {
var newLi = document.createElement("li");
newLi.className = "listItem";
// newLi.innerHTML = "<h3>List item</h3> <p>This is a simple list item</p>";
list.appendChild(newLi);
}
You can see full code here: https://jsfiddle.net/l_wel/cuvc0m5g/
The problem is: how you can see within the first function, I put a commented code. It inserts html content inside the new list item. Is there a better way to do it? I mean, what if i would the new list item to have the number of the list item into the ?
Something like that:
List item 1
List item 2
List item 3
etc.. etc..
I know I should use a counter, but I was not able to let the created list items to have all the original html content from the first list item without the need to rewrite it within the function.
Ok, sorry for my bad english and sorry if you think this is a very simple problem, but I tried for hours. I hope you understood what I'm trying to achieve. I think that without the comment it could work as well, depending on the project.
P.S.
I don't know jQuery yet, I wanted to solve this using vanilla js.
See if this works for you:
// store the list
var list = document.getElementById("list");
var number = 1;
// the function that adds a list item
function addListItem () {
number++;
var newLi = document.createElement("li");
newLi.className = "listItem";
newLi.innerHTML = "<h3>List item</h3> <p>This is a simple list item " + number + "</p>";
list.appendChild(newLi);
}
// the function that removes the last list item
function removeListItem () {
number--;
var ulList = document.querySelectorAll("listItem");
var lastLi = list.lastElementChild;
var containerLi = lastLi.parentNode;
containerLi.removeChild(lastLi);
}
// add a list item
var btnAdd = document.getElementById("btnAdd");
if(btnAdd.addEventListener) {
btnAdd.addEventListener("click", addListItem, false);
} else {
btnAdd.attachEvent("click", addListItem, false);
}
// remove the last list item
var btnRemove = document.getElementById("btnRemove");
if(btnRemove.addEventListener) {
btnRemove.addEventListener("click", removeListItem, false);
} else {
btnAdd.attachEvent("click", removeListItem, false);
}
body {
font-family: monospace;
background: #1e2530;
color: #cce8ff;
}
.top { text-align: center; }
#list {
list-style-type: none;
padding: 0;
margin: 10px;
}
.listItem {
background: #cce8ff;
color: #1e2530;
margin-bottom: 10px;
padding: 5px 0 5px 10px;
border-radius: 5px;
}
<body>
<div class="top">
<h2>challenge #8</h2>
<button id="btnAdd">Add an item list</button>
<button id="btnRemove">Remove an item list</button>
</div>
<ul id="list">
<li class="listItem">
<h3>List item</h3>
<p>This is a simple list item 1</p>
</li>
</ul>
</body>
addListItem is a function which can accept parameters. for example, the forEach command is iterating the array and calling the addListItem for each of the items, forEach is calling the callback with two arguments, the first argument is the item itself, and the second is the index of the item in the array...
then you can use the arguments to display the data...
var items = ['Dog','Cat','Mouse'];
function addListItem( title, index ) {
var newLi = document.createElement("li");
newLi.className = "listItem";
newLi.innerHTML = "<h3>"+title+"</h3> " + index;
list.appendChild(newLi);
}
items.forEach( addListItem );
I know you said you didn't want to use JQuery (http://api.jquery.com/append/), but it does make your life easier. For example, you could use the function below. Writing JavaScript is fun, but reading good open source JavaScript (like reading JQuery source) is a far better learning experience.
you are going to need to create a counter to get the list number:
var lst = $('ul.mylist') //class is my list, if ul.mylist doesn't exist use append to append it to the document
for(let i = 0; i < [number of elements]; i++) {
lst.append('<li>List Item' + i + '</li>);
}

Categories

Resources