Accordion (Multiselect)

Accordion (Multiselect)

Turn on a screen reader to experience this example in action.

Non-functional link, for testing purposes

HTML Source Code

<!--
This component has been adapted from an example provided by the W3C, in accordance with the W3C Software and Document License https://www.w3.org/copyright/software-license-2023/
-->
<div id="accordionGroup" class="accordion">
    <h2>
        <button type="button"
              aria-expanded="true"
              class="accordion-trigger"
              aria-controls="sect1"
              id="accordion1id">
            <span class="accordion-title">
            Prospective Students
            <span class="accordion-icon"></span>
        </span>
      </button>
    </h2>
    <div id="sect1"
         role="region"
         aria-labelledby="accordion1id"
         class="accordion-panel">
         <ul>
            <li><a href="#">Prospective Students Home Page</a></li>
            <li><a href="#">Admissions</a></li>
            <li><a href="#">Financing Your Education</a></li>
        </ul>
    </div>
    <h2>
      <button type="button"
              aria-expanded="false"
              class="accordion-trigger"
              aria-controls="sect2"
              id="accordion2id">
            <span class="accordion-title">
            Students
            <span class="accordion-icon"></span>
        </span>
      </button>
    </h2>
    <div id="sect2"
         role="region"
         aria-labelledby="accordion2id"
         class="accordion-panel"
         hidden="">
         <ul>
            <li><a href="#">Students Home Page</a></li>
            <li><a href="#">Records</a></li>
            <li><a href="#">Student Life</a></li>
            <li><a href="#">Student Organizations</a></li>
            <li><a href="#">Technology</a></li>
            <li><a href="#">Library</a></li>
        </ul>
    </div>
    <h2>
      <button type="button"
              aria-expanded="false"
              class="accordion-trigger"
              aria-controls="sect3"
              id="accordion3id">
        <span class="accordion-title">
            Faculty
          <span class="accordion-icon"></span>
        </span>
      </button>
    </h2>
    <div id="sect3"
         role="region"
         aria-labelledby="accordion3id"
         class="accordion-panel"
         hidden="">
         <ul>
            <li><a href="#">Faculty Home</a></li>
            <li><a href="#">Directory</a></li>
            <li><a href="#">News</a></li>
            <li><a href="#">Publications</a></li>
        </ul>
         
    </div>
    <h2>
        <button type="button"
                aria-expanded="false"
                class="accordion-trigger"
                aria-controls="sect4"
                id="accordion4id">
          <span class="accordion-title">
            Alumni
            <span class="accordion-icon"></span>
          </span>
        </button>
      </h2>
      <div id="sect4"
           role="region"
           aria-labelledby="accordion4id"
           class="accordion-panel"
           hidden="">
           <ul>
            <li><a href="#">Alumni Home Page</a></li>
            <li><a href="#">Benefits and Services</a></li>
            <li><a href="#">Giving</a></li>
            <li><a href="#">Alumni Association</a></li>
            <li><a href="#">Volunteer Opportunities</a></li>
        </ul>
      </div>

</div>
  

JavaScript Source Code

Dependencies:
  • JQuery
//This component has been adapted from an example provided by the W3C, in accordance with the W3C Software and Document License https://www.w3.org/copyright/software-license-2023/

if( typeof TabAccordion === "undefined")
{    
    var TabAccordions = [];    
    class TabAccordion {
        static openedEle = null;
        constructor(domNode, multiple ) {
        if (typeof multiple == "undefined") multiple=true;
        if (typeof multiple == "boolean") this.multiple = multiple;
            
        this.rootEl = domNode;
        this.buttonEl = this.rootEl.querySelector('button[aria-expanded]');
        if ( this.buttonEl == null)
        {
            console.log("button and/or aria-expanded attribute is missing.");
            return null;
        }    

        const controlsId = this.buttonEl.getAttribute('aria-controls');
        this.contentEl = document.getElementById(controlsId);
        this.open = this.buttonEl.getAttribute('aria-expanded') === 'true';
        this.closeActiveTab();
            
        // add event listeners
        this.buttonEl.addEventListener('click', this.onButtonClick.bind(this));
        }
    
        onButtonClick() {
        this.toggle(!this.open);
        }
    
        toggle(open) {
        // don't do anything if the open state doesn't change
        if (open === this.open) {
            return;
        }
        // update the internal state
        this.open = open;
        // handle DOM updates
        this.buttonEl.setAttribute('aria-expanded', `${open}`);
        if (open) {
            this.contentEl.removeAttribute('hidden');
        } else {
            this.contentEl.setAttribute('hidden', '');
        }
        this.closeActiveTab();
        //this.closeOthers();
            
        }
    
        closeActiveTab()
        {
            if (this.multiple ) return;
            if(this.open)
            {
                if( TabAccordion.openedEle != null ) 
                {
                TabAccordion.openedEle.contentEl.setAttribute('hidden', '');
                TabAccordion.openedEle.open = false;
                TabAccordion.openedEle.buttonEl.setAttribute('aria-expanded', 'false');
                }    
                TabAccordion.openedEle = this;
            }  
            else
            {
            if(this == TabAccordion.openedEle) 
                    TabAccordion.openedEle = null;
            }
        }

        closeOthers()
        {
            const accordions = document.querySelectorAll('.accordion h2');
            TabAccordions.forEach((accordionEl) => {
                //let tab = new TabAccordion(accordionEl);
                let tab = accordionEl;
                console.log("tab id:"+ tab.contentEl.id);
                console.log("target id:"+ this.contentEl.id);
                if (tab.contentEl.id !== this.contentEl.id)
                {
                    //accordionEl.setAttribute('hidden', '');
                    tab.close();
                }    
                    
            });
        }
        // Add public open and close methods for convenience
        open() {
        this.toggle(true);
        }
        close() {
        this.toggle(false);
        }

    }
  
    // init accordions
    const accordions = document.querySelectorAll('.accordion h2');
    accordions.forEach((accordionEl) => {
        var tab = new TabAccordion(accordionEl);
        TabAccordions.push(tab); 
    });

}

  

CSS Source Code

/*
This component has been adapted from an example provided by the W3C, in accordance with the W3C Software and Document License https://www.w3.org/copyright/software-license-2023/
*/
.accordion {
    margin: 0;
    padding: 0;
    border: 2px solid hsl(0deg 0% 52%);
    border-radius: 7px;
    width: 20em;
  }
  
  .accordion h2 {
    margin: 0;
    padding: 0;
  }
  
  .accordion:focus-within {
    border-color: hsl(216deg 94% 43%);
  }
  
  .accordion:focus-within h2 {
    background-color: hsl(0deg 0% 97%);
  }
  
  .accordion > * + * {
    border-top: 1px solid hsl(0deg 0% 52%);
  }
  
  .accordion-trigger {
    background: #375898;
    color: white;
    display: block;
    font-weight: bold;
    font-size: 1rem;
    width: 100%;
    margin: 0;
    padding: 1em 1.5em;
    position: relative;
    text-align: left;
    outline: none;
  }
  
  .accordion-trigger:focus,
  .accordion-trigger:hover {
    background: hsl(216deg 94% 94%);
    color: black;
  }
  
  .accordion-trigger:focus {
    outline: 4px solid transparent;
  }
  
  .accordion > *:first-child .accordion-trigger,
  .accordion > *:first-child {
    border-radius: 5px 5px 0 0;
  }
  
  .accordion > *:last-child .accordion-trigger,
  .accordion > *:last-child {
    border-radius: 0 0 5px 5px;
  }
  
  button {
    border-style: none;
  }
  
  .accordion button::-moz-focus-inner {
    border: 0;
  }
  
  .accordion-title {
    display: block;
    pointer-events: none;
    border: transparent 2px solid;
    border-radius: 5px;
    padding: 0.25em;
    outline: none;
  }
  
  .accordion-trigger:focus .accordion-title {
    border-color: hsl(216deg 94% 43%);
  }
  
  .accordion-icon {
    border: solid currentcolor;
    border-width: 0 2px 2px 0;
    height: 0.5rem;
    pointer-events: none;
    position: absolute;
    right: 2em;
    top: 50%;
    transform: translateY(-60%) rotate(45deg);
    width: 0.5rem;
  }
  
  .accordion-trigger:focus .accordion-icon,
  .accordion-trigger:hover .accordion-icon {
    border-color: hsl(216deg 94% 43%);
  }
  
  .accordion-trigger[aria-expanded="true"] .accordion-icon {
    transform: translateY(-50%) rotate(-135deg);
  }
  
  .accordion-panel {
    margin: 0;
   /* padding: 1em 1.5em;  */
  }
  
  /* For Edge bug https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/4806035/ */
  .accordion-panel[hidden] {
    display: none;
  }
  
.accordion div.tab:focus {
	background-color: #566ac8;
	/* outline: 2px solid #8cc63f; */
	outline: 3px solid #467310;
}
.accordion > li:hover {
	cursor: pointer;
}
.accordion div.tab {
	background: #375898;
	border: 1px solid #fff;
	color: #fff;
	padding: 3px 6px;
}
.tablist div.tab.active {
	font-weight: bold;
}
.accordion-panel .inactive {
	display: none;
}
.accordion-panel ul {
	margin: 0;
	padding: 0;
}
.accordion-panel li a {
	text-decoration: none;
}

.accordion-panel li {
	list-style-type: none;
	background:#fff;
	border-bottom:1px dashed #375898;
	color:#375898;
	margin: 0;
	padding: 5px;
}
.accordion-panel li:last-child {
	border-bottom: none;
}

Copy and Paste Full Page Example