skip to Main Content

I have a list of buttons in a menu that all have a unique ID and a collapse-able menu underneath it. When I click one of these buttons I want to use the ID of the clicked button to check if there is a key with the same ID already stored in the local storage.

If there IS already a key in local storage with the same clicked button ID, then I want to update the value of the key to true/false (depending on the collapse-able menu).

If there IS NOT already a key in local storage with the same clicked button ID, then I want to add a new key and value to the local storage using the ID of the clicked button.

I haven’t been able to figure out how to use the ID of the clicked button to check if it exists in local storage. I have been able to write a single new key, but it just replaces the existing key/value, so it’s only replacing a single pair instead of adding new pairs to the object/array.

I have searched for several hours already today and I just haven’t been able to figure out why this won’t work.

MORE INFO:
I wanted to add a little reasoning to why I am doing this. I have over 30 different collapse-able panels in a side navigation menu. I want the browser to remember which panels the user has open/closed, but I didn’t want to write and if/else statement for every single one. And there will be panels that are added or removed in the future, so I didn’t want to have to update this local storage code in the future. I just want everything to happen automatically in the local storage. So, I am just looking for a way to dynamically write these panel IDs and states to the local storage, and then loop through all of them in a mutation observer on page load to open/close them automatically.

Here is my HTML code that has specific classes and IDs:

<a href="#" class="rpLink rpExpandable rpExpanded" id="button-id-one">Menu Button One</a>
<a href="#" class="rpLink rpExpandable" id="button-id-two">Menu Button Two</a>
<a href="#" class="rpLink rpExpandable" id="button-id-three">Menu Button Three</a>

Here is my JS code that listens for when the menu buttons are clicked:

$(document).on("click",".rpExpandable",function()
{
    var panelId = $(this).attr("id");
    var panelState = false;

    if($(this).hasClass("rpExpanded")){panelState = true;}

    //update left menu panel settings in local storage
    persistLeftMenuPreferences(panelId,panelState);
});

Here is my JS code that tries to check the local storage and add to it or update it:

function persistLeftMenuPreferences(panelId,panelState)
{
    setTimeout(function()
    {
        var panelKey = "panel-preferences";
        var panelPreferences = {};

        if(localStorage.getItem(panelId) !== null)
        {
            console.log("not null");
            panelPreferences.panels[panelId] = panelState;
        }
        else
        {
            console.log("null");
            panelPreferences.panels = [panelId,panelState];
        }

        //set item values in local storage
        localStorage.setItem(panelKey, JSON.stringify(panelPreferences));
        Cookies.set(panelKey, JSON.stringify(panelPreferences), {expires: 360});
    }, 200);
}

EDIT/UPDATE:

So, I have found that the code below is actually doing what I want for the most part. The only down-side is that it’s adding individual key/value pairs instead of putting them all in to one object that contains all of the key/value pairs inside of it. If you can show me how to put them all inside of a single object, that would be ideal.

function persistLeftMenuPreferences(panelId,panelState)
{
    setTimeout(function()
    {
        var panelPreferences = {};

        panelPreferences = [panelId,panelState];

        //set item values in local storage
        localStorage.setItem(panelId, JSON.stringify(panelPreferences));
        Cookies.set(panelId, JSON.stringify(panelPreferences), {expires: 360});
    }, 200);
}

LATEST EDIT/UPDATE:

I’ve simplified my code even more. I am just writing the panelId and panelState directly to the local storage instead of trying to put it in an object. Ultimately I’d like each key/value pair to be inserted in to a single object, but I will save it for a later day.

function persistLeftMenuPreferences(panelId,panelState)
{
    setTimeout(function()
    {
        //set item values in local storage
        localStorage.setItem(panelId, panelState);
        Cookies.set(panelId, panelState, {expires: 360});
    }, 200);
}

Here is a screenshot of how this looks when adding them to the local storage:
screenshot of local storage key/value paris

2

Answers


  1. You need to get the old value of panelPreferences from local storage and update that, not start with an empty object.

    You shouldn’t be using panelId as the local storage key. It’s the key in the JSON object that’s saved in local storage, with the key panel-preferences.

    function persistLeftMenuPreferences(panelId, panelState) {
      setTimeout(function() {
        var panelKey = "panel-preferences";
        var panelPreferences
    
        if (localStorage.getItem(panelKey) !== null) {
          console.log("not null");
          panelPreferences = JSON.parse(localStorage.getItem(panelId));
        } else {
          console.log("null");
          panelPreferences.panels = {};
        }
        panelPreferences[panelId] = panelState;
        //set item values in local storage
        localStorage.setItem(panelKey, JSON.stringify(panelPreferences));
        Cookies.set(panelKey, JSON.stringify(panelPreferences), {
          expires: 360
        });
      }, 200);
    }
    Login or Signup to reply.
  2. Following on from the comment of yesterday regarding JSON vs multiple name/value pairs perhaps this might be of interest. I wrote a simple class a while back for working with storage objects which makes the setting and toggling (true/false) of a key/value pair remarkably simple in this instance. By default it will be creating and working with JSON which is ideal for your use case. I can’t see why the need to use both localStorage and a cookie – no jQuery code as I never use it but sure it’ll be an easy transposition…

    <!DOCTYPE html>
    <html lang='en'>
        <head>
            <meta charset='utf-8' />
            <title></title>
            <style> 
                /* just a little basic css to prettify it */
                body{ 
                    width:100%;
                    height:100vh;
                    margin:0;
                    display:flex;
                    flex-direction:column;
                    justify-content:flex-start;
                    align-content: stretch;
                }
                body *{
                    transition:linear all 250ms
                }
                .rpLink{ 
                    margin:0.5rem;
                    padding:1rem;
                    border:2px solid grey;
                    border-radius:0.5rem;
                    background:white;
                    align-self:auto;
                    width:150px;
                    text-align:center;
                    text-decoration:none;
                }
                .rpLink:hover{
                    background:rgba(0,200,0,0.25)
                }
                
                .rpExpLogged{
                    /* 
                    helper css class. 
                    Open the panel, make it sing and dance or whatever
                    */
                    border:2px solid black;
                    background:rgba(0,200,0,0.75);
                }
            </style>
        </head>
        <body>
        
            <!-- example NAV menu taking some of observed panel names/ids -->
            <a href="#" class="rpLink rpExpandable rpExpanded" id="sis-carrier-info-panel">SIS Carrier Info</a>
            <a href="#" class="rpLink rpExpandable" id="sis-app-mover-panel">SIS App mover</a>
            <a href="#" class="rpLink rpExpandable" id="sis-student-survey-panel">SIS Student survey</a>
            <a href="#" class="rpLink rpExpandable" id="sis-reports-panel">SIS Reports</a>
            <a href="#" class="rpLink rpExpandable" id="sis-loan-mods-panel">SIS Loan Mods</a>
            <a href="#" class="rpLink rpExpandable" id="sis-finance-panel">SIS Finance</a>
            
            
            
            <script>
            
                /*************************************************
                    basic class to further simplify working with
                    storage objects. By default localStorage is
                    used, easily overridden.
                */
                class StoreFactory{
                    constructor( name, local=true ){
                        this.name=name;
                        this.engine=local==true ? localStorage : sessionStorage;
                        return this;
                    };
                    set( data ){
                        if( data )this.engine.setItem( this.name, JSON.stringify( data ) );
                    };
                    get(){
                        return this.exists( name ) ? JSON.parse( this.engine.getItem( this.name ) ) : false;
                    };
                    exists(){
                        return this.engine.getItem( this.name )==null ? false : true;
                    };
                    create(){
                        if( !this.exists() ) this.set( arguments[0] || {} );
                        return this;
                    };
                    getkey( key ){
                        return this.get().hasOwnProperty( key ) ? this.get()[ key ] : false;
                    };
                    setkey( key, value ){
                        let data=this.get();
                            data[ key ]=value;
                        this.set( data );
                    };
                    save(){
                        this.set( this.get() );
                    }
                };
                
                
                
                
                /***********************************
                    The name of the storage object
                    & helper css classnames.
                */
                const config={
                    store:'panel_preferences',
                    css_logged:'rpExpLogged',
                    css_panel:'rpExpandable'
                };
                
                
                /***********************************************************************
                    create the instance of the Storage Object - create default 
                    type if it does not exist by calling `create` method.
                    If the store already exists nothing happens when calling `create()`
                */
                let oStore=new StoreFactory( config.store );
                    oStore.create();
                
                
                /*******************************
                    single delegated listener
                */
                document.addEventListener('click',e=>{
                    if( e.target.classList.contains( config.css_panel ) && e.target instanceof HTMLAnchorElement ){
                        /************************************************************************
                            check for the key ( using event to identify object & get its ID ) 
                            & then set initial value of true. This will toggle on subsequent 
                            clicks by using !value type syntax
                        */
                        let data=oStore.getkey( e.target.id );
                        oStore.setkey( e.target.id, !data );
                        
                        /******************************************************************
                            The above could, if desired, be rewritten as a one-liner
                            -> oStore.setkey( e.target.id, !oStore.getkey( e.target.id ) )
                        */
                        
                        
                        
                        /****************************************************
                            Set some sort of identifying class / attribute. 
                            -> OPEN/CLOSE a panel etc
                        */
                        e.target.classList.toggle( config.css_logged );
                    }
                });
                
                
                
                /**************************************************
                    At page load - restore the panel states by
                    adding class to each item found in storage.
                */
                document.querySelectorAll( `.${config.css_panel}` ).forEach(n=>{
                    if( oStore.getkey( n.id ) )n.classList.add( config.css_logged )
                });
                
                
            </script>
        </body>
    </html>
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search