How to add and remove items in localStorage? - javascript

I am trying to build a logic where we click a button and save an ID in localStorage. We could have 2 ids max. However, we can also remove an ID from it and add a new one. Finally, we cannot add more than 2 and these ids must be unique. These IDs will be then set as an input value in a form.
So I have four steps page:
Homepage page
Search result page
Single article page
Form result page
Each page has:
<form id="post_form_id" method="post" action="/test/compare/">
<input id="input_post_id" type="hidden" name="varPostId" value="">
<button type="submit" class="btn btn-link nav-link" id="jikubox"></i>
<span class="badge badge-secondary">0</span></button>
</form>
Saved IDs will be set in name="varPostId" value=""
Now the tricky part which is confusing me, so localStorage can only have strings so first of all on each page load I do:
var lines = localStorage.getItem("lines") ?
JSON.parse(localStorage.getItem("lines")) : [];
Then since in the article page I have a save button to add the current article id in the array, I do:
<button type="button" class="save_post">SAVE</button>
JS
$(".save_post").on("click", function(e) {
e.stopImmediatePropagation();
if (localStorage.getItem("attempts") >= 2) {
alert('nope');
return;
} else {
// Here I set the id for each article which I saved before via php in a span
var thisId = $(".my_post_id").attr("data-id");
var lines = localStorage.getItem("lines") ? JSON.parse(localStorage.getItem("lines")) : [];
lines.push(thisId);
localStorage.setItem("lines", JSON.stringify(lines));
console.log(lines);
$("#input_post_id").val(JSON.parse(localStorage.getItem("lines")));
var attempts = Number(localStorage.getItem("attempts"));
localStorage.setItem("attempts", ++attempts);
console.log(localStorage.getItem("attempts"));
$("#jikubox span").text(localStorage.getItem("attempts"));
}
});
Notice I have another localStorage: attempts, that is used to add or remove a number I set in a counter badge, basically an indicator which tells how many items I have in my "bag". Remember: max 2 and unique.
So finally on the form result page I have a button to remove added items and the following js is used:
$(".removeJiku").on("click", function(){
// Here I set the id for each article, I do that with php
// <span class="removeId" data-thisPostId="<?php echo $idThisPost; ?>"></span>
var removeThisId = $(".removeId").attr("data-thisPostId");
$(this).parent().fadeOut();
var attempts = Number(localStorage.getItem("attempts"));
localStorage.setItem("attempts", --attempts);
$("#jikubox span").text(localStorage.getItem("attempts"));
lines.splice($.inArray(removeThisId, lines), 1);
localStorage.getItem("lines", lines);
localStorage.setItem("lines", lines);
console.log(localStorage.getItem("lines"));
});
The logic is kinda ok but after I try few times, I eventually get empty lines array and the ids are not set to the input value. I think I over complicated the logic and I was wondering where and how I could simplify and correct it. I am looking into a single general bit of code which handles all of this without complicating the logic.
UPDATE
Thanks to an answer, one bit of code has been simplified and the counter will be set by checking the length of the ids in lines array so We can remove the whole code for the attempts local storage logic
$("#jikubox span").text(lines.length);

Assume you store your IDs in an array-
var idList=["123","456"];
You can store IDs like this -
localStorage.setItem("idList",JSON.stringify(idList));
and fetch idList like this-
JSON.parse(localStorage.getItem("idList")).length
Just make sure to put validations all around.
P.S. No need to count the "attempts" as you can anytime use the below code to find the length of the idList, which can be 2 or any number you want
JSON.parse(localStorage.getItem("idList")).length
UPDATE:
To remove IDs-
function removeId(array, element) {
const index = array.indexOf(element);
array.splice(index, 1);
}
Fetch array from localStorage and pass it into this function -
idList = JSON.parse(localStorage.getItem("idList"))
and call the function like this -
removeId(idList,idToBeDeleted)

This block isn't doing what you want:
lines.splice($.inArray(removeThisId, lines), 1);
localStorage.getItem("lines", lines);
localStorage.setItem("lines", lines);
The second line is getting something from localStorage but isn't doing anything with it. It could be the equivalent of
lines.splice($.inArray(removeThisId, lines), 1);
['one line', 'another line'];
localStorage.setItem("lines", lines);
Just some random value that proceeds to get ignored by the interpreter.
The other problem is that you're setting localStorage.lines to the plain lines variable, not to JSON.stringify(lines).
Note that you can simplify your syntax noise just by doing
localStorage.lines = JSON.stringify(lines);
and
const lines = JSON.parse(localStorage.lines || '[]');

localStorage.getItem('item') only accepts one parameter, which will return the value if it exists in the local storage and will return null item doesn't exist.
localStorage.setItem(item, value) accepts two parameters. item which is a key and the value which is to be saved for the key. If item is already set it will overwrite the value.

Related

Create unique ID with JavaScript selecting a new number from array

I would need some help to adjust this code to suit my needs.
I need to build a javascript that will be stored on a SharePoint page in order to generate on demand a NEW random USERID.
The problem is that I have zero knowledge of javascript, but I am very willing to learn.
The ID is built like this : "IT" & "number from 30001 to 79999"
Example: IT30002
The IDs created must always be different, so those "used" have to be permanently stored in a file.
Every time a new ID is prompted, the script will check the history file and provide a new ID.
Is there a way to achieve what I need?
I have looked at these 2 codes examples:
This example has the key functionality of storing the previous choices, so I am sure I will not use the same ID twice, the problem is that I need numbers, not names and also I need the script to store the numbers permanently
The code below has the other functionality of the "button" to press in order to display the ID.
<html>
<body>
<p id="one"></p>
<button onclick="random()">Random</button>
<script>
function random(){
document.getElementById("one").innerHTML = Math.floor(Math.random() * 10);
}
</script>
</body>
randojs.com makes this pretty easy.
Put this in the head of your page:
<script src="https://randojs.com/1.0.0.js"></script>
And then you can use this JavaScript:
var sequence = randoSequence(30001, 79999);
var currentIndex = 0;
function getNewID(){
return "IT" + sequence[currentIndex++];
}
And you can add this button to the body of your page if you need to:
<button onclick="alert(getNewID());">Alert new ID.</button>
Here's all of that together (click "Run" and then click the "Alert new ID." button that shows up to see it work):
var sequence = randoSequence(30001, 79999);
var currentIndex = 0;
function getNewID(){
return "IT" + sequence[currentIndex++];
}
<script src="https://randojs.com/1.0.0.js"></script>
<button onclick="alert(getNewID());">Alert new ID.</button>
Some references to concepts for you: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set and https://www.w3schools.com/jsref/prop_win_localstorage.asp
Well, all you need to do is generate a random number and store it somewhere. Since, you're using javascript at the front-end, you can't write onto a file without using some backend. If you're fine with storing things for a single user's session, you can use localstorage to store your previous findings in a Set. This lets you store stuff in the browser but for a single user only.
Now the question is what to store? Everytime you generate a random number, look for it in the set from localstorage. If it exists, generate the random number again and repeat lookup process. Keep repeating this random number generation and lookup process until a new random number is found.
What to do if a new random number is finally generated i.e. it doesn't exist in the set? You store it into the set and save the set to the localstorage, stop repeating the process and use the newly generated number for your needs.
That's it.
#Rishinder has explained some of your possible approaches. I will share some code to explain the same. This will print the random numbers on the browser
<html>
<body>
<div id="random"></div>
<button onclick="printRandom()">Generate numbers</button>
</body>
<script>
// fetch and prepare the data
var storage = localStorage.getItem('random'),
existingNumbers = storage ? JSON.parse(storage) : {};
// will generate the random number
function generateRandom() {
var loop = true,
newRand;
while (loop) {
newRand = Math.ceil(Math.random() * 1000000) % 79999;
if (existingNumbers[newRand])
continue;
else if (!existingNumbers[newRand] && newRand < 30001)
continue;
else if (!existingNumbers[newRand]) {
existingNumbers[newRand] = true;
localStorage.setItem('random', JSON.stringify(existingNumbers));
loop = false;
}
}
return Object.keys(existingNumbers).join(', ');
}
// print the existing numbers already stored
document.getElementById('random').innerHTML = Object.keys(existingNumbers).join(', ');
function printRandom() {
document.getElementById('random').innerHTML = generateRandom();
}
</script>
</html>
Hope this helps

Getting lowest available field name with jQuery

I have a form a where fields are named like data[Field][0][value], data[Field][1][value], et cetera, where the integer increments but the rest stays the same. Is there a way of finding out the lowest available integer value with jQuery?
My form uses JS to dynamically add and remove fields (some of which are created by the PHP backend on page load), so I need the lowest integer so as to create fields without conflicts.
Right now on page load, I set field_count to $( "input[name$='[value]'" ).length; and then increment it as I create my inputs. This prevents any namespace collisions, however it doesn't take removed inputs into consideration: I'd like to be able to re-use the names of removed fields.
If you can address data[Field][0] ... data[Field][n], then I would say data[Field] is an Array and data[Field].length returns the current length of that Array. In that case the last value would then be in data[Field][data[Field].length-1] and the next available integer (index) would be data[Field].length.
Just for fun and to demonstrate an alternative: if you ditch the 'naming scheme' and use a data-attribute to keep track of the input fields, something like <input type="text" data-idx="1"> you could use a method to dynamically determine the lowest slot available:
function getFreeSlot(inputFields) {
var freeslots = []
,idxs = inputFields.map(
function (i, el) { return +$(el).attr('data-idx');}
).toArray()
idxs.every( function (v,i) {
void( v-(i ? 1 : 0) !== this[(i>0 ? i-1 : 0)]
&& freeslots.push(this[(i>0 ? i-1 : 0)]+1) );
return v; }, idxs );
return freeslots.length ? freeslots[0] : idxs.length+1;
}
Working example
Assuming your description is of the name attribute of the dynamically created/destroyed fields on the page (in other words, <input name="Data[Field][0][value]">):
var $lastField = $('[name^=Data\\[Field\\]\\[][name$=\\]\\[value\\]]').last();
var lastField = $lastField[0];
This should get the last element in the DOM with a name matching the regex Data[Field][.*][value]. If you want to ensure that the middle bit is a number, you need to get a little more involved:
var $lastField = $('[name^=Data\\[Field\\]\\[').filter(function(idx, element) {
return /Data\[Field\]\[[0-9]+?\]\[value\]/.test($(this).attr('name'));
}).last();
var lastField = $lastField[0];
Can you keep a list of the removed elements and then just pop one of those of the list to use when you need to create a new one (and if the list is empty, just increment your field_count as you are doing now).

Accessing a Dynamically Named array in jQuery/javascript

I wish to name an array according to the table row containing the button that was clicked.
I get the table row thus:
var rowNum = $(this).parent().parent().index();
Now, I wish to name the array and access it.
var arrayName = 'arrTR' + rowNum;
window[arrayName] = new Array();
window[arrayName]["First"] = "Bob";
window[arrayName]["Last"] = "Roberts";
window[arrayName]["email"] = "me#there.com";
//The array should be accessible as arrTR__
alert(arrTR1["Last"]);
The alert does not work, so I am doing something wrong.
How should I refactor the code to allow me to update and access the array?
jsFiddle
What you're doing with the dynamically named variables is essentially creating an array of those variables (one for each rowNum), but giving each of those array elements its own individual named variable.
There is a much better way to do this. Instead of generating a series of dynamically named variables, make a single array or an object. Then add an element or property for each of the dynamically named variables you were going to generate.
Your test code could look like this:
var arrTR = [];
var rowNum = 1;
arrTR[rowNum] = {
First: 'Bob',
Last: 'Roberts',
email: 'me#there.com'
};
alert( arrTR[1].Last );
Alternatively, you can do something with $.data as mentioned in Johan's answer. But if you do use plain JavaScript code, use a single array as described here instead of multiple dynamically named variables.
There are several reasons to do it this way. It's cleaner and easier to understand the code, it may be faster when there are large numbers of entries, and you don't have to pollute the global namespace at all. You can define the var arrTR = []; in any scope that's visible to the other code that uses it.
Arrays and objects are made for keeping track of lists of things, so use them.
There is nothing wrong with your code, and the only place it has error is the alert since it is not defined on the first click button
see this fiddle with a little update
if(rowNum === 1)
alert(arrTR1["Last"]);
else if(rowNum === 2)
alert(arrTR2["Last"]);
fiddle
How about something like this?
$('.getinfo').click(function() {
var result = $('table tr:gt(0)').map(function(k, v){
return {
firstName: $(v).find('.fname').val(),
lastName: $(v).find('.lname').val(),
email: $(v).find('.email').val(),
}
}).get();
//update to show how you use the jQuery cache:
//1. set the value (using the body tag in this example):
$('body').data({ result: result });
//2. fetch it somewhere else:
var res = $('body').data('result');
});
Not sure how you want to handle the first row. I skip in in this case. You can access each row by result[index].
As you might have noticed, this saves all rows for each click. If you want to use the clicked row only, use the this pointer.
http://jsfiddle.net/nwW4h/4/

Deleting multiple values from cookie

I have a list of items, which has two values, a name and an id. Each list item is built using data extracted from a cookie. It also has a delete button, which basically looks at the HTML, and then removes that item from the cookie. It ain't pretty, but it works dandy.
Now all of a sudden, a third value (img src) is introduced. I've therefore made a new function to create the cookie and to build the list. Life is still pretty great.
My problem arise, however, when I attempt to delete the item from the array.
JS
$('.deleteNewCookie').on('click', function () {
var val = $(this).prev().prev().html(); // Search DOM for value
var cookieStr = cookieData.split('|');
createCookie('cokLocation', "", -1);
var newCookie = '';
$.each(cookieStr, function (index, value) {
if (value != '') {
var cookieAdd = value.split(',');
if (cookieAdd[1] != val) {
newCookie = newCookie + value + '|';
}
}
});
createCookie('cokLocation', newCookie, '300');
});
This is how one item in the cookie looks like:
ID,Name,/image.png|
Now, this works as intended when the markup was simpler and there was only the ID and Name, but when I click .deleteNewCookie now, the image sorce remains in the cookie. How would I go about deleting everything in that array (item)? Why does it apparently delete only the first two values?
Markup
<li>
<div class="myLocImg">
<img src="/image.png">
</div>
<div class="myLocInfo">
<h3>NAME</h3>
<span url="locationid=ID" class="btn">More details</span>
<a class="deleteNewCookie">Remove</a>
</div>
</li>
I'm somewhat reluctant to restructure my cookie (as it's being used other places), but I may be persuaded as a last resort. I'm also not using any plugins, and I am not really interested in importing any. Plain JS or jQuery solutions only, please. :)
Fiddle trying to reproduce issue. Code works as expected. Other parts of code are causing issue.
Issue turned out to be relating to "createCookie() and how the values were stored in the HTML itself."

Need to update values in an array but keep the original values to update it again

I want to update a recipe to change the amount of each ingredient. Everything works fine when I put in the first value but after that, it updates against the updated values not the original values. I tried setting up a 2nd array to hold the original values and then exchange them but it didn't work. Here is a live version of script http://jsfiddle.net/a8YTa/3/. I hope seeing the jsfiddle makes my question clear.
Edit: Here is the version where I tried to set up a second array with the original values and it didn't work http://jsfiddle.net/a8YTa/5/
Edit: As requested here is the code in question:
var numIngred = document.getElementsByClassName('ingred');
var initServ = document.getElementsByClassName('servnumber')[0].innerHTML;
var newServ = document.getElementById('newserv');
var divider = 0;
function changeServ(){
divider = initServ/newServ.value;
var i=0;
for(i=0; i<numIngred.length; i++){
numIngred[i].innerHTML = numIngred[i].innerHTML/divider;
}
}
newServ.oninput = changeServ;
html which has the original values:
Serves: <span class="servnumber">8</span><br/><br/>
How many would you like to serve?:<input id="newserv" />
<br/><br/>
<span class="ingred">1</span> Apple <br/>
<span class="ingred">3</span> Peaches <br/>
<span class="ingred">.5</span> Pineapples <br/>
<span class="ingred">2</span>lbs nuts <br/>
<span class="ingred">6</span> small peppers <br/>
The problem with your solution is that your array consists of references to objects in the page:
var numIngred = document.getElementsByClassName('ingred'); //array of HTML elements
So, when you do numIngred[index].innerHTML, it access the current value of the property.
To solve this, you just have to create a new array to store the actual value of innerHTML, not a reference to the objects:
var defaultValues = [];
for(var i = 0; i < numIngred.length; i++) {
defaultValues[i] = numIngred[i].innerHTML;
}
Here is the fiddle:
http://jsfiddle.net/a8YTa/7/
You are calculating your ingredients from the last array, thus your recipe is soon going to become a big mush. :)
You need to create an array which contains the original ingredients and remains unchanged and then calculate your new values from this array when a value (Amount to serve) is entered into the textbox.
Hope this helps.
Re. your comment above:
origingred is the same as numingred. It's a (live) node list containing the same (live) span.ingred elements. Changing them in numingred changes origingred too.
What you need is a real static data source.
You could either store youre ingredient in a real array, like this:
http://jsfiddle.net/EmWeG/
or use extra attributes in your markup:
<span class="ingred" data-orig="1">1</span>
then base your calculation on
parseFloat(numIngred[i].getAttribute('data-orig'))
// i.e. numIngred[i].innerHTML = <the above> / divider;

Categories