1. Advertising
    y u no do it?

    Advertising (learn more)

    Advertise virtually anything here, with CPM banner ads, CPM email ads and CPC contextual links. You can target relevant areas of the site and show ads based on geographical location of the user if you wish.

    Starts at just $1 per CPM or $0.10 per CPC.

Save Tab State - HTML5 localStorage

Discussion in 'JavaScript' started by energylevels, May 3, 2022.

  1. #1
    Can anyone help me out with saving the last tab state in the event someone leaves the page and returns ... thanks in advance

                        <div class="container">
                            <div class="bar blue">
                                    <button class="bar-item button tablink red" onclick="openTab(event,'Summary')">Summary</button>
                                    <button class="bar-item button tablink" onclick="openTab(event,'Finance')">Finance</button>
                                    <button class="bar-item button tablink" onclick="openTab(event,'Enquire')">Enquire</button>
                                <button class="bar-item button tablink" onclick="openTab(event,'Save')">Save</button>
                            </div>
                            <div id="Summary" class="container border vehTab">
    SUMMARY IMNFO HERE
                            </div>
                            <div id="Finance" class="container border vehTab" style="display:none">
    FINANCE INFO HERE
                            </div>
                            <div id="Enquire" class="container border vehTab" style="display:none">
    ENQUIRY CONTENT HERE                       
                                </div>
                                <div id="Save" class="container border vehTab" style="display:none">                                           
                                   
    SAVE DETAILS HERE
                            </div>
                </div>
    HTML:
    JAVASCRIPT:
    function openTab(evt, tabName) {
        var i, x, tablinks;
        x = document.getElementsByClassName("vehTab");
        for (i = 0; i < x.length; i++) {
            x[i].style.display = "none";
        }
        tablinks = document.getElementsByClassName("tablink");
        for (i = 0; i < x.length; i++) {
            tablinks[i].className = tablinks[i].className.replace("red", "");
        }
        document.getElementById(tabName).style.display = "block";
        evt.currentTarget.className += " red";
    }
    Code (JavaScript):
     
    energylevels, May 3, 2022 IP
  2. sarahk

    sarahk iTamer Staff

    Messages:
    28,494
    Likes Received:
    4,457
    Best Answers:
    123
    Trophy Points:
    665
    #2
    I set up a demo at https://codepen.io/itamer/pen/jOZPWMb

    with this code (I changed a few things)... It saves the value and then retrieves it as a demo, normally the retrieve would be somewhere else.

    <div class="container">
      <div class="bar blue">
        <button class="bar-item button tablink red" data-label='Summary'>Summary</button>
        <button class="bar-item button tablink" data-label='Finance'>Finance</button>
        <button class="bar-item button tablink" data-label='Enquire'>Enquire</button>
        <button class="bar-item button tablink" data-label='Save'>Save</button>
      </div>
      <div id="Summary" class="container border vehTab">
        SUMMARY INFO HERE
      </div>
      <div id="Finance" class="container border vehTab" style="display:none">
        FINANCE INFO HERE
      </div>
      <div id="Enquire" class="container border vehTab" style="display:none">
        ENQUIRY CONTENT HERE
      </div>
      <div id="Save" class="container border vehTab" style="display:none">
    
        SAVE DETAILS HERE
      </div>
    </div>
    <div>The saved tab is: <span id='saved' class='red'></saved>
    HTML:
    const allTabs = document.querySelectorAll(".tablink");
    console.log(allTabs, allTabs.length);
    
    for (var i = 0; i < allTabs.length; i++) {
      allTabs[i].addEventListener("click", openTab);
    }
    
    function openTab() {
    const tabName = this.dataset.label;
    
    for (let i = 0; i < allTabs.length; i++) {
        if (allTabs[i].dataset.label === tabName) {
          allTabs[i].className += " red";
          document.getElementById(tabName).style.display = "block";
        
        } else {
          allTabs[i].className = allTabs[i].className.replace("red", "");
          let label = allTabs[i].dataset.label;
          let infoBlock = document.getElementById(label);
          infoBlock.style.display = "none";
        }
      }
    
      // can we access localstorage? if yes, save the value
      if (typeof window !== "undefined") {
        window.localStorage.setItem("activeTab", tabName);
      }
    
      // demo on retrieving the value back again.
      let savedDemoField = document.getElementById("saved");
      savedDemoField.innerHTML = getActiveTab();
    }
    
    function getActiveTab() {
      if (typeof window === "undefined") {
        return "Summary";
      }
      try {
        // Get from local storage by key
        return window.localStorage.getItem("activeTab");
        // Parse stored json or if none return initialValue
      } catch (error) {
        console.log(error);
        return "Summary";
      }
    }
    
    Code (JavaScript):
     
    sarahk, May 3, 2022 IP
  3. energylevels

    energylevels Member

    Messages:
    9
    Likes Received:
    1
    Best Answers:
    0
    Trophy Points:
    38
    #3
    I don't think that works - it seems to lose the value on page refresh
     
    energylevels, May 4, 2022 IP
  4. sarahk

    sarahk iTamer Staff

    Messages:
    28,494
    Likes Received:
    4,457
    Best Answers:
    123
    Trophy Points:
    665
    #4
    That's a demo of storage and retrieval. When the page opens you need to do the retrieval bit. when they click you do the storage bit.
     
    sarahk, May 4, 2022 IP
  5. deathshadow

    deathshadow Acclaimed Member

    Messages:
    9,732
    Likes Received:
    1,998
    Best Answers:
    253
    Trophy Points:
    515
    #5
    Stunning example of how NOT to do... any of this.

    1) let CSS do your heavy lifting. That means don't use buttons and scripting only functionality for your tabs. LABEL pointing at INPUT type="radio" using the adjacent sibling selector would work wonders.

    2) NEVER piss "onevent" attributes into the markup, especially if they're calling functions as that means you're polluting the global namespace. Concepts like "separation of concerns" exists for a reason people! 99% of the time you see something like onclick="" in the HTML, you're looking at ignorance, incompetence, and ineptitude by people unqualified to write client-side code.

    Much the same can be said of aria-bloat BS like data-label... Remember ARIA roles mostly mean that you're using the wrong markup. You know what's better than going full re-re halfwit pissing more code into the page so you can use the wrong HTML? Using the RIGHT HTML, that's what!

    And of course don't even get me STARTED about the mental-huffing migetry of presentational classes such as "red" or "blue", much less going full gungan with class="button" on a freaking button tag.

    Yewza wents full Gungan. Meesa says yewsa nevah goes FULL Gungan.

    So first, let's clean up the markup and set it up for scriptless tabs.

    
    <div class="tabbedSections">
    
    	<input type="radio" name="tabSection" id="tab_summary" checked hidden>
    	<input type="radio" name="tabSection" id="tab_finance" hidden>
    	<input type="radio" name="tabSection" id="tab_enquire" hidden>
    	<input type="radio" name="tabSection" id="tab_save" hidden>
    	
    	<ul hidden>
    		<li><label for="tab_summary">Summary</lable></ll>
    		<li><label for="tab_finance">Finance</lable></ll>
    		<li><label for="tab_enquire">Enquire</lable></ll>
    		<li><label for="tab_save">Save</lable></ll>
    	</ul>
    	
    	<section>
    		SUMMARY INFO HERE
    	</section>
    	
    	<section>
    		FINANCE INFO HERE
    	</section>
    	
    	<section>
    		ENQUIRY CONTENT HERE
    	</section>
    	
    	<section>
    		SAVE DETAILS HERE
    	</section>
    	
    <!-- .tabbedSections --></div>
    
    Code (markup):
    The use of "hidden" is so that we do not negatively impact screen readers, likewise we do NOT hide the individual sections by default with display:none or inlined style or any other BS since we don't want non-visual ua's (screen readers, braille readers, search engines) ignoring your content! Basically we don't want such UA's ignoring the SECTION, but we do want them ignoring the input and tab menu since those mean exactly two things to them... and Jack left town.

    Remember the basic rule, write your base HTML as if CSS, default appearance of tags, and your final desired appearance does not even exist FIRST. Then when you add media specific elements and behaviors, filter them out with hidden or via other techniques. That way you're not pissing on accessibility.

    Then implement tabs in CSS. I set it up to support up to nine tabs. You need more than nine tabs on a website you might be doing something wrong.

    
    
    .tabbedSections > ul {
    	display:flex;
    	list-style:none;
    	padding:0;
    	margin:0 0 -0.0625rem;
    	border-left:0.0625rem solid #999;
    }
    
    .tabbedSections > ul > li {
    	background:#CCC;
    	border:0.0625rem solid #999;
    	border-left:0;
    }
    
    .tabbedSections > ul > li:first-child {
    	border-radius:0.5rem 0 0;
    }
    
    .tabbedSections > ul > li:last-child {
    	border-radius:0 0.5rem 0 0;
    }
    
    .tabbedSections > ul label {
    	cursor:pointer;
    	display:block;
    	padding:0.25rem 0.5rem;
    }
    
    .tabbedSections > * {
    	/*
    		Some older browsers won't let us change the input when "hidden",
    		and somme non-visual UA's wond read display:none text
    		so hide them off-screen instead.
    	*/
    	display:block;
    	position:fixed;
    	bottom:-100vh;
    	left:-100vh;
    }
    
    .tabbedSections > section {
    	padding:1rem;
    	background:#EEE;
    	border:0.0625rem solid #999;
    	 border-radius:0 0 0.5rem 0.5rem;
    }
    
    .tabbedSections > ul,
    .tabbedSections > input:nth-of-type(1):checked ~ section:nth-of-type(1),
    .tabbedSections > input:nth-of-type(2):checked ~ section:nth-of-type(2),
    .tabbedSections > input:nth-of-type(3):checked ~ section:nth-of-type(3),
    .tabbedSections > input:nth-of-type(4):checked ~ section:nth-of-type(4),
    .tabbedSections > input:nth-of-type(5):checked ~ section:nth-of-type(5),
    .tabbedSections > input:nth-of-type(6):checked ~ section:nth-of-type(6),
    .tabbedSections > input:nth-of-type(7):checked ~ section:nth-of-type(7),
    .tabbedSections > input:nth-of-type(8):checked ~ section:nth-of-type(8),
    .tabbedSections > input:nth-of-type(9):checked ~ section:nth-of-type(9) {
    	position:static; /* ignore positioning */
    }
    
    .tabbedSections > input:nth-of-type(1):checked ~ ul > li:nth-of-type(1),
    .tabbedSections > input:nth-of-type(2):checked ~ ul > li:nth-of-type(2),
    .tabbedSections > input:nth-of-type(3):checked ~ ul > li:nth-of-type(3),
    .tabbedSections > input:nth-of-type(4):checked ~ ul > li:nth-of-type(4),
    .tabbedSections > input:nth-of-type(5):checked ~ ul > li:nth-of-type(5),
    .tabbedSections > input:nth-of-type(6):checked ~ ul > li:nth-of-type(6),
    .tabbedSections > input:nth-of-type(7):checked ~ ul > li:nth-of-type(7),
    .tabbedSections > input:nth-of-type(8):checked ~ ul > li:nth-of-type(8),
    .tabbedSections > input:nth-of-type(9):checked ~ ul > li:nth-of-type(9) {
    	background:#EEE;
    	border-bottom-color:#EEE;
    }
    
    Code (markup):
    Now that we have working tabs without JS, we can enhance them with scripting to remember the last set one.

    
    {
      const tabChangeEvent = (event) => {
        localStorage.setItem(event.currentTarget.name, event.currentTarget.id);
      };
      for (let radio of document.querySelectorAll(".tabbedSections > input")) {
        let storageId = localStorage.getItem(radio.name);
        if (storageId) radio.checked = storageId === radio.id;
        radio.addEventListener("change", tabChangeEvent);
      }
    }
    Code (markup):
    The outer {} isolates scope so we're not pissing on the global namespace. We set up the function handler first since it's applied multiple times. Then we loop through all our radio input grabbed via querySelectorAll. IF the name of the radio button exists in localstorage we set the radio.checked to if the id stored in LS matches the one on the input. Then we just hook in our tabChangeEvent PROPERLY using addEventListener.

    Easy-peasy, lemon squeezy.

    Codepen live of this here:

    https://codepen.io/jason-knight/pen/YzeXRQV

    Hope that helps.

    Also this is written to allow multiple tab areas on a page. Just change the name on all the radio INPUT and the ID's for the for/id associations, and you're good to go.
     
    Last edited: May 4, 2022
    deathshadow, May 4, 2022 IP
    sarahk likes this.
  6. sarahk

    sarahk iTamer Staff

    Messages:
    28,494
    Likes Received:
    4,457
    Best Answers:
    123
    Trophy Points:
    665
    #6
    and there I was feeling bad for meddling as much as I did!

    @energylevels - take heed of all that - it's like a masterclass for the specific problem you have!
     
    sarahk, May 4, 2022 IP