Toggle menu with jQuery

Viewed 140

I have been racking my brain how I could include a toggle menu on my website, after some searching I found the below and have implemented it, which is great!

http://jsfiddle.net/hhcsz5cr/

<div>
<h1><button class="button" data-circle="travel">
<i class="fa fa-plus-square"></i>
</button> Travel</h1>
</div>
<div class="travel options">
<ul>
<li>Travel</li>
<li>Vehicles</li>
</ul>
</div>

var localStorageKey = "app_state";

// to preserve state, you first need to keep track of it
var default_state = {
    biographies: false,
    pictures: false,
    poetry: false
}

var saved_state = localStorage.getItem(localStorageKey);

// ternary operator which means if `saved_state` is true we parse it and use that value for `state`; otherwise use `default_state`
var state = saved_state ? JSON.parse(saved_state) : default_state;

$(function() {
    init();

    $('.button').on('click', function() {
        var circle = $(this).attr('data-circle');
        toggleCircle(circle, !state[circle]);
        $(this).find('i').toggleClass('fa-minus fa-plus');
    });
});


function init() {
    for(var key in state) {
        var is_displayed = state[key];
        if ( is_displayed ) {
        $(this).find('i').toggleClass('fa-minus fa-plus');
        } else {
        $(this).find('i').toggleClass('fa-plus fa-plus');
        }
        console.log(is_displayed);
        toggleCircle(key, is_displayed);
    }
}

function toggleCircle(circle, is_displayed) {
    if (is_displayed) {
        $('.'+circle).show()
        state[circle] = true;
    } else {
        $('.'+circle).hide()
        state[circle] = false;
    }
    
    localStorage.setItem(localStorageKey, JSON.stringify(state));
}

But.. if you minimize a menu then refresh the icon shows a - even though its already minimize. Is there any way I can change this?

I realise the code above is not my own and I can't find the person to credit! My jquery is terrible.

Any help would be appreicated.

Thanks

2 Answers

jsFiddle DEMO (since SO snippets do not allow localStorage from Iframe)

  • Use IDs, not classes. IDs are unique, not classes.
  • Store the entire ID as the object property i.e: "#pictures": false,
  • Store the entire selector inside data-* i.e: data-toggle="#biographies"
  • Use "is-*" classes as state CSS helpers: "is-hidden", "is-expanded"
  • You don't have to use .fa classes, just use CSS and font-family
  • Make use of Object.assign() to override your default values with the ones in Local Storage (if any).
  • Loop your object key value pairs using Object.entries() when initing your menu states.
// Override defaults with localStorage
const state = Object.assign({
  "#biographies": false,  // Feel free to change this default boolean
  "#pictures": false,
  "#poetry": false
}, JSON.parse(localStorage.state || "{}"));

const toggle = (k, v) => {
  $(k).toggleClass('is-hidden', !v);
  $(`[data-toggle="${k}"]`).toggleClass('is-expanded', v);
};

// On init
Object.entries(state).forEach(([k, v]) => toggle(k, v));

// On click
$("[data-toggle]").on("click", function() {
  const id = this.dataset.toggle; // Get ID i.e: "#pictures"
  state[id] = !state[id]; // Flip boolean
  toggle(id, state[id]); // Trigger UI changes
  localStorage.state = JSON.stringify(state); // Store into LS
});
.is-hidden {
  display: none;
}

[data-toggle] i:before{
  font-family: "FontAwesome";
  font-style: normal;
  content: "\f067"; /* Plus */
}

[data-toggle].is-expanded i:before{
  content: "\f068"; /* Minus */
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.3.0/css/font-awesome.min.css">

<div id="biographies" class="is-hidden">Biography</div>
<div id="pictures" class="is-hidden">Pictures</div>
<div id="poetry" class="is-hidden">Poetry</div>

<button type="button" class="button" data-toggle="#biographies">
  <i></i> biographies
</button>

<button type="button" class="button" data-toggle="#pictures">
  <i></i> pictures
</button>

<button type="button" class="button" data-toggle="#poetry">
  <i></i> poetry
</button>

<script src="https://code.jquery.com/jquery-3.1.0.js"></script>

Please try this..

$('.button').click(function(){
  var whichbtn = $(this).attr('data-circle');
  if($("."+whichbtn).hasClass("hidden")){
    $(this).children("i").removeClass("fa-plus").addClass("fa-minus");
  }else{
    $(this).children("i").addClass("fa-plus").removeClass("fa-minus");
  }
  $("."+whichbtn).toggleClass("hidden");
});
.hidden{display:none;}
.button{
  background:#00cc00;
  padding:10px 20px;
  margin-right:10px;
  border:none;
  color:white;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.3.0/css/font-awesome.min.css" rel="stylesheet"/>

<div class="m-3">
  <div class="biographies hidden mb-2 font-weight-bold">Biography</div>
  <div class="pictures hidden mb-2 font-weight-bold">Pictures</div>
  <div class="poetry hidden mb-2 font-weight-bold">Poetry</div>

  <button class="button" data-circle="biographies">
      <i class="fa fa-plus"></i> biographies
  </button>

  <button class="button" data-circle="pictures">
      <i class="fa fa-plus"></i> pictures
  </button>

  <button class="button" data-circle="poetry">
      <i class="fa fa-plus"></i> poetry
  </button>
</div>

I add js for click event of button and get the attribute of data-circle of it's own for find which button clicked. And fa fa-plus icon changed to fa fa-minus also. Thats only. toggleClass is used for toggle class when user click the button. First click add class hidden then second click remove class hidden.For more clarifications comment me.

Related