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.
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); if (!el) { console.error(`Element with id "${id}" not found.`); return; } el.className = 'container'; var tmp = [...el.children]; tmp.forEach((e, i) => { let p = document.createElement('p'); p.className = "accordion_item"; if (i % 2 == 0) { //titles let dt = document.createElement('dt'); dt.className = "accordion__heading"; let 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 var 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; 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; 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); } .DropdownListContainer{ z-index:1 } </style>`; document.getElementById(id).insertAdjacentHTML('beforeend', css); //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() { var 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 var 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
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.
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
//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
//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.
Back to JavaScript for Text Areas
Recommended Comments
There are no comments to display.