Reputation: 31
I am a Javascript beginner trying to use local storage to store a set of button toggle states (on/off). My goal is to be able to toggle a set of svg fills, on and off, and have the states stored, returning to the selected state, after a browser refresh.
//LOOP 1 - GET STORED VALUES AND SET SVGs ACCORDINGLY
function grabStoredStates(){
for (let i=0; i < localStorage.length; i++) {
let key = localStorage.key(i);
let value = localStorage.getItem(key);
if (value == "on") {
//"cbox" below is from LOOP 2 - maybe it's causing the problem
cbox[i].style.fill = "coral";
}
}
}
//LOOP 2 - TOGGLE AND STORE TOGGLE STATES
let cbox = document.querySelectorAll(".toggleMe");
for (let i = 0; i < cbox.length; i++) {
cbox[i].addEventListener("click", function() {
if (cbox[i].style.fill == "coral") {
cbox[i].style.fill = "white";
localStorage.setItem(cbox[i].id, "off");
}else{
cbox[i].style.fill = "coral";
localStorage.setItem(cbox[i].id, "on");
}
});
}
<body onload="grabStoredStates()">
<svg height="300" width="700>
<g pointer-events="all">
<rect class="toggleMe" cursor:pointer; id="rect1" width="100" height="100" x="50" y="80" fill="aliceblue" stroke="black" stroke-width="2" />
<rect class="toggleMe" cursor:pointer; id="rect2" width="100" height="100" x="175" y="80" fill="aliceblue" stroke="black" stroke-width="2" />
<rect class="toggleMe" cursor:pointer; id="rect3" width="100" height="100" x="300" y="80" fill="aliceblue" stroke="black" stroke-width="2" />
<rect class="toggleMe" cursor:pointer; id="rect4" width="100" height="100" x="425" y="80" fill="aliceblue" stroke="black" stroke-width="2" />
</g>
</svg>
</body>
My problem is that if you load the page, and then open local storage in Chrome Dev Tools, (Application > Storage > Local Storage), and then click the rectangles in sequence from left to right you should see this, as they toggle correctly from light blue to orange:
Key Value
rect1 on
rect2 on
rect3 on
rect4 on
In local storage, the keys and values are, to that point, matched properly, showing all squares to be orange, as intended. However, if you then click rect3, (the third rectangle from the left), for instance, toggling it to light blue, with an expected corresponding "off" state -- on refreshing the page, the stored values appear to get jumbled, with the loop apparently renumbering the rectangles as if it was starting from [0] -- ignoring the intended key/value pairing. You then see this:
Key Value [PROBLEM]
rect1 on
rect2 on [ --> Displays as Off]
rect3 off [ --> Displays as on]
rect4 on
Is there any way that I can make the keys and values stay together, regardless of loop called. The "grabStoredStates()" loop appears to work, as tested by manually changing values and refreshing... I suspect, then, that it has to do with the transition between two separate for loops? Maybe the "cbox" loop should also be in a function? I looked at the prior stack overflow "can't use booleans in local storage" answer, but as I'm using "on" and "off" that doesn't seem to apply. I also saw a jQuery answer, but I'm not familiar with that library yet. (I wouldn't, however, turn down a jQuery answer if offered.)
Again, my original goal was to be able to toggle svg fills on and off and have the states stored after browser refresh. I'd love a more elegant way if there is one...
Thanks everyone, in advance, for your help.
Upvotes: 1
Views: 107
Reputation: 13100
Here I tried to make a simpler version. The presentation of the <rect>
s relies on an attribute called data-state
. When the document is loaded the attributes will be updated (I commented out the code for localStorage and replaced it with the array testArr
). An event listener on <g>
will handle click events and test if a <rect>
was clicked, if so, the attribute is updated/toggled. After that the values are all saved to localStorage/testArr.
var cbox;
var testArr = ['on', 'off', 'off', 'on']; // for testing
document.addEventListener('DOMContentLoaded', e => {
cbox = document.querySelectorAll(".toggleMe");
/* // Code for localStorage:
Array.from(Array(localStorage.length).keys()).forEach(i => {
let key = localStorage.key(i);
let value = localStorage.getItem(key);
cbox[i].attributes['data-state'] = value;
});*/
testArr.forEach((state, i) => cbox[i].attributes['data-state'].value = state); // for testing
document.querySelector('g').addEventListener('click', e => {
if(e.target.classList.contains('toggleMe')){
let currentValue = e.target.attributes['data-state'].value;
e.target.attributes['data-state'].value = (currentValue == 'on') ? 'off' : 'on';
/* // Code for localStorage:
[...cbox].forEach((elm, i) => {
let value = elm.attributes['data-state'].value;
localStorage.setItem(`Rect${i}`, value);
});*/
testArr = [...cbox].map(elm => elm.attributes['data-state'].value); // for testing
console.log(testArr.join(',')); // for testing
}
});
});
rect.toggleMe {
cursor: pointer;
width: 50px;
height: 50px;
stroke: black;
stroke-width: 2px;
}
rect[data-state="on"] {
fill: aliceblue;
}
rect[data-state="off"] {
fill: coral;
}
<body>
<svg height="300" width="300">
<g>
<rect class="toggleMe" data-state="off" x="50" y="80" />
<rect class="toggleMe" data-state="off" x="110" y="80" />
<rect class="toggleMe" data-state="off" x="170" y="80" />
<rect class="toggleMe" data-state="off" x="230" y="80" />
</g>
</svg>
</body>
Upvotes: 0