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");
}
Related
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.
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?
Using Joomla custom output module to display articles for a category, but have limited space.
Here is the test site: http://cshtest.camdendiocese.org/
In the footer you'll see the third of 4 columns has data. It scrolls forward and backwards using JavaScript. But as you see it has 4 columns, all of which work the same on different categories.
I'm having trouble with creating the other columns because they all will need to use separate data items, hence separate modules. I was trying to create an object but got stopped specifying a JavaScript object inside PHP.
Here is the JavaScript that is working:
// This script provides forward and backward paging for article intros in footer-1
// create an array of strings
// Each array element is initalized at this time to establish global scope
var mytexts = [];
mytexts[0] = "<h2>this is text string one</h2>";
mytexts[1] = "<h2>this is text string two</h2>";
mytexts[2] = "<h2>this is text string three</h2>";
// initialize variables
var txtNum = 0;
var txtLength = mytexts.length - 1;
// function to change string
function changeText(direction, col) {
// get next text number
txtNum = txtNum + direction;
// make sure we loop
if (txtNum > txtLength) {
txtNum = 0;
}
if (txtNum < 0) {
txtNum = txtLength;
}
// change the src attribute of the image
var colx = "col-" + col;
document.getElementById( colx ).innerHTML = mytexts[txtNum];
return false; // prevent default link
}
Here is the constructor I made to try to make an object:
function CatArticles(x0, x1, x2) {
// This script provides forward and backward paging for article intros in footer-1
// create an array of strings
// Each array element is initalized at this time to establish global scope
this.mytexts = [];
this.mytexts[0] = x0;
this.mytexts[1] = x1;
this.mytexts[2] = x2;
// initialize variables
this.Num = 0;
this.txtLength = this.mytexts.length - 1;
this.changeText = changeText;
}
// function to change string
function changeText (direction, col) {
// get next text number
this.Num = this.Num + direction;
// make sure we loop
if (this.Num > this.txtLength) {
this.Num = 0;
}
if (this.Num < 0) {
this.Num = this.txtLength;
}
// change the src attribute of the image
var colx = "col-" + col;
document.getElementById( colx ).innerHTML = this.mytexts[this.Num];
return false; // prevent default link
}
I can instantiate the object with script tags in PHP but can't think how to get the variable created in the html to be output. Do you have a suggestion or approach?
If I understand well, considering they are two different scripts, server side and client side another, the solution would be to use ajax for interaction, after the page has already been generated by php.
I've been trying this for a while now and could not find anything online...
I have a project, where tablerows get added to a table. Works fine.
Now I want to save the Table in the localStorage, so I can load it again. (overwrite the existing table).
function saveProject(){
//TODO: Implement Save functionality
var projects = [];
projects.push($('#tubes table')[0].innerHTML);
localStorage.setItem('projects', projects);
//console.log(localStorage.getItem('projects'));
The problem is the Array "projects" has (after one save) 2000+ elements. But all I want is the whole table to be saved to the first (or appending later) index.
In the end I want the different Saves to be listed on a Option element:
function loadSaveStates(){
alert('loading saved states...');
var projects = localStorage.getItem('projects');
select = document.getElementById('selectSave'); //my Dropdown
var length = projects.length,
element = null;
console.log(length);
for (var i = 0; i < length; i++) {
element = projects[i];
var opt = document.createElement('option');
opt.value = i;
opt.innerHTML = 'project ' + i;
select.appendChild(opt);
}
}
Can anyone tell me what I am doing wrong?
You can easily do this by jquery, are you interested in this, if yes.. then try following code
For setting the value
$.jStorage.set("projects", $.trim(projects));
For Getting the data
$.jStorage.get("projects");
For deleting the data with key
$.jStorage.deleteKey("projects");
I coose to stay with localStorage, but insted of using an Array I just let the user give every project a name and create a new Item for every Save:
function saveProject(){
//TODO: Implement Save functionality
var pname=prompt("Please enter your project name:","projectname")
var text = $('#mainTable')[0].innerHTML;
//console.log(text);
localStorage.setItem(pname, text);
//console.log(localStorage.key(2));
loadSaveStates();
}
function loadProject(){
var selected = $('#selectSave')[0].selectedIndex
//console.log(selected);
if (localStorage.key(selected) == 'jStorage'){
selected++;
}
var innerHTMLTable = localStorage[localStorage.key(selected)];
//console.log(innerHTMLTable);
$('#mainTable')[0].innerHTML = innerHTMLTable;
updateHandlers();
}
function deleteProject(){
var selected = $('#selectSave')[0].selectedIndex
var pname = $('#selectSave')[0].options[selected].value
$('#selectSave')[0].remove(selected);
localStorage.removeItem(pname);
//console.log(pname);
loadSaveStates();
}
I have an application where I am adding li elements to the web page. I need to change the class name of the element to "done" inside of local storage when I mark it as "done" on the webpage. (It should say done: true). With my current code I am unintentionally making two items in local storage, one which is done: true and the other which is done: false. I'll show my code here:
function updateDone(e) {
var spanClicked = e.target;
var id = spanClicked.parentElement.id;
var done = spanClicked.parentElement.className;
spanClicked.innerHTML = " ✔ ";
spanClicked.setAttribute("class", "done");
var key = "todos" + id;
for(var i = 0; i < todos.length; i++) {
if(todos[i].id == id) {
var mark = todos[i];
mark.done = true;
console.log(mark);
spanClicked.setAttribute("class", "done");
var newKey = JSON.stringify(mark);
localStorage.setItem(key, newKey);
if(mark.done == false) {
spanClicked.setAttribute("class", "not done");
spanClicked.innerHTML = "not done";
}
}
}
}
They are both labeled with the same id which is how I keep track of each item, yet there are two of them. Also, when i refresh the page there are two list items shown, one which is marked done. My question is how do I prevent another item from being created and instead mark just one item as done in localStorage?
You need a way to uniquely identify each item, so you can ensure your marks are being set on the items you intend and are not overwriting because you might have, say, two items with the same key. Since you are looping through a list, maybe you can change your keys to be composed of two parts.
var parent_key = "todos" + parent_id;
And then, in the loop :
var store_key = parent_key + ":" + i;
...
localStorage.set(store_key,newKey);
This way (so long as the order is going to be consistent), you can separate multiple list elements from the same parent.
As commented, a live example in jsFiddle or something would help better address your requirement.
However if this solution is insufficient you could try the following idea, effectively setting a "table" within localstorage.
var parent_key = "todos" + id;
var parent_table = {};
// for loop
parent_table[i] = newKey;
// end of for loop
localStorage.set(parent_key,parent_table);
So you have a table inside of local storage, to give you finer granularity.