4532066
4532066

Reputation: 2110

Toggle Show / Hide DIV bug

I have the following code which takes some simple HTML and uses some JS to show / hide it.

// So forEach can be used on 'querySelectorAll' and 'getElementsByClassName' collections
HTMLCollection.prototype.forEach = NodeList.prototype.forEach = Array.prototype.forEach;

function HideShow(e, itm_id) {
  var tbl = document.getElementById(itm_id);
  if (tbl.style.display == "") {
    e.innerHTML = "<i class='fa fa-plus' aria-hidden='true'></i>";
    tbl.style.display = "none";
  } else {
    e.innerHTML = "<i class='fa fa-minus' aria-hidden='true'></i>";
    tbl.style.display = "";
  }
}


// -----------------------------------------------------------
// NEW Code


// New toggle links
let toggles = document.getElementsByClassName('toggler');

// Attach click event
toggles.forEach(link => link.addEventListener('click', fnToggleElement))

// Event handler definition
function fnToggleElement() {
  let elements = document.querySelectorAll(`[id^="${this.dataset.selector}"]`)
  let className = 'd-none'
  elements.forEach(el => {
    let fas = el.parentElement.closest('.item,.sub-container,.menu-container').querySelectorAll('.fa')
    if (el.classList.contains(className)) {
      el.classList.remove(className)
      fas.forEach(fa => {
        fa.classList.remove('fa-plus')
        fa.classList.add('fa-minus')
      })
    } else {
      el.classList.add(className)
      fas.forEach(fa => {
        fa.classList.remove('fa-minus')
        fa.classList.add('fa-plus')
      })
    }
  })
}
body {
  background: #fff;
  margin-top: 20px;
}

h1.heading {
  font: 'Oswald';
  text-transform: uppercase;
}

td {
  background: #f1f1f1;
  border-bottom: 1px solid #ccc;
  border-right: 1px solid #ccc;
  padding: 20px;
  margin: 5px;
  border-top: 1px solid #fff;
  border-left: 1px solid #fff;
}

.wrappingmapping {
  margin: 20px 0 0 20px;
  border-radius: 85px;
  overflow: hidden;
  border: 10px solid #fff;
  box-shadow: 0 0 10px #999;
}

.menu-container {
  margin-bottom: 50px;
}

.sub-container {
  padding: 20px;
  background: repeating-linear-gradient( -45deg, #999, #999 10px, #888 10px, #888 20px);
  border-radius: 2px;
}

.heading {
  color: #000;
  background: #ccc;
  border-bottom: 1px solid #ccc;
  padding: 5px;
}

.indent {
  background: #fff;
  padding: 20px;
}

.icon {
  width: 64px;
  height: 64px;
}

.item {
  background: #fff;
  padding-left: 10px;
  background: #f1f1f1;
  border-top: 1px solid #fff;
  font-size: 30px;
}

.gallery {
  width: 100%;
  *width: 99.94877049180327%;
  margin: 0;
  padding: 0;
}

.gallery.grid li {
  margin: 2px 5px;
}

.gallery.grid li {
  margin: 2px 5px;
  display: block;
}

.gallery.grid li:hover {
  background: #ccc;
}

.gallery.grid li {
  display: inline-block;
  border-top: 1px solid #eee;
  border-right: 1px solid #ccc;
  border-bottom: 1px solid #ccc;
  border-left: 1px solid #eee;
  padding: 6px;
  position: relative;
  -moz-box-sizing: border-box;
  border-radius: 3px 3px 3px 3px;
  background: #fff;
}

.gallery a {
  display: block;
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet" />
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css">
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script>
<div class="container-fluid">

  <div>
    <span class="toggler btn btn-primary btn-lg" data-selector="parent_">Toggle</span>
  </div>

  <hr />

  <!-- first section -->

  <div id="activities" class="menu-container">
    <h1 class="heading">
      <a href="javascript:;" onclick="HideShow(this,'parent_activities')">
        <i class="fa fa-minus" aria-hidden="true"></i>
      </a> Activities
    </h1>
    <div id="parent_activities" class="sub-container">
      <ul class="gallery grid">
        <li>
          <a href="#"><img title="jack-o-lantern - 🎃" src="https://cdn.jsdelivr.net/emojione/assets/svg/1f383.svg" class="icon" role="presentation"></a>
        </li>
      </ul>
    </div>
  </div>

  <!-- second section -->

  <div id="animals-nature" class="menu-container">
    <h1 class="heading">
      <a href="javascript:;" onclick="HideShow(this,'parent_animals-nature')">
        <i class="fa fa-minus" aria-hidden="true"></i>
      </a> Animals & Nature
    </h1>
    <div id="parent_animals-nature" class="sub-container">
      <ul class="gallery grid">
        <li>
          <a href="#"><img title="monkey face - 🐵" src="https://cdn.jsdelivr.net/emojione/assets/svg/1f435.svg" class="icon" role="presentation"></a>
        </li>
      </ul>
    </div>
  </div>

</div>

The code works fine in that the blue Toggle button shows and hides the DIVs below it.

However, the problem I have is that when the DIVs are collapsed, I cannot then click on the blue plus icon to expand the divs individually. The only way to expand them again is to use the toggle button rather than to be able to use the link on the blue plus icon.

It might be it's not possible to do what I'm trying to do, but I feel like I am probably not far off, but am struggling to work out how to proceed as I am using a solution provided in my previous SO question, and not being a "real" programmer don't really understand how the JS code in the solution works, I just know there's a slight issue with it.

Upvotes: 1

Views: 256

Answers (2)

Girish Sadanandan
Girish Sadanandan

Reputation: 174

Since you already added JQuery with your web page, i wrote an JQuery solution for your Problem. Which looks bit cleaner than writing everything in JS. If you want to stick to the JS, you can rewrite the same logic to JS. The problem is that you are not handled all the cases.

$(document).ready(function() {

  // all of them are already open so set toggle accordingly
  $('.toggler').addClass('toggle-open');

  // on click
  $('.heading > a').on('click', function(event){
    // prevent href action
    event.preventDefault();

    // change hedding icon
    // If - then make + else make -
    if ( $(this).find('i').hasClass('fa-minus') ) {
      // change of - to +
      $(this).find('i').removeClass('fa-minus').addClass('fa-plus');
      // hide sub container
      $(this).parent().parent().find('.sub-container').css("display", "none");
      // give an indication of state
      $(this).parent().parent().find('.sub-container').addClass('closed');
      // if this action made all closed sub-container but toggle thinks any sub-container is open, indicate all closed state
      if ( $('.sub-container.closed').length == $('.menu-container').length ) {
        $('.toggler').removeClass('toggle-open');
      }
    }
    else if ( $(this).find('i').hasClass('fa-plus') ) {
      // change + to -
      $(this).find('i').removeClass('fa-plus').addClass('fa-minus');
      // show sub container
      $(this).parent().parent().find('.sub-container').css("display", "block");
      // give an indication of state
      $(this).parent().parent().find('.sub-container').removeClass('closed');
      // if this action made any open sub-container but toggle thinks all are off, indicate open state
      $('.toggler').addClass('toggle-open');
    }
  });

  // toggler actions
  $('.toggler').on('click', function(){

    // we use toggle-open class to check any sub-container is open or all sub-container closed
    // if toggle-open class is there which means all sub-container or any sub-container is open
    // change if it is all sub-container open or any sub-container open
    if ( $(this).hasClass('toggle-open') ) {
      // close all sub-container
      $('.heading').each(function() {
        // here each item is taken then closes it, $(this) refers the context
        $(this).find('i').removeClass('fa-minus').addClass('fa-plus');
        $(this).parent().find('.sub-container').css("display", "none");
        $(this).parent().parent().find('.sub-container').addClass('closed');
      });
      // set all closed mode
      $('.toggler').removeClass('toggle-open');
    }
    else{
      // open all
      $('.heading').each(function() {
        $(this).find('i').removeClass('fa-plus').addClass('fa-minus');
        $(this).parent().find('.sub-container').css("display", "block");
        $(this).parent().parent().find('.sub-container').removeClass('closed');
      });
      // indicate sub-container is open
      $('.toggler').addClass('toggle-open');
    }

  });

});
body {
      background: #fff;
      margin-top: 20px;
      }

      h1.heading {
      font: 'Oswald';
      text-transform: uppercase;
      }

      td {
      background: #f1f1f1;
      border-bottom: 1px solid #ccc;
      border-right: 1px solid #ccc;
      padding: 20px;
      margin: 5px;
      border-top: 1px solid #fff;
      border-left: 1px solid #fff;
      }

      .wrappingmapping {
      margin: 20px 0 0 20px;
      border-radius: 85px;
      overflow: hidden;
      border: 10px solid #fff;
      box-shadow: 0 0 10px #999;
      }

      .menu-container {
      margin-bottom: 50px;
      }

      .sub-container {
      padding: 20px;
      background: repeating-linear-gradient( -45deg, #999, #999 10px, #888 10px, #888 20px);
      border-radius: 2px;
      }

      .heading {
      color: #000;
      background: #ccc;
      border-bottom: 1px solid #ccc;
      padding: 5px;
      }

      .indent {
      background: #fff;
      padding: 20px;
      }

      .icon {
      width: 64px;
      height: 64px;
      }

      .item {
      background: #fff;
      padding-left: 10px;
      background: #f1f1f1;
      border-top: 1px solid #fff;
      font-size: 30px;
      }

      .gallery {
      width: 100%;
      *width: 99.94877049180327%;
      margin: 0;
      padding: 0;
      }

      .gallery.grid li {
      margin: 2px 5px;
      }

      .gallery.grid li {
      margin: 2px 5px;
      display: block;
      }

      .gallery.grid li:hover {
      background: #ccc;
      }

      .gallery.grid li {
      display: inline-block;
      border-top: 1px solid #eee;
      border-right: 1px solid #ccc;
      border-bottom: 1px solid #ccc;
      border-left: 1px solid #eee;
      padding: 6px;
      position: relative;
      -moz-box-sizing: border-box;
      border-radius: 3px 3px 3px 3px;
      background: #fff;
      }

      .gallery a {
      display: block;
      }
<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title></title>
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet" />
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css">
    <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script>
  </head>
  <body>
    <div class="container-fluid">

      <div>
        <span class="toggler btn btn-primary btn-lg" data-selector="parent_">Toggle</span>
      </div>

      <hr/>

      <div class="menu-container">
        <h1 class="heading">
          <a href="#"><i class="fa fa-minus" aria-hidden="true"></i></a> Activities
        </h1>
        <div class="sub-container">
          <ul class="gallery grid">
            <li>
              <a href="#"><img title="jack-o-lantern - 🎃" src="https://cdn.jsdelivr.net/emojione/assets/svg/1f383.svg" class="icon" role="presentation"></a>
            </li>
          </ul>
        </div>
      </div>

      <div class="menu-container">
        <h1 class="heading">
          <a href="#"><i class="fa fa-minus" aria-hidden="true"></i></a> Animals & Nature
        </h1>
        <div class="sub-container">
          <ul class="gallery grid">
            <li>
              <a href="#"><img title="monkey face - 🐵" src="https://cdn.jsdelivr.net/emojione/assets/svg/1f435.svg" class="icon" role="presentation"></a>
            </li>
          </ul>
        </div>
      </div>

    </div>
  </body>
</html>

I didn't change any of CSS or added class I mode little Change to HTML so it looks cleaner. Also removed IDs, since we need generalized solution that can accommodate as much as items. Otherwise you need to fix JS every time you add something or remove something.

Easy Solution for next time -> use collapse from Bootstrap. You forgot to add Bootstrap specific JS files. Refer: bootstrap collapse with multiple-targets

Upvotes: 0

Asons
Asons

Reputation: 87191

The main reason that won't work is that you change the display value inline with e.g.

 tbl.style.display = "none";`

As inline styles generally have a higher specificity, they override an external CSS rule (like the here used d-none), unless one start using !important in those classes that of course, which is not recommended as a general fix to such issue.

Instead toggle the class with your button, by doing something like this

      if (tbl.className.indexOf("d-none") > -1 ) {
        e.innerHTML = "<i class='fa fa-plus' aria-hidden='true'></i>";
        tbl.classList.remove("d-none");
      } else {
        e.innerHTML = "<i class='fa fa-minus' aria-hidden='true'></i>";
        tbl.classList.add("d-none");
      }

Stack snippet

// So forEach can be used on 'querySelectorAll' and 'getElementsByClassName' collections
		HTMLCollection.prototype.forEach = NodeList.prototype.forEach = Array.prototype.forEach;

		function HideShow(e, itm_id) {
		  var tbl = document.getElementById(itm_id);
		  if (tbl.className.indexOf("d-none") > -1 ) {
			e.innerHTML = "<i class='fa fa-plus' aria-hidden='true'></i>";
			tbl.classList.remove("d-none");
		  } else {
			e.innerHTML = "<i class='fa fa-minus' aria-hidden='true'></i>";
			tbl.classList.add("d-none");
		  }
		}


		// -----------------------------------------------------------
		// NEW Code


		// New toggle links
		let toggles = document.getElementsByClassName('toggler');

		// Attach click event
		toggles.forEach(link => link.addEventListener('click', fnToggleElement))

		// Event handler definition
		function fnToggleElement() {
		  let elements = document.querySelectorAll(`[id^="${this.dataset.selector}"]`)
		  let className = 'd-none'
		  elements.forEach(el => {
			let fas = el.parentElement.closest('.item,.sub-container,.menu-container').querySelectorAll('.fa')
			if (el.classList.contains(className)) {
			  el.classList.remove(className)
			  fas.forEach(fa => {
				fa.classList.remove('fa-plus')
				fa.classList.add('fa-minus')
			  })
			} else {
			  el.classList.add(className)
			  fas.forEach(fa => {
				fa.classList.remove('fa-minus')
				fa.classList.add('fa-plus')
			  })
			}
		  })
		}
body{
  background: #fff;
  margin-top:20px;
}

h1.heading {
  font: 'Oswald';
  text-transform: uppercase;
}

td { background: #f1f1f1; border-bottom:1px solid #ccc; border-right:1px solid #ccc; padding:20px; margin:5px; border-top:1px solid #fff; border-left:1px solid #fff; }



.wrappingmapping {
	margin:20px 0 0 20px;
	border-radius:85px;
	overflow:hidden;
	border:10px solid #fff;
	box-shadow:0 0 10px #999;
}


.menu-container {
  margin-bottom: 50px;
}

.sub-container {
  padding: 20px;
background: repeating-linear-gradient(
  -45deg,
  #999,
  #999 10px,
  #888 10px,
  #888 20px
);
  border-radius: 2px;
}

.heading {
  color: #000;
  background: #ccc;
  border-bottom: 1px solid #ccc;
  padding: 5px;
}

.indent {
  background: #fff;
  padding: 20px;
}

.icon {
  width: 64px;
  height: 64px;
}

.item {
  background: #fff;
  padding-left: 10px;
  background: #f1f1f1;
  border-top: 1px solid #fff;
  font-size: 30px;
}

.gallery {
  width: 100%;
  *width: 99.94877049180327%;
  margin: 0;
  padding: 0;
}

.gallery.grid li {
  margin: 2px 5px;
}

.gallery.grid li {
  margin: 2px 5px;
  display: block;
}

.gallery.grid li:hover {
  background: #ccc;
}

.gallery.grid li {
  display: inline-block;
  border-top: 1px solid #eee;
  border-right: 1px solid #ccc;
  border-bottom: 1px solid #ccc;
  border-left: 1px solid #eee;
  padding: 6px;
  position: relative;
  -moz-box-sizing: border-box;
  border-radius: 3px 3px 3px 3px;
  background: #fff;
}

.gallery a {
  display: block;
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet"/>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css">
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script>
	<div class="container-fluid">

		<div>
			<span class="toggler btn btn-primary btn-lg" data-selector="parent_">Toggle</span>	
		</div>

		<hr />

		<!-- first section -->

		<div id="activities" class="menu-container">
			<h1 class="heading">
				<a href="javascript:;" onclick="HideShow(this,'parent_activities')">
					<i class="fa fa-minus" aria-hidden="true"></i>
				</a> Activities 
			</h1>
			<div id="parent_activities" class="sub-container">
				<ul class="gallery grid">	
					<li><a href="#"><img title="jack-o-lantern - 🎃" src="https://cdn.jsdelivr.net/emojione/assets/svg/1f383.svg" class="icon" role="presentation"></a></li>
				</ul>
			</div>
		</div>

		<!-- second section -->

		<div id="animals-nature" class="menu-container">
			<h1 class="heading">
				<a href="javascript:;" onclick="HideShow(this,'parent_animals-nature')">
					<i class="fa fa-minus" aria-hidden="true"></i>
				</a> Animals & Nature 
			</h1>
			<div id="parent_animals-nature" class="sub-container">
				<ul class="gallery grid">	
					<li><a href="#"><img title="monkey face - 🐵" src="https://cdn.jsdelivr.net/emojione/assets/svg/1f435.svg" class="icon" role="presentation"></a></li>
				</ul>
			</div>
		</div>

	</div>

Upvotes: 1

Related Questions