When clicking a li element, I want to store its text as a localStorage value only once. If it exists in localStorage, a second click must have no effect (just an alert).
To check if the string exists I'm doing an if inside a for loop, but I'm doing something wrong. (fiddle line 26). My question is if there is a way to make this condition work.
I can not use the colors as keys, since my real script uses large strings instead of colors. Adding a class is not the way I want to solve it.
Thanks
http://jsfiddle.net/SXjtd/3/
// Each color must be stored with localStorage once with a click.
// The second click must have no effect, just run an alert.
// LINE 26: the problem is while checking if the value exists
// Colors are used for this example, the real js uses long strings, that's why I can not use the colors as keys.
localStorage.clear();
// define a value that will be used for increment
if (localStorage.getItem('current_id') === null) {
localStorage.setItem('current_id', 0);
}
$(document).on('click', 'li', function() {
var dl = $('dl');
var current_color = $(this).text();
// each click generates a new key
current_key = 'color_id_' + (parseInt(localStorage.getItem('current_id')) + 1);
localStorage.setItem('current_id', (parseInt(localStorage.getItem('current_id')) + 1));
$('<dt>' + current_key + '</dt><dd>' + current_color + '</dd>').appendTo(dl);
// THE PROBLEM IS HERE
// I want to know how to check if a value exists in localStorage
// if this value doesn't exist, it is set in localStorage with a click
for (var i = 0, len = localStorage.length; i < len; i++) {
if (localStorage.getItem('color_id_' + i) == current_color) {
alert('Color exists in localStorage.');
} else {
alert('New Color added to localStorage');
localStorage.setItem(current_id, current_color);
}
}
});
I think this solution can help to you:
$(document).on('click', 'li', function() {
var color = $(this).text();
if(!localStorage.getItem("colors")){
localStorage.setItem("colors", "[]");
}
var list = JSON.parse(localStorage.getItem("colors"));
var exist = false;
for(var i = 0; i < list.length; i++)
if(list[i] == color) {
exist = true;
break;
}
if(!exist) list.push(color);
else{
alert("EXIST");
}
localStorage.setItem("colors", JSON.stringify(list));
});
Working demo.
The idea is storing selected colors in array, then save to localStorage. If user clicks to color we just get data serialize it and then check for existing.
Get data serialize it
Check if color exist
Save if not exist, otherwise show alert
Deserialize data and store
Another way:
$(document).on('click', 'li', function() {
var color = $(this).text();
var nSpace = "colors." + color;
if(localStorage.getItem(nSpace))
alert("EXIST");
else
localStorage.setItem(nSpace, true);
});
Idea is using namespaces.
Why don't you just use the string you're storing as the key of the localStorage entry?
Related
The Problem: I am able to retrieve the HTMLOptionsCollection object from the select element, but unable to get an accurate length or array from it.
For context, I am trying to make an array from the HTMLOptionsCollection object so I can loop through the options to add the selected attribute to one of the option elements. Also, I'm doing this for a chrome extension so I'm not sure if there would be any odd compatibility issues because of that.
Right now, I have this code:
var dropdown = document.getElementById("clients"); // Initially empty
fillDropdown(); // This does in fact fill the select element with option elements
console.log(dropdown) // Returns select element
console.log(dropdown.options); // Returns filled HTMLOptionsCollection object
console.log(dropdown.options.length); // Returns 0
// Make an array out of HTMLOptionsCollection object (taken from https://stackoverflow.com/questions/6138042/javascript-selecbox-options-to-array)
var arr = Array.apply(null, dropdown.options).map(function(el) { return el.value; });
console.log(arr); // Returns Array[0]
Here are the console.log results:
I did not expect the length to be inaccurate at all and can't figure out why this is. Any help is greatly appreciated!
EDIT: Here is my fillDropdown() function. It's ultimate goal is to append option elements to the select element. The extra jargon is to prevent options from getting too long word wise.
// Input: None
// Output: None
// Proceeds to fill the clients dropdown with clients from local storage
function fillDropdown() {
chrome.storage.local.get(function(data) {
if (typeof data.lastClientName !== "undefined") {
for (var i = 0; i < clients.length; i++) {
// Create an option element to add to the dropdown.
var clientOption = document.createElement("option");
// cutoff is an array which holds whole words. This is done to cleanly cut off a name.
var cutoff = clients[i].split(" ");
// A clients name may have no more than 4 words to its name.
if (cutoff.length > 4) {
cutoff = cutoff[0] + " " + cutoff[1] + " " + cutoff[2] + " " + cutoff[3] + " ...";
// The full name attribute is used to store the actual name.
clientOption.setAttribute("fullName", clients[i]);
// The value and innerHTML are both the same and are user visible.
clientOption.setAttribute("value", cutoff);
if (data.lastClientName === cutoff) {
dropdown.value = clientOption.value;
}
clientOption.innerHTML = cutoff;
}
else {
// fullName is added here for consistency
clientOption.setAttribute("fullName", clients[i]);
clientOption.setAttribute("value", clients[i]);
if (data.lastClientName === clients[i]) {
dropdown.value = cutoff;
}
clientOption.innerHTML = clients[i];
}
dropdown.appendChild(clientOption);
}
}
else {
for (var i = 0; i < clients.length; i++) {
// Create an option element to add to the dropdown.
var clientOption = document.createElement("option");
// cutoff is an array which holds whole words. This is done to cleanly cut off a name.
var cutoff = clients[i].split(" ");
// A clients name may have no more than 4 words to its name.
if (cutoff.length > 4) {
cutoff = cutoff[0] + " " + cutoff[1] + " " + cutoff[2] + " " + cutoff[3] + " ...";
// The full name attribute is used to store the actual name.
clientOption.setAttribute("fullName", clients[i]);
// The value and innerHTML are both the same and are user visible.
clientOption.setAttribute("value", cutoff);
clientOption.innerHTML = cutoff;
}
else {
// fullName is added here for consistency
clientOption.setAttribute("fullName", clients[i]);
clientOption.setAttribute("value", clients[i]);
clientOption.innerHTML = clients[i];
}
dropdown.appendChild(clientOption);
}
}
});
}
Also the only html to be concerned with here is
<select name="clients" id="clients"></select>
Try this:
const newArr = Array.from(dropdown.options);
console.log(newArr.length)
You can find other ways to do this here: https://hackernoon.com/htmlcollection-nodelist-and-array-of-objects-da42737181f9
I'm so sorry, I just realized that clients in fillDropdown() was not defined. Clients is supposed to be an array of business names that I would use to actually fill the dropdown. Now I'm curious as to why I didn't get an error for that in my console. I thought any undefined variable would show in the console.
On top of clients not being defined, I also had to make fillDropdown a callback function.
Thank you everyone for helping!
I'm writing a GreaseMonkey script to make some much-needed enhancements to my employer's internal job search website.
The loop I've included here iterates through all the rows in a table returned by a job search. I'm adding icons to the left side of each row to track communication, acceptance, and rejection. The data is logged in the browser localStorage as a JSON object converted to a string. An example localStorage entry looks like this:
job_12345 = '{"accept":"9/3/2017, 6:50 PM","reject":"9/3/2017, 6:50 PM"}'
In the below function, lstore is a localStorage key+value like the above example. The loop works fine until I try to add in a bit of conditional formatting that shows a "disabled" icon (really just 50% opacity) for job actions that don't exist in localStorage.
It's that three-line IF statement near the bottom of my loop that I just cannot figure out. It works on the first time in the array, and then the array simply breaks and the function ends.
function checkRows() {
if (document.getElementsByClassName("jstat") != null) {
clearInterval(startIt2);
var jobs2 = document.getElementById('results').getElementsByClassName('jrow');
for (var j = 0; j < jobs2.length; j++) {
// conditional formatting by status
var job_status = jobs2[j].getElementsByClassName("jstat")[0].innerText;
job_status = job_status.toLowerCase();
job_status = job_status.replace(/ /g, "_");
// apply conditional class to each row
jobs2[j].classList.add(job_status);
// get job id for shortcut buttons
var jid = jobs2[j].getElementsByTagName("input")[0].value;
// get the table cell where the buttons go
var job_box = jobs2[j].getElementsByClassName("col-chk")[0];
job_box.classList.add("icon");
// read localStorage for selected job
var lstore = JSON.parse(localStorage.getItem("job_" + jid));
// array for adding buttons
var arr_acts = ["mail", "accept", "reject"];
for (var a = 0; a < arr_acts.length; a++) {
// get the action from the array
var action = arr_acts[a];
// create a new span to hold the icon
var span = document.createElement("span");
span.setAttribute("data-jobid", jid);
span.setAttribute("data-action", action);
span.id = (action + jid);
span.classList.add(action);
span.onclick = toggleLocal;
//span.classList.add("disabled");
// set the icon for each action
var icon;
switch (action) {
case "mail":
icon = "📧";
break;
case "accept":
icon = "👍";
break;
case "reject":
icon = "👎";
break;
}
// if the action doesn't exist in localStorage, set the class to "disabled"
console.log(lstore);
// *** this is where the loop breaks *** //
if (lstore.hasOwnProperty(action)) {
span.classList.add("disabled");
}
span.innerHTML = icon;
// render the icon
job_box.appendChild(span);
}
}
}
}
Thanks for the help!
I believe I've figured this out - my variable calling to localStorage (lstore) is null if nothing exists for that job ID, and that appears to have been breaking my IF statement. This worked:
// if the action doesn't exist in localStorage, set the class to "disabled"
if ((lstore == null) || !(lstore.hasOwnProperty(action))) {
span.classList.add("disabled");
}
I'm trying to set objects into localStorage with a format similar to the following:
[{"1":{"property1":false,"property2":false}},{"2":{"property1":false,"property2":false}}]
Where I'd be able to set the 1 or 2 based on a dynamic value I'm getting from a REST call. What I have so far is:
// check if session exists and create if not
var StorageObject = JSON.parse(localStorage.getItem("session")) || [];
//see if the current id from the REST call is in storage and push with properties if not
if ( !StorageObject[thisItemsListID] ) {
var itemProperties = {};
itemProperties[thisItemsListID] = {};
itemProperties[thisItemsListID]["property1"] = false;
itemProperties[thisItemsListID]["property2"] = false;
StorageObject.push(itemProperties);
localStorage.setItem('session', JSON.stringify(StorageObject));
}
I can get the data into localStorage using this format but StorageObject[thisItemsListID] always gets into the if statement and generates a duplicate item in localStorage and I'm not sure how to access this with a variable. I'm trying to append the new ID if it doesn't exist so if {1:{} exists but current ID is 2 I need to push the new value.
I'm close here and maybe I need to reevaluate the format I'm storing the data string but I'm going in circles here and could use a point in the right direction.
Well, the duplicate item is happening in StorageObject.push(itemProperties).
Try this to update the object:
//StorageObject.push(itemProperties); <-- remove
StorageObject[thisItemsListID] = itemProperties;
[EDIT]
If you want to keep [{"1":{"property1":false,"property2":false}},{"2":{"property1":false,"property2":false}}]. To conditional would be a bit different.
var haveItem = StorageObject.filter(function(item){
return Objects.keys(item)[0] == thisItemsListID;
}).length > 0;
if ( !haveItem ) {
var itemProperties = {};
itemProperties[thisItemsListID] = {};
itemProperties[thisItemsListID]["property1"] = false;
itemProperties[thisItemsListID]["property2"] = false;
StorageObject.push(itemProperties);
localStorage.setItem('session', JSON.stringify(StorageObject));
}
Are you trying to update the object or just overwrite it? Filipes response illustrates how to update the entire storage object by just reassigning the object with the new value.
If you wanted to update just as section/ value of the object you could do so using a for loop. This would allow you to scan the array locate the one property and then remove it, updated it, overwrite it etc.
Here is an example of the loop. Bear in mind This is a snippet from a report library I was building. It uses angular $scope but it is a complex type doing a similar action to your update (here I am setting a label as a favorite/bookmark)
function OnFavoriteComplete(response) {
var id = response.config.data.reportId; //dynamic values set by client
var isFavorite = response.config.data.isFavorite;//dynamic values set by client
var arrayCount = $scope.reportList.length;
//loop my current collection and look for the property id of the label
//then check to see if true or false/this was a toggle enable disable
if (isFavorite) {
for (var i = 0, iLen = arrayCount; i < iLen; i++) {
if ($scope.reportList[i].reportId == id) {
$scope.reportList[i].isFavorite = false;
}
}
}
//if false update the property with the new value
else {
for (var i = 0, iLen = arrayCount; i < iLen; i++) {
if ($scope.reportList[i].reportId == id) {
$scope.reportList[i].isFavorite = true;
}
}
}
};
If you are using another framework like lowDash it has some really nice helper functions for updating and evaluating arrays.
I'm using localStorage to store some data and all the data are concatenated separated by \n. I want to remove specific data in localStorage and i'm using listbox to display all the data.
example {"rowdata":"data1\ndata2\ndata3"} // the three data are stored in localStorage, the key of rowdata in the localStorage is storedata and the rowdata is the value of storedata that have three data concatenated.
is there an easy way to remove the selected data, example i want to remove data3. i'm using google chrome browser..
code for display:
function populate(){
for(i=0; i<rowdata.length; i++){
var select = document.getElementById("test"); // id of the listbox
var splitRow = rowdata.split("\n");
var row = splitRow[i];
if(row != undefined)
select.options[select.options.length] = new Option(row);
}
}
code for remove:
function removeSelectedItem(){
var htmlSelect=document.getElementById('test'); // id of the listbox
if(htmlSelect.options.length == 0)
{
alert('You have already removed all list items');
return false;
{
var optionToRemove = htmlSelect.options.selectedIndex;
htmlSelect.remove(optionToRemove);
if(htmlSelect.options.length > 0)
{
htmlSelect.options[0].selected=true;
{
alert('The selected data has been removed successfully');
return true;
}
Thanks...
Not sure if I clearly understood the question, but if you just need to update state if rowdata variable then try put the code below before removing an option from select in RemoveSelectedItem (man, start function names with lowercase!):
rowdata = ("\n" + rowdata + "\n").replace("\n" + htmlSelect.options[htmlSelect.options.selectedIndex].innerHTML + "\n", "").trim()
this is my first post in stackoverflow.. I am trying to iterate over an object(my implementation is an associative array) which in turn has some properties. Now I wish to construct another array out of it in order to use it as a localsource in jquery autocomplete widget for seach operations. Now the problem is that i am using for in loop to that according to the documenations available... However the output is always one less than the original object. The itearation involving the last element is not performed at all. Below is the sample object that I am using as input.
SubTeachPair = object{"5CS1":{SubAbbrev:"CA-L",SubCode:"5CS1",SubName:"Computer Architecture",TeacherId:"1",TeacherName:"Ayush Pandey",label:"Computer Architecture",value:"5CS1"},"5CS2":{SubAbbrev:"CA-P",SubCode:"5CS2",SubName:"Computer Engg",TeacherId:"10",TeacherName:"MAyush Pandey",label:"Computer Engg",value:"5CS2"}}
It has this kind of elements and is dynamically generated so the property names are variable. The loop construct that I have written is
var SubSource = [];
console.log(SubTeachPair);
var count = 0;
for(sub in SubTeachPair){
console.log(count);
SubSource[count] = {};
SubSource[count]['label']=SubTeachPair[sub]['label'];
SubSource[count]['value']=SubTeachPair[sub]['value'];
count++;
}
However, the result for the given input is only:
object{{ label: "Computer Architecture", value: "5CS1"}}
Am I missing something here?
edit-- The function that produces the input object is as follows(It is triggered onclick by the next button).
$('#' + $(this).attr("id")).autocomplete({
source : 'search',
minLength : 1,
change : function(event, ui) {
if( typeof ui.item != 'undefined') {
SubTeachPair[$(this).attr("id")] = {};
// console.log(ui.item);
SubTeachPair[$(this).attr("id")]['value'] = $(this).attr("id");
SubTeachPair[$(this).attr("id")]['label'] = $('label[for="' + this.id + '"]').html();
SubTeachPair[$(this).attr("id")]['SubCode'] = $(this).attr("id");
SubTeachPair[$(this).attr("id")]['SubName'] =$('label[for="' + this.id + '"]').html();
SubTeachPair[$(this).attr("id")]['SubAbbrev'] =$('label[for="' + this.id + '"]').attr('id');
SubTeachPair[$(this).attr("id")]['TeacherId'] = ui.item.id;
SubTeachPair[$(this).attr("id")]['TeacherName'] = ui.item.value;
// console.log(SubTeachPair);
//window.SubTeachPair = SubTeachPair;
}
}
});
I think I have found the cause of the error -- the object that is the input is actually the out put of another form that uses jquery autocomplete . Now when I enter something in the input and then click on the suggestion, the suggestion is filled in the text input, however if i do not click outside the input text and directly click the button which triggers my script, I get that error. Otherwise its fine. Is there any way to avoid that?
In your code, the array SubSource and count are not defined, You have to declare:
var SubSource = [];
var count = 0`
before for(sub in SubTeachPair) {...}
See http://jsfiddle.net/abu5C/
Try this:
SubSource[count] = {};
for(sub in SubTeachPair) {
console.log(count);
SubSource[count]['label']=SubTeachPair[sub]['label'];
SubSource[count]['value']=SubTeachPair[sub]['value'];
count++;
}