Home > Back-end >  Controlling collapsible headers and scrolling on click using javascript
Controlling collapsible headers and scrolling on click using javascript

Time:09-28

Good morning,

I have a web page that I created based off a PDF, which is not web accessible and device responsive, whereas this web page will be.

Problem 1: I have used JavaScript HTML CSS to create collapsible headings to expand the contents within. It works except that I cannot collapse the same section back if I click on the same heading again.

Problem 2: As an addition, I have attempted to make it so the page scroll to the expanded section... but it's doing a funny and I'm not sure why... sometimes the page scrolls to the location, sometimes it scrolls to the top... and when clicking on the same heading as what is already open, the page scrolls somewhere else.

I have tried to move around the code for both issues, but I always get different results and not the ones I am looking for.

I have a JSFiddle here - I was wondering if someone could give me pointers on what I'm doing wrong?

/*jslint browser:true */

var coll = document.getElementsByClassName("collapsible");
var i;
var previousElement;
var colTest;

// Function to run when clicking on a section
// - Section will expand
// - Section should close if clicking on it again
// - Page should scroll to the position of the section (debateable)
function clickEvent(currIndx) {

  // Close all other collapsible elements
  var colR = document.getElementsByClassName("collapsible");
  var l;

  for (l = 0; l < colR.length; l = l   1) {

    var content2 = colR[l].nextElementSibling;

    if (content2.style.maxHeight) {

      colR[l].classList.toggle("active");

      if (content2.style.maxHeight) {
        content2.style.maxHeight = null;
      }
    }

  }

  // Expand the selected element
  var content = this.nextElementSibling;

  this.classList.toggle("active");

  if (content.style.maxHeight) {
    content.style.maxHeight = null;
  } else {
    content.style.maxHeight = content.scrollHeight   "px";
  };

  window.scrollTo({
    top: getPosition(this).y,
    left: 0,
    behavior: 'smooth'
  });
}

// Function to get the position of the element on the screen
function getPosition(el) {
  var xPos = 0;
  var yPos = 0;

  while (el) {
    if (el.tagName == "BODY") {
      // deal with browser quirks with body/window/document and page scroll
      var xScroll = el.scrollLeft || document.documentElement.scrollLeft;
      var yScroll = el.scrollTop || document.documentElement.scrollTop;

      xPos  = (el.offsetLeft - xScroll   el.clientLeft);
      yPos  = (el.offsetTop - yScroll   el.clientTop);
    } else {
      // for all other non-BODY elements
      xPos  = (el.offsetLeft - el.scrollLeft   el.clientLeft);
      yPos  = (el.offsetTop - el.scrollTop   el.clientTop);
    }

    el = el.offsetParent;
  }
  return {
    x: xPos,
    y: yPos
  };
}

// Apply the function to all Sections
for (i = 0; i < coll.length; i = i   1) {
  var indx = i;
  colTest = coll[i];
  coll[i].addEventListener("click", function() {
    clickEvent.call(this, indx);
  }, false);
}
/* stylesheet-general */

@import url('https://fonts.googleapis.com/css2?family=Open Sans&family=Roboto&display=swap');
body {
  font-family: 'Roboto', 'Open Sans', Arial, Tahoma, Sans-Serif;
  font-size: 100%;
  background-color: #089de3;
}

#outerContainer {
  width: 100%;
  /* Contains the contents within the outer container */
  overflow: auto;
}

#container {
  background-color: white;
  border: 3px solid lightgray;
}

#logo_container {
  margin-left: auto;
  margin-right: auto;
}

#logo {
  background-image: url("./images/redacted.png");
  background-repeat: no-repeat;
  background-position: center;
  background-size: contain;
  min-height: 100px;
  height: 124px;
}

#header {
  margin-left: auto;
  margin-right: auto;
}

#header h1 {
  text-align: center;
}

#innerContainer_NoBorder {
  border-collapse: collapse;
  margin-top: 10px;
  display: inline-block;
  width: 100%;
}

.divTableBody {
  width: 100%;
  display: block;
}

.divTableRowFull {
  display: block;
  width: 100%;
  margin-bottom: 20px;
  margin-bottom: 30px;
}

.divTableHeadFull {
  display: block;
  padding-left: 20px;
  background-color: rgb(236, 236, 236);
}

.divTableCellFull {
  display: block;
}

.divTableCellFull>p {
  display: block;
  padding: 0;
  margin: 0;
  margin-left: 10px;
  margin-top: 15px;
}

.divTableCellFull>ul>li {
  margin-bottom: 5px;
}

h2 {
  display: block;
  font-size: 1.17em;
  margin-block-start: 1em;
  margin-block-end: 1em;
  margin-inline-start: 0px;
  margin-inline-end: 0px;
  font-weight: bold;
}

h3 {
  display: block;
  font-size: 1em;
  margin-block-start: 1em;
  margin-block-end: 1em;
  margin-inline-start: 0px;
  margin-inline-end: 0px;
  font-weight: normal;
}


/* By default, any elements with this class will
not be displayed to the screen */

.nodisplay {
  display: none;
}


/* Sets the constant formatting for the rowShade
divs */

.rowShade {
  background-color: #efefef;
  border-top: 1px solid lightgray;
  border-bottom: 1px solid lightgray;
  border-collapse: collapse;
  display: block;
}

.divTableCell {
  position: relative;
}

.collapsibleIcon {
  display: none;
}


/* stylesheet-largescreen */


/* ========================================================== */


/* For screens wider than 768px such as Tablet devices and PC */


/* ========================================================== */

@media only screen and (min-width: 768px) {
  /* Set the Logo here to the specified height */
  #logo {
    height: 146px;
  }
  /* Set's the size and padding of the container for larger
        devices */
  #container {
    max-width: 850px;
    min-width: 500px;
    margin-left: auto;
    margin-right: auto;
    padding: 50px;
  }
  .columnContainer {
    width: 25%;
    min-height: 720px;
    display: inline-block;
    z-index: 5;
  }
  /* Make the rowShade div a bit taller for larger 
        devices */
  .rowShade {
    height: 35px;
  }
}


/* stylesheet-smallscreen.css */


/* ========================================================== 
    For screens smaller than 768px such as mobile devices      
========================================================== */

@media only screen and (max-width: 768px) {
  #container {
    /* Set the minimum width just in case... but this will
        unlikely to matter */
    min-width: 260px;
  }
  /* Set the Logo here and keep it small using the
    height attribute for Mobile Devices - no sense having a big
    logo */
  #logo {
    height: 100px;
  }
  /* Make the rowShade div thinner for smaller devices */
  .rowShade {
    height: 20px;
  }
}


/* stylesheet-animate */

.collapsible .divTableHeadFull {
  all: unset;
  display: inline-block;
  margin-left: 20px;
  top: 0px;
}

.collapsible .divTableHeadFull {
  padding-top: 0px;
}

.collapsible {
  background-color: #efefef;
  border: 2px solid lightgray;
  color: black;
  cursor: pointer;
  width: 100%;
  text-align: left;
  outline: none;
  font-size: 15px;
  border-top-left-radius: 10px;
  border-top-right-radius: 10px;
}

.active,
.collapsible:hover {
  background-color: lightgray;
}

.collapsible .collapsibleIcon {
  display: inline-block;
  background-image: url('./images/outline_arrow_forward_ios_black_24dp.png');
  background-position: center;
  background-repeat: no-repeat;
  background-size: 20px;
  width: 24px;
  height: 24px;
}

.active .collapsibleIcon {
  background-image: url('./images/outline_expand_more_black_24dp.png');
  background-position: center;
  background-repeat: no-repeat;
  background-size: 28px;
}

.content {
  padding: 0 18px;
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.2s ease-out;
  background-color: #f1f1f1;
  border-bottom-left-radius: 10px;
  border-bottom-right-radius: 10px;
  border: 1px solid lightgray;
  max-height: 0px;
}
<html lang="en">

<title>Documents</title>

<meta name="viewport" content="width=device-width, initial-scale=1">

<head>

  <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material Icons">
  <link rel="stylesheet" href="./stylesheets/stylesheet-general.css">
  <link rel="stylesheet" href="./stylesheets/stylesheet-largescreen.css">
  <link rel="stylesheet" href="./stylesheets/stylesheet-smallscreen.css">
  <link rel="stylesheet" href="./stylesheets/stylesheet-animate.css">

</head>

<body>

  <div id="outerContainer">

    <div id="container">

      <!-- Logo section -->
      <div id="logo_container">

        <!-- Logo -->
        <div role="banner" id="logo" alt="Logo"> </div>

      </div>

      <!-- Page Heading Section -->
      <div id="header">

        <!-- Page Heading -->
        <h1>[REDACTED]</h1>

      </div>

      <div style="text-align: center; font-weight: bold; font-size: 1.3em">
        [redacted]
      </div>

      <div id="innerContainer_NoBorder">

        <!-- <div class="rowShade">&nbsp;</div> -->

        <div role="main" class="divTableBody">

          <!-- Div Table Row -->
          <div class="divTableRowFull">

            <!-- Div Table Section Heading Column -->
            <button class="collapsible">
                            <div class="collapsibleIcon"> </div>
                            <div class="divTableHeadFull" lang="en">

                                <!-- Section Heading in English -->
                                <h2>[redacted]:</h2>

                            </div>
                        </button>

            <!-- Div Table Section Data Column -->
            <div class="divTableCellFull content" lang="en">

              <p>[redacted]:
                <ul class="bullets">
                  <li>[redacted]</li>
                  <li>[redacted]</li>
                  <li>[redacted]</li>
                </ul>
              </p>

            </div>

          </div>

          <!-- Div Table Row -->
          <div class="divTableRowFull">

            <!-- Div Table Section Heading Column -->
            <button class="collapsible">
                            <div class="collapsibleIcon"> </div>
                            <div class="divTableHeadFull" lang="en">

                                <!-- Section Heading in English -->
                                <h2>[redacted]</h2>

                            </div>
                        </button>

            <!-- Div Table Section Data Column -->
            <div class="divTableCellFull content" lang="en">

              <p>[redacted]
                <ul class="bullets">
                  <li>[redacted</li>
                  <li>[redacted</li>
                </ul>
              </p>

            </div>

          </div>

          <!-- Div Table Row -->
          <div class="divTableRowFull">

            <button class="collapsible">
                            <div class="collapsibleIcon"> </div>
                            <div class="divTableHeadFull" lang="en">

                                <!-- Section Heading in English -->
                                <h2>[redacted]</h2>

                            </div>
                        
                        </button>

            <!-- Div Table Section Data Column -->
            <div class="divTableCellFull content" lang="en">
              <p>[redacted]</u>
                <ul class="bullets">
                  <li>[redacted]</li>
                  <li>[redacted]</li>
                </ul>
              </p>
            </div>

          </div>

          <!-- Div Table Row -->
          <div class="divTableRowFull">

            <button class="collapsible">
                            <div class="collapsibleIcon"> </div>
                            <div class="divTableHeadFull" lang="en">

                                <!-- Section Heading in English -->
                                <h2>[redacted]</h2>

                            </div>
                        
                        </button>

            <!-- Div Table Section Data Column -->
            <div class="divTableCellFull content" lang="en">
              <p>[redacted]</u>
                <ul class="bullets">
                  <li>[redacted]</li>
                  <li>[redacted]</li>
                </ul>
              </p>
            </div>

          </div>

          <!-- Div Table Row -->
          <div class="divTableRowFull">

            <button class="collapsible">
                            <div class="collapsibleIcon"> </div>
                            <div class="divTableHeadFull" lang="en">

                                <!-- Section Heading in English -->
                                <h2>[redacted]</h2>

                            </div>
                        
                        </button>

            <!-- Div Table Section Data Column -->
            <div class="divTableCellFull content" lang="en">
              <p>[redacted]</u>
                <ul class="bullets">
                  <li>[redacted]</li>
                  <li>[redacted]</li>
                </ul>
              </p>
            </div>

          </div>

          <!-- Div Table Row -->
          <div class="divTableRowFull">

            <button class="collapsible">
                            <div class="collapsibleIcon"> </div>
                            <div class="divTableHeadFull" lang="en">

                                <!-- Section Heading in English -->
                                <h2>[redacted]</h2>

                            </div>
                        
                        </button>

            <!-- Div Table Section Data Column -->
            <div class="divTableCellFull content" lang="en">
              <p>[redacted]</u>
                <ul class="bullets">
                  <li>[redacted]</li>
                  <li>[redacted]</li>
                </ul>
              </p>
            </div>

          </div>

        </div>

        <!-- Div Table Row -->
        <div class="divTableRowFull" style="margin-top: 20px; display: block; border: 0;text-align: center;">

          <!-- Div Table Section Data Column -->
          <div class="divTableCellFull" lang="en">
            [redacted]
          </div>
        </div>

        <!-- Div Table Row -->
        <div class="divTableRowFull" style="margin-top: 20px; display: block; border: 0; text-align: center;">

          <!-- Div Table Section Data Column -->
          <div class="divTableCellFull" lang="en">
            [redacted]
            <a href="mailto:#">[redacted]</a>
          </div>

        </div>

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

</body>

</html>

Please note I am a beginner at best when it comes to JavaScript. I can understand the basics but often have to refer to web sources for documentation or other JavaScript code to try and figure out my requirements, which really is meant to be as minimal as possible.

CodePudding user response:

It's because you're toggling the active one twice when you press it. Simplest solution would be to add a check in your loop to see if you're working with the same child or not

eg:

    if (content2.style.maxHeight) {   

becomes

    if (content2 !== this.nextElementSibling && content2.style.maxHeight) {   

CodePudding user response:

You can check first if the target is already active, if true, toggle the active class at the current one, else? remove active class from all the panels and active the target.

let sections = document.querySelectorAll(".section");
    
sections.forEach(section => {
    // Get each section's button and panel
    let btn = section.querySelector("button");
    let panel = section.querySelector(".panel");
    
    // Add button event onclick 
    btn.addEventListener("click", (e) => {
        // If the target is active, toggle the class
        if(panel.classList.contains('active')) {
            panel.classList.toggle('active');
        } else {
          // Else ? remove the active class from all the panels...
          sections.forEach(element => {
              let panel = element.querySelector(".panel").classList.remove('active');
          });
          
          // Add active class to the current button and panel
          panel.classList.toggle('active');
        }
    });
});  
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

.wrapper {
    margin: 20px;
}

.section button {
    width: 100%;
    height: 40px;
}

.section .panel.active {
    max-height: 100px;
}

.section .panel {
    overflow: hidden;
    max-height: 0;
    transition: all 0.2s;
}
<div class="wrapper">
    <div class="section">
        <button>Panel 1</button>
        <div class="panel">Lorem ipsum dolor sit amet consectetur adipisicing elit. Pariatur reiciendis, quidem sapiente dolor quam quisquam aut fuga corrupti aliquam natus.
        </div>
    </div>
    <div class="section">
        <button>Panel 2</button>
        <div class="panel">Lorem ipsum dolor sit amet consectetur adipisicing elit. Pariatur reiciendis, quidem sapiente dolor quam quisquam aut fuga corrupti aliquam natus.
        </div>
    </div>
    <div class="section">
        <button>Panel 3</button>
        <div class="panel">Lorem ipsum dolor sit amet consectetur adipisicing elit. Pariatur reiciendis, quidem sapiente dolor quam quisquam aut fuga corrupti aliquam natus.
        </div>
    </div>
</div>

  • Related