Jump to content
  • Javascript Accordion for Spotfire


    This accordion works on a text area regardless if the html sanitization is on. It does not require any JQuery code either. Just plain vanilla javascript. The contents of the the titles or panels can be any spotfire element and the tags can be any tag as long as they are not nested

    Introduction

    This accordion works on a text area regardless if the html sanitization is on. It does not require any jQuery code either. Just plain vanilla JavaScript. The contents of the the titles or panels can be any Spotfire element and the tags can be any tag as long as they are not nested. 

    converted-file.gif.3cb347a4fd37a0dac1860625743ea255.gif

    html 

    This code  is a bare bones example.

    <div id='myAccordion1'>
       <b>PANEL 1 </b>   
       <div>PANEL 1 CONTENTS</div>
       <b>PANEL 2</b>   
       <div>PANEL 2 CONTENTS</div>
       <b>PANEL 3 </b>    
       <div>PANEL 3 CONTENTS</div>
    </div>

    JavaScript 

    This code works for any of the examples shown on the below animation.

    (function () { //script parameters 
       const id = "myAccordion1"
       const w = "500px"
       const multiselect = true;
       var el = document.getElementById(id);
       el.className = 'container'
       //var tmp = [...el.cloneNode(true).children]; 
       //el.innerHTML="" 
       tmp = [...el.children] tmp.forEach((e, i) => {
          let p = document.createElement('p');
          p.className = "accordion_item"
          if (i % 2 == 0) { //titles      
             dt = document.createElement('dt') dt.className = "accordion__heading"
             btn = document.createElement("button") btn.className = "accordion__trigger"
             btn.appendChild(e) dt.appendChild(btn) el.appendChild(dt)
          } else { //contents
             e.className = "accordion__panel"
             p.appendChild(e) el.appendChild(e)
          }
       })
    
       //accordion look and feel 
       css = `<style> dt.accordion__heading {
              font-size: 1.125rem;
              font-weight: normal;
              line-height: 1;
              margin: 0;
              padding: 0;
          }
    
          .accordion__item {
              margin: 0 !important;
          }
    
          .container {
              max-width: ${w};
              Xmargin: 0 auto;
              background: transparent;
          }
    
          .accordion {
              width: 100%;
              background: #fff;
              border: 1px solid RGB(45, 132, 219);
              text-align: left;
          }
    
          .accordion__trigger {
              -webkit-appearance: none;
              -moz-appearance: none;
              appearance: none;
              font-size: inherit;
              /*   text-transform: uppercase; */
              letter-spacing: 2px;
              padding: 1rem 1.5rem;
              background: RGB(62, 142, 222);
              color: white;
              cursor: pointer;
              transition: 0.3s ease;
              border: 0 none;
              border-bottom: 1px solid RGB(45, 132, 219);
              width: 100%;
              text-align: left;
              margin: 0;
              position: relative;
          }
    
          .accordion__trigger::after {
              content: "";
              position: absolute;
              right: 20px;
              top: calc(50% - 5px);
              width: 0;
              height: 0;
              border-left: 10px solid transparent;
              border-right: 10px solid transparent;
              border-top: 10px solid #fff;
              /*arrow*/
              transform: rotate(0deg);
              transform-origin: center;
              transition: transform 0.5s;
          }
    
          .accordion__trigger[aria-expanded="true"]::after {
              transform: rotate(-180deg);
          }
    
          .accordion__trigger:hover,
          .accordion__trigger:focus {
              background: RGB(35, 122, 210);
          }
    
          .accordion__panel {
              overflow: hidden;
              padding: 0 1.5rem 0 1.5rem;
              line-height: 1.6;
              font-size: 1rem;
              font-weight: 500;
              max-height: 0;
              border-right: 1px solid RGB(35, 122, 210);
              border-left: 1px solid RGB(35, 122, 210);
              visibility: hidden;
              transition: visibility 0.5s, padding 0.5s, max-height 0.5s;
          }
    
          .accordion__panel--open {
              visibility: visible;
              border-bottom: 1px solid RGB(35, 122, 210); } </style>`
       document.getElementById(id).insertAdjacentHTML('beforeend', css)
    
       //return 
    
       //accordion stuff
       const headings = el.querySelectorAll(".accordion__heading");
       const triggers = [];
       const accordionContents = document.querySelectorAll(".accordion__panel");
       const copyOpenClass = "accordion__panel--open";
       headings.forEach((h, i) => {
          let btn = h.querySelector("button");
          triggers.push(btn);
          let target = h.nextElementSibling;
          btn.onclick = () => {
             let expanded = btn.getAttribute("aria-expanded") === "true";
             if (expanded) {
                closeItem(target, btn);
             } else {
                openItem(target, btn);
             }
          };
       });
    
       function closeAllExpandedItems() {
          const expandedTriggers = triggers.filter((t) => t.getAttribute("aria-expanded") === "true");
          const expandedCopy = Array.from(accordionContents).filter((c) => c.classList.value.includes(copyOpenClass));
          expandedTriggers.forEach((trigger) => {
             trigger.setAttribute("aria-expanded", false);
          });
          expandedCopy.forEach((copy) => {
             copy.classList.remove(copyOpenClass);
             copy.style.maxHeight = 0;
             copy.style.padding = "0 1.5rem 0 1.5rem";
          });
       }
    
       function closeItem(target, btn) {
          if (!multiselect) {
             closeAllExpandedItems();
          } else {
             btn.setAttribute("aria-expanded", false);
             target.classList.remove(copyOpenClass);
             target.style.maxHeight = 0;
             target.style.padding = "0 1.5rem 0 1.5rem";
          }
       }
    
       function openItem(target, btn) {
          if (!multiselect) {
             closeAllExpandedItems();
          }
          btn.setAttribute("aria-expanded", true);
          target.classList.add(copyOpenClass);
          target.style.maxHeight = target.scrollHeight + "px";
          target.style.padding = "1rem 1.5rem 2rem 1.5rem";
       }
    
       //save function stores the state of each panel if it is open or closed [true,false,true] for example
       function saveState() {
          items = triggers.map(e => e.getAttribute("aria-expanded") == "true");
          localStorage.setItem(id + "_panelState", JSON.stringify(items));
       }
       //load function gets the stored state of the panel
       function restoreState() {
          //loads the saved state    
          accordionPanelState = JSON.parse(localStorage.getItem(id + "_panelState"));
          //restore the state of each panel    
          triggers.forEach((e, i) => {
             accordionPanelState[i] && e.click()
          })
       }
       //save the state every time the panel opens or closes 
       triggers.forEach(e => e.addEventListener("click", saveState));
       //load the last saved state of the panel
       try {
          restoreState();
       } catch (err) {
          console.log(err)
       }
    })();
     

     

    Instructions

    Create a text area, edit as HTML and paste the html code. Insert a JavaScript element and create a new script by copying the JavaScript code. 

    Multiple accordions on the same page

     

    converted-file.gif.2870bddeaef74a03b928e064ca3eaccb.gif

     

     

    If you need to put more than one accordion on the same text area, then comment or delete the //script parameters section and pass the id, w and multiselect variables as script parameters. By using parameters, you can reuse the code and change these variables easily.

    converted-file.png.8b00a3cbe459ce1c64d01d01bd98c789.png

    The Multi Select example has more rows and the id changes to myAccordion2  and the multiselect variable was set to true while the other accordions have a different id such as myAccordion1  and myAccordion3.   

    Customizing the accordion contents

    The Spotfire Controls example illustrates how to include Spotfire Controls such as Action Controls, Filters, Property Controls or Dynamic Items. You can also add any other html code such as images, tables, input elements, videos, etc.  To include these elements, replace the text at the header or accordion panel. In this case, I replaced PANEL 1 and PANEL 1 CONTENTS from the below html code with a variety of Spotfire controls

    Changing the display colors and fonts

    All the accordion look and feel is located in the accordion look and feel section. You can either change the colors, font, borders, spacing accordingly.

    //accordion look and feel 
    css = `<style>
      dt.accordion__heading {
      font-size: 1.125rem;
      font-weight: normal;
      line-height: 1;
      : 
      :
      </style>`
     

    A better approach is to override the default colors by adding an accordion theme script. Please send me your themes  so I can publish them here if you happen to create some cool ones. Here are a couple of accordion theme examples:

    accordion_green_neon.css

    converted-file.gif.0797e712ac9bf4c0c72f17c5c25f5b1d.gif

    //script parameters 
    id = "myAccordion1"
    //colors 
    let borderColor = "green" //or use an hex color code #aabbcc 
    let textColor = "gray"
    let bgColor = "lightgreen"
    css = `<style>
    .accordion__trigger[aria-expanded="true"]::after {
    	transform: unset;
    	content: "?";
    }
    
    .accordion__trigger:hover,
    .accordion__trigger:focus {
    	background: ${bgColor};
    }
    
    .accordion__trigger::after {
    	content: "?";
    	border: none;
    	right: 10px;
    	font-size: 12px;
    	border-right: 10px solid transparent;
    	/*triangle color*/
    	transition: none;
    }
    
    /*border colors*/
    .accordion__trigger {
    	padding: 0.3rem;
    	font-size: 13px;
    	background: ${bgColor};
    	color: ${textColor};
    	border-bottom: 1px solid ${borderColor};
    }
    
    .accordion__panel--open {
    	border-bottom: 1px solid ${borderColor};
    }
    
    .accordion__panel {
    	border-right: 1px solid ${borderColor};
    	border-left: 1px solid ${borderColor};
    }
    
    </style>`
    
    document.getElementById(id).insertAdjacentHTML('beforeend', css)
     

    accordion_midnight_owl.css

    converted-file.gif.d992056e2809b798c933b1b99937b648.gif

    //scrip parameters
    id="myAccordion1"
    
    //colors
    let borderColor = "gray" //or use an hex color code #aabbcc
    let textColor = "white"
    let bgColor = "#2c3e50"
    
    css=`<style>
    
    #${id} .accordion__trigger[aria-expanded="true"]{
        border-radius: 8px 8px 0px 0px;
        box-shadow: none;
    }
    
    #${id} .accordion__trigger[aria-expanded="true"]::after {
        transform: scale(1, -1);
        margin-top: 12px;
    	content:"-";
    }
    	
    	
    #${id} .accordion__trigger:hover, .accordion__trigger:focus {
       background: ${bgColor};
    }
    
    #${id} .accordion__trigger::after{
      content:"+";
      border:none;
      right: 10px;
      font-size: 12px;
      border-right: 10px solid transparent; /*triangle color*/
      transition:none;
    }
    
    /*border colors*/
    
    #${id} .accordion__trigger{
      padding: 0.3rem;
      font-size: x-small;
      background: ${bgColor};
      color: ${textColor};
      border-bottom: 1px solid ${borderColor};
      margin-top: 12px;
      border-radius: 8px;
      overflow: hidden;
      box-shadow: 0 4px 4px -2px rgba(0,0,0,0.5);
      text-align:center;
      
    }
    
    
    #${id} .accordion__panel--open {
      border-bottom: 1px solid  ${borderColor};
      border-radius: 0 0 8px 8px;
      overflow: hidden;
      box-shadow: 0 4px 4px -2px rgba(0,0,0,0.5);
    }
    
    #${id} .accordion__panel{
        border-right: 1px solid ${borderColor};
    	border-left: 1px solid ${borderColor};
    	font-size: inherit;
    }
    </style>`
    
    document.getElementById(id).insertAdjacentHTML('beforeend',css)
     

    To use them, create another script and copy paste the code. Make sure you place them after the accordion.js file. You can combine them as they override different parts of the CSS styles.

    converted-file.png.63d4a3d5c3871e22346c77981b2c2142.png

     

    Back to the JavaScript Component Hub for Spotfire


    User Feedback

    Recommended Comments

    There are no comments to display.


×
×
  • Create New...