Why is persisting data not working as it should?

2019-09-16 12:54发布

问题:

I'm working with three tabs called 'Monday', 'Tuesday' and 'Favorites'. I have a toggle icon which is an empty heart at start 'favorite i'. If I'm in Monday and click on the icon, the empty heart turns to be filled out and its parent is cloned and added to the '#fav' tab. When this happens the clone is saved to local storage. So if people refresh the page, they can still see their preferences.

When the heart is clicked in one of those cloned divs that specific div is removed from '#fav' and is also removed from the array.

Everything works well, except when I refresh the browser and local storage is detected.

So, in this case if I'm in Monday and click on a filled heart it doesn't remove the clone from #fav and still adds a new clone to #fav. Also, if I'm in #fav tab, when clicking in one of the hearts, it should erase the index from the array, but in fact, it erases the full array.

How to overcome this issue? Many thanks.

HTML:

<section id="speakers-programme">
  <div class="container">
    <div class="tabs_main">

      <div class="col-md-5"><a data-target="#mon" class="btn active" data-toggle="tab">Monday</a></div>
      <div class="col-md-5"><a data-target="#tue" class="btn active" data-toggle="tab">Tuesday</a></div>
      <div class="col-md-2"><a data-target="#fav" class="btn active" data-toggle="tab"><i class="fa fa-heart" aria-hidden="true"></i></a></div>

    </div>

    <div class="tab-content">
      <div class="tab-pane active" id="mon">
        <br>
        <div class="spaces">
          <div class="box-container">
            <div class="box not-selected" id="box1">
              <span>1</span>
              <a href="#" class="favorite"><i class="fa fa-heart-o" aria-hidden="true"></i></a>
            </div>
          </div>
          <div class="box-container">
            <div class="box not-selected" id="box2">
              <span>2</span>
              <a href="#" class="favorite"><i class="fa fa-heart-o" aria-hidden="true"></i></a>
            </div>
          </div>
        </div>
      </div>

      <div class="tab-pane active" id="tue">
        <br>
        <div class="spaces">
        </div>
      </div>

      <div class="tab-pane active" id="fav">
        <br>
        <div class="spaces">
        </div>
      </div>

    </div>
  </div>
</section>

JS

console.clear();
//localStorage.setItem('sessions', "");

var tempArray = [];

// Clones
$('div.tab-pane').on('click', '.favorite', function(e) {
  e.preventDefault();

  // Elements we play with... Having significative variable names.
  var heartLink = $(this);
  var box = heartLink.parent('.box');
  var container = box.parent('.box-container');
  var favoriteTab = $("#fav .spaces");

  // I don't know what is the use for those 3 lines below.
  var idFind = box.attr("id");
  var idComplete = ('#' + idFind);
  console.log(idComplete);

  //TOGGLE FONT AWESOME ON CLICK
  heartLink.find('i').toggleClass('fa-heart fa-heart-o'); // .selected or not, you need those 2 classes to toggle.
  box.toggleClass("selected not-selected"); // Toggle selected and not-selected classes

  // Clone div
  var boxContent = container.clone(true, true);

  // Change the id
  var thisID = boxContent.attr("id")+"_cloned";
  boxContent.attr("id", thisID);

  // Get the html to be saved in localstorage
  var get = boxContent.wrap('<p>').parent().html();
  get = get.replace(/\r?\n/g, "").replace(/>\s*</g, "><"); // remove line feeds and spaces
  console.log(get);
  boxContent.unwrap();

  // Decide to add or remove
  if(box.hasClass("selected")){
    console.log("Add to array")
    tempArray.push(get);

    // Add to favorites tab
    favoriteTab.append(boxContent);

  }else{
    console.log("Remove from array");
    var index = tempArray.indexOf(get);
    tempArray.splice(index);

    // Remove from favorite tab
    favoriteTab.find("#"+thisID).remove();
  }

  // Save array
  localStorage.setItem('sessions', tempArray.join(""));
  console.log(tempArray);

  // save this current toggle state
  localStorage.setItem(box.attr("id"), $(this).find("i").attr("class"));
  console.log($(this).find("i").attr("class"));

});

// Append item if localstorage is detected
if (localStorage["sessions"]) {
  console.log("storage exist");

  // Load 
  $(".box").each(function(){
    console.log( $(this).attr("id") );
    console.log( localStorage.getItem($(this).attr("id")) );

    if(localStorage.getItem($(this).attr("id")) != null){
      $(this).find("i").removeClass().addClass( localStorage.getItem($(this).attr("id")) );
    }
  });

  $("#fav .spaces").append(localStorage["sessions"]);
  console.log( localStorage["sessions"] );
}

Fiddle: https://codepen.io/Bes7weB/pen/bobjdv?editors=1011

回答1:

I twisted your code in a way that deserves explanations.

First, you finally don't need to save the HTML of your favorited elements. You just need the heart icon states, which you already do. I added a counter, just to know how many favorited there is in storage.

Now, on page load... If there is more than zero favorites in storage, Apply the icon states by loading their classes from storage. You already had this part right. THEN cycle throught all hearts to target the filled ones and clone them in the favorite tab. I made a "named function" to do this.

On icon click now... Clicking on a cloned element or on an original element are two different situations.

On an original element, you want to toggle its classes and clone it to the favorite tab. So here, just do the togglings and for the favorite tab, just call the previous named function to clone them all!

On a cloned element, you want to remove it from favorites and toggle the original element classes. See the code to get this twist I made! I redefined some variables in this case.

Notice there no more tempArray in use.
;)

var favoriteTab = $("#fav .spaces");

// Named function to load the favorites tab
function loadFav(){
  // First, clear old favorites.
  favoriteTab.empty();

  // Look for filled hearts
  var favCount = 0;
  $(".tab-content").find("i.fa-heart").each(function(){
    // Count them
    favCount++;
    // Clone its box
    var favClone = $(this).closest(".box").clone();
    // Change the id
    favClone.attr("id", favClone.attr("id")+"_clone");
    // Append to favorites
    favoriteTab.append(favClone);
  });

  console.log("favCount: "+favCount);
  localStorage.setItem("favAmount", favCount);
}

// Click handler
$('div.tab-pane').on('click', '.favorite', function(e) {
  e.preventDefault();

  // Elements we play with... Having significative variable names.
  var heartLink = $(this);
  var box = heartLink.parent('.box');
  var thisID = box.attr("id");
  var container = box.parent('.box-container');

  if(thisID.split("_")[1] == "clone"){
    console.log("You clicked a clone!");
    // Remove that clone
    box.remove();
    // Use the original element for the rest of the function.
    heartLink = $("#"+thisID.split("_")[0]).find("a.favorite");
    box = heartLink.parent('.box');
    thisID = box.attr("id");
  }

  //TOGGLE FONT AWESOME ON CLICK
  heartLink.find('i').toggleClass('fa-heart fa-heart-o'); // .selected or not, you need those 2 classes to toggle.
  box.toggleClass("selected not-selected"); // Toggle selected and not-selected classes

  // Clone div
  loadFav();

  // Save this current toggle state
  localStorage.setItem(box.attr("id"), heartLink.find("i").attr("class"));
  console.log(heartLink.find("i").attr("class"));

});




// ON PAGE LOAD
// Append item if localstorage is detected
if (localStorage["favAmount"]>0) {
  console.log("storage exist");

  // Load heart's element states
  $(".box").each(function(){
    console.log( $(this).attr("id") );
    console.log( localStorage.getItem($(this).attr("id")) );

    if(localStorage.getItem($(this).attr("id")) != null){
      $(this).find("i").removeClass().addClass( localStorage.getItem($(this).attr("id")) );
    }
  });

  // Load favorites
  loadFav();

}else{
  console.log("no storage");
}

CodePen v6