<section class="accordion">
    <h2 id="group-one" class="accordion-title">Group One</h2>
    <p class="accordion-intro">You'll focus developing your creativity and practical skills that employers tell us they want from graduates. You'll study a total of 120 credits per year. This will be made up of single or double modules.</p>
    <div class="accordion-tablist" role="tablist">
        <div class="accordion-tab">
            <a id="year-one-label" href="#year-one" class="accordion-tab-title-container collapse-toggle active" data-collapse role="tab" aria-expanded="true" aria-controls="year-one">
                <h4 class="accordion-tab-title">
                    Year one
                    <span class="accordion-tab-title-indicator">
                        <i class="uod-icons uod-icons-chevron-down"></i>
                    </span>
                </h4>
            </a>
            <div id="year-one" class="accordion-tab-content collapse active" role="tabpanel" aria-labelledby="year-one-label">
                <div class="accordion-tab-content-inner">
                    You’ll study modules such as:
                    <ol>
                        <li>Making it: Materials and Manufacturing Processes</li>
                        <li>Product Design Studies</li>
                        <li>Innovation</li>
                        <li>Computer Aided Product Design</li>
                        <li>Design Evaluation</li>
                        <li>Context: Being a Product Designer</li>
                    </ol>
                </div>
            </div>
        </div>
        <div class="accordion-tab">
            <a id="year-two-label" href="#year-two" class="accordion-tab-title-container collapse-toggle" data-collapse role="tab" aria-expanded="false" aria-controls="year-two">
                <h4 class="accordion-tab-title">
                    Year two
                    <span class="accordion-tab-title-indicator">
                        <i class="uod-icons uod-icons-chevron-down"></i>
                    </span>
                </h4>
            </a>
            <div id="year-two" class="accordion-tab-content collapse" role="tabpanel" aria-labelledby="year-two-label">
                <div class="accordion-tab-content-inner">
                    <p>Product Design at Derby is aimed at creative people who may have studied Art and Design or Design Technology at school or developed a passion for the subject since leaving formal education. We will help you to
                        <strong>build on your creativity</strong> and develop an understanding of the
                        <em>reality of design</em> for manufacture.
                        <a href="#">This is a link.</a> because we teach in small groups we can help you to develop your potential as an individual and support your enthusiasm for design.</p>
                </div>
            </div>
        </div>
        <div class="accordion-tab">
            <a id="year-three-label" href="#year-three" class="accordion-tab-title-container collapse-toggle" data-collapse role="tab" aria-expanded="false" aria-controls="year-three">
                <h4 class="accordion-tab-title">
                    Year three
                    <span class="accordion-tab-title-indicator">
                        <i class="uod-icons uod-icons-chevron-down"></i>
                    </span>
                </h4>
            </a>
            <div id="year-three" class="accordion-tab-content collapse" role="tabpanel" aria-labelledby="year-three-label">
                <div class="accordion-tab-content-inner">
                    <p>Full-time students applying in September should apply for this course through UCAS or you can apply directly to the University for an undergraduate course if you’re not applying to any other UK university in the same year.</p>
                    <p>
                        <a href="#" class="button-outline">
            Apply through UCAS<i class="uod-icons uod-icons-external-link"></i>
        </a>
                        <a href="#" class="button-outline">
            Apply directly to the University<i class="uod-icons uod-icons-external-link"></i>
        </a>
                    </p>
                </div>
            </div>
        </div>
    </div>
</section>

<style type="text/css">
    #udol-course-taster-exercise .accordion-tab:after {
        display: inline-block;
        transform: translate(0, 0);
        text-rendering: auto;
        font: normal normal 400 14px/1 uod-icons;
        font-size: inherit;
        -moz-osx-font-smoothing: grayscale;
        -webkit-font-smoothing: antialiased;
        content: "\ea0d";
        font-size: 2em;
        color: #101d49;
        width: 100%;
        text-align: center;
        padding-top: 20px;
    }

    #udol-course-taster-exercise .accordion-tab:last-child:after {
        content: '';
        padding-top: 0;
    }
</style>
<section class="accordion{% if fullWidth == true %} full-width{% endif %}{% if scheme %} accordion-scheme-{{ scheme }}{% endif %}"{% if id %} id="{{ id|raw }}"{% endif %}>
    {% if title %}<h2 id="{% if id %}{{ id }}{% else %}{{ title | lower | replace({ ' ' : '-' }) }}{% endif %}" class="accordion-title">{{ title|raw }}</h2>{% endif %}
    {% if intro %}<p class="accordion-intro">{{ intro|raw }}</p>{% endif %}
    <div class="accordion-tablist" role="tablist">
        {% for item in contents %}
        <div class="accordion-tab">
            <a id="{{ item.tag|raw }}-label" href="#{{ item.tag|raw }}" class="accordion-tab-title-container collapse-toggle{{ item.expanded == true ? ' active'}}{% if usehash == true %} usehash{% endif %}"
                data-collapse {% if modal == true %} data-group="{{ tag|raw }}" {% endif %}
                role="tab" aria-expanded="{{ item.expanded == true ? 'true' : 'false' }}" aria-controls="{{ item.tag|raw }}">
                {% if useDivTitles %}
                <div class="accordion-tab-title h4">
                    {{ item.label|raw }}
                    <span class="accordion-tab-title-indicator">
                        <i class="uod-icons uod-icons-chevron-down"></i>
                    </span>
                </div>
                {% else %}
                <h4 class="accordion-tab-title">
                    {{ item.label|raw }}
                    <span class="accordion-tab-title-indicator">
                        <i class="uod-icons uod-icons-chevron-down"></i>
                    </span>
                </h4>
                {% endif %}
            </a>
            <div id="{{ item.tag|raw }}" class="accordion-tab-content collapse{{ item.expanded == true ? ' active'}}" role="tabpanel" aria-labelledby="{{ item.tag|raw }}-label">
                <div class="accordion-tab-content-inner">
                    {{ item.content|raw }}
                </div>
            </div>
        </div>
        {% endfor %}
    </div>
</section>


<style type="text/css">
    #udol-course-taster-exercise .accordion-tab:after {
        display: inline-block;
        transform: translate(0, 0);
        text-rendering: auto;
        font: normal normal 400 14px/1 uod-icons;
        font-size: inherit;
        -moz-osx-font-smoothing: grayscale;
        -webkit-font-smoothing: antialiased;
        content: "\ea0d";
        font-size: 2em;
        color: #101d49;
        width: 100%;
        text-align: center;
        padding-top: 20px;
    }

    #udol-course-taster-exercise .accordion-tab:last-child:after {
        content: '';
        padding-top: 0;
    }
</style>
{
  "contents": [
    {
      "tag": "year-one",
      "label": "Year one",
      "expanded": true,
      "content": "You’ll study modules such as:\n    <ol>\n        <li>Making it: Materials and Manufacturing Processes</li>\n        <li>Product Design Studies</li>\n        <li>Innovation</li>\n        <li>Computer Aided Product Design</li>\n        <li>Design Evaluation</li>\n        <li>Context: Being a Product Designer</li>\n    </ol>"
    },
    {
      "tag": "year-two",
      "label": "Year two",
      "expanded": null,
      "content": "<p>Product Design at Derby is aimed at creative people who may have studied Art and Design or Design Technology\n    at school or developed a passion for the subject since leaving formal education. We will help you to\n    <strong>build on your creativity</strong> and develop an understanding of the\n    <em>reality of design</em> for manufacture.\n    <a href=\"#\">This is a link.</a> because we teach in small groups we can help you to develop your potential as an\n    individual and support your enthusiasm for design.</p>"
    },
    {
      "tag": "year-three",
      "label": "Year three",
      "expanded": null,
      "content": "<p>Full-time students applying in September should apply for this course through UCAS  or you can apply directly to the University  for an undergraduate course if you’re not applying to any other UK university in the same year.</p>\n    <p>\n        <a href=\"#\" class=\"button-outline\">\n            Apply through UCAS<i class=\"uod-icons uod-icons-external-link\"></i>\n        </a>\n        <a href=\"#\" class=\"button-outline\">\n            Apply directly to the University<i class=\"uod-icons uod-icons-external-link\"></i>\n        </a>\n    </p>"
    }
  ],
  "title": "Group One",
  "intro": "You'll focus developing your creativity and practical skills that employers tell us they want from graduates. You'll study a total of 120 credits per year. This will be made up of single or double modules.",
  "tag": "accordion-demo",
  "modal": null
}
  • Content:
    import houdini from './houdini.custom'
    
    /**
     * Polyfil this function (swapping out for jquery long term would be nice)
     */
    const forEach = function (array, callback, scope) {
        for (let i = 0; i < array.length; i++) {
            callback.call(scope, i, array[i]);
        }
    };
    
    /**
     * Custom Events
     */
    const ACCORDION_OPEN = 'ACCORDION_OPEN'
    const ACCORDION_CLOSE = 'ACCORDION_CLOSE'
    
    /**
     * When the page hash changes, scroll to that position.
     */
    function scrollIntoView() {
        const hash = window.location.hash;
        if (!hash) {
            return;
        }
        var toggle = document.querySelector(hash + '-label');
        if (toggle) {
            window.scrollTo(0, toggle.offsetTop);
        }
    }
    
    const accordionElementExistsOnPage = document.querySelector('.accordion') != undefined;
    if (accordionElementExistsOnPage) {
        const hash = window.location.hash;
        if (hash) {
            // auto-close any open panels which do not match the current hash
            const toggle = document.querySelectorAll('[data-collapse]' + ':not([href*="' + hash + '"])');
            forEach(toggle, function (index, value) {
                value.classList.remove('active');
                value.setAttribute('aria-expanded', 'false');
                const elementId = value.getAttribute('aria-controls');
                const content = document.querySelector('[id="' + elementId + '"]');
                if (content) {
                    content.classList.remove('active');
                }
            });
        }
    
        window.addEventListener('hashchange', scrollIntoView, false);
    
        houdini.init({
            callbackOpen: function (content, toggle) {
                // remove the focus rect created by houdinijs forcing focus to the new panel
                content.blur();
    
                // set a new max-height to the content height (max height can be animaed, height alone cannot)
                content.style['max-height'] = content.scrollHeight + 'px';
    
                if (toggle) {
                    // update the aria metadata for this component to reflect the new state
                    toggle.setAttribute('aria-expanded', 'true');
                    // dispatch a custom event to allow other components to react to the panel being opened
                    $('.main-navigation-mobile').trigger(ACCORDION_OPEN, toggle.getAttribute('aria-controls'));
                }
            },
            callbackClose: function (content, toggle) {
                // set a new max-height to allow the panel to be animated closed
                content.style['max-height'] = 0;
                if (toggle) {
                    // if the hash is currently in the url and we've just closed the panel, remove the hash
                    if (toggle.getAttribute('aria-expanded') == 'true' && '#' + toggle.getAttribute('aria-controls') === window.location.hash) {
                        history.pushState("", document.title, window.location.pathname + window.location.search);
                    }
                    // update the aria metadata for this component to reflect the new state
                    toggle.setAttribute('aria-expanded', 'false');
                    // remove the focus rect created by houdinijs forcing focus to the new panel
                    toggle.blur();
                    // dispatch a custom event to allow other components to react to the panel being closed
                    $('.main-navigation-mobile').trigger(ACCORDION_CLOSE, toggle.getAttribute('aria-controls'));
                } else {
                    // dispatch a custom event to allow other components to react to the panel being closed
                    $('.main-navigation-mobile').trigger(ACCORDION_CLOSE);
                }
            }
        });
    }
    
  • URL: /components/raw/accordion/accordion.js
  • Filesystem Path: components/components/accordion/accordion.js
  • Size: 3.6 KB
  • Content:
    .accordion-tab {
        margin-bottom: $margin-extra-small;
    
        &-title {
            display: flex;
            align-items: center;
            justify-content: space-between;
            margin-bottom: 0;
    
            .full-width & {
                @include full-bleed-inset;
            }
    
            &-container {
                @include disable-underlines;
    
                display: block;
                transition: background-color .3s $default-animation-curve;
                border-radius: 2px;
                background: $light-grey;
                padding: 13px 60px 13px 10px;
    
                @include for-tablet-portrait-up {
                    padding: 15.5px 65px 15.5px 20px;
                }
    
                .full-width & {
                    padding-right: 0;
                    padding-left: 0;
                }
    
                .accordion-scheme-dark & {
                    background-color: #191919;
                }
    
                &:hover,
                &:focus,
                &:active {
                    background-color: $light-grey-hover;
    
                    .accordion-scheme-dark & {
                        background-color: $text-black;
                    }
                }
            }
    
            &-indicator {
                display: inline-block;
                position: absolute;
                top: 50%;
                right: 10px;
                margin-top: -20px;
                margin-left: 10px;
                border: solid 2px $mid-blue;
                border-radius: 2px;
                width: 40px;
                height: 40px;
                text-align: center;
                line-height: 40px;
                color: $mid-blue;
                font-size: 9px; // the width of the arrow indicator
    
                @include for-tablet-portrait-up {
                    right: 20px;
                    margin-top: -22.5px;
                    width: 45px;
                    height: 45px;
                    line-height: 45px;
                    font-size: 11px;
                }
    
    
                .accordion-scheme-dark & {
                    border-color: $white;
                    color: $white;
                }
    
                .uod-icons {
                    transform: rotate(0deg);
                    transition: transform .3s $default-animation-curve;
                    transform-origin: 50%;
    
                    .active & {
                        transform: rotate(-180deg);
                    }
                }
            }
    
            a {
                &,
                &:link {
                    color: $primary-blue;
                }
    
                .accordion-scheme-dark & {
                    color: $white;
                }
            }
    
            .accordion-scheme-dark & {
                color: $white;
            }
        }
    
        &-content {
            position: relative;
            transition: max-height .3s $default-animation-curve;
            overflow: hidden;
    
            &:focus {
                outline: none;
            }
    
            &.active {
                margin-bottom: $margin-extra-small;
                border-bottom: 1px dashed $dark-grey;
            }
    
            &-inner {
                padding: 10px;
    
                @include for-tablet-portrait-up {
                    padding: $margin-extra-small;
                }
    
                .full-width & {
                    @include full-bleed-inset;
    
                    padding-top: 10px;
                    padding-bottom: 10px;
    
                    @include for-tablet-portrait-up {
                        padding-top: $margin-extra-small;
                        padding-bottom: $margin-extra-small;
                    }
                }
            }
        }
    
        .collapse {
            transition: max-height .3s $default-animation-curve;
            max-height: 0;
    
            &.active {
                max-height: 100vh;
            }
        }
    }
    
  • URL: /components/raw/accordion/accordion.scss
  • Filesystem Path: components/components/accordion/accordion.scss
  • Size: 3.5 KB
  • Content:
    /**
     * This a modified version of houdinijs v9.4.2 - https://github.com/cferdinandi/houdini
     * Modified to add the ability to toggle panels without updating the page hash, via the useHashClass setting.
     */
    (function (root, factory) {
    	if ( typeof define === 'function' && define.amd ) {
    		define([], factory(root));
    	} else if ( typeof exports === 'object' ) {
    		module.exports = factory(root);
    	} else {
    		root.houdini = factory(root);
    	}
    })(typeof global !== 'undefined' ? global : this.window || this.global, function (root) {
    
    	'use strict';
    
    	//
    	// Variables
    	//
    
    	var houdini = {}; // Object for public APIs
    	var supports = 'querySelector' in document && 'addEventListener' in root && 'classList' in document.createElement('_'); // Feature test
    	var settings, collapse;
    
    	// Default settings
    	var defaults = {
    		selectorToggle: '[data-collapse]',
    		selectorContent: '.collapse',
    		toggleActiveClass: 'active',
    		contentActiveClass: 'active',
    		useHashClass: 'usehash',
    		initClass: 'js-houdini',
    		stopVideo: true,
    		callbackOpen: function () {},
    		callbackClose: function () {}
    	};
    
    
    	//
    	// Methods
    	//
    
    	/**
    	 * A simple forEach() implementation for Arrays, Objects and NodeLists
    	 * @private
    	 * @param {Array|Object|NodeList} collection Collection of items to iterate
    	 * @param {Function} callback Callback function for each iteration
    	 * @param {Array|Object|NodeList} scope Object/NodeList/Array that forEach is iterating over (aka `this`)
    	 */
    	var forEach = function (collection, callback, scope) {
    		if (Object.prototype.toString.call(collection) === '[object Object]') {
    			for (var prop in collection) {
    				if (Object.prototype.hasOwnProperty.call(collection, prop)) {
    					callback.call(scope, collection[prop], prop, collection);
    				}
    			}
    		} else {
    			for (var i = 0, len = collection.length; i < len; i++) {
    				callback.call(scope, collection[i], i, collection);
    			}
    		}
    	};
    
    	/**
    	 * Merge defaults with user options
    	 * @private
    	 * @param {Object} defaults Default settings
    	 * @param {Object} options User options
    	 * @returns {Object} Merged values of defaults and options
    	 */
    	var extend = function () {
    
    		// Variables
    		var extended = {};
    		var deep = false;
    		var i = 0;
    		var length = arguments.length;
    
    		// Check if a deep merge
    		if ( Object.prototype.toString.call( arguments[0] ) === '[object Boolean]' ) {
    			deep = arguments[0];
    			i++;
    		}
    
    		// Merge the object into the extended object
    		var merge = function (obj) {
    			for ( var prop in obj ) {
    				if ( Object.prototype.hasOwnProperty.call( obj, prop ) ) {
    					// If deep merge and property is an object, merge properties
    					if ( deep && Object.prototype.toString.call(obj[prop]) === '[object Object]' ) {
    						extended[prop] = extend( true, extended[prop], obj[prop] );
    					} else {
    						extended[prop] = obj[prop];
    					}
    				}
    			}
    		};
    
    		// Loop through each object and conduct a merge
    		for ( ; i < length; i++ ) {
    			var obj = arguments[i];
    			merge(obj);
    		}
    
    		return extended;
    
    	};
    
    	/**
    	 * Get the closest matching element up the DOM tree
    	 * @param {Element} elem Starting element
    	 * @param {String} selector Selector to match against (class, ID, or data attribute)
    	 * @return {Boolean|Element} Returns false if not match found
    	 */
    	var getClosest = function ( elem, selector ) {
    
    		// Element.matches() polyfill
    		if (!Element.prototype.matches) {
    			Element.prototype.matches =
    				Element.prototype.matchesSelector ||
    				Element.prototype.mozMatchesSelector ||
    				Element.prototype.msMatchesSelector ||
    				Element.prototype.oMatchesSelector ||
    				Element.prototype.webkitMatchesSelector ||
    				function(s) {
    					var matches = (this.document || this.ownerDocument).querySelectorAll(s),
    						i = matches.length;
    					while (--i >= 0 && matches.item(i) !== this) {}
    					return i > -1;
    				};
    		}
    
    		// Get closest match
    		for ( ; elem && elem !== document; elem = elem.parentNode ) {
    			if ( elem.matches( selector ) ) return elem;
    		}
    
    		return null;
    
    	};
    
    	/**
    	 * Escape special characters for use with querySelector
    	 * @public
    	 * @param {String} id The anchor ID to escape
    	 * @author Mathias Bynens
    	 * @link https://github.com/mathiasbynens/CSS.escape
    	 */
    	var escapeCharacters = function ( id ) {
    
    		// Remove leading hash
    		if ( id.charAt(0) === '#' ) {
    			id = id.substr(1);
    		}
    
    		var string = String(id);
    		var length = string.length;
    		var index = -1;
    		var codeUnit;
    		var result = '';
    		var firstCodeUnit = string.charCodeAt(0);
    		while (++index < length) {
    			codeUnit = string.charCodeAt(index);
    			// Note: there’s no need to special-case astral symbols, surrogate
    			// pairs, or lone surrogates.
    
    			// If the character is NULL (U+0000), then throw an
    			// `InvalidCharacterError` exception and terminate these steps.
    			if (codeUnit === 0x0000) {
    				throw new InvalidCharacterError(
    					'Invalid character: the input contains U+0000.'
    				);
    			}
    
    			if (
    				// If the character is in the range [\1-\1F] (U+0001 to U+001F) or is
    				// U+007F, […]
    				(codeUnit >= 0x0001 && codeUnit <= 0x001F) || codeUnit == 0x007F ||
    				// If the character is the first character and is in the range [0-9]
    				// (U+0030 to U+0039), […]
    				(index === 0 && codeUnit >= 0x0030 && codeUnit <= 0x0039) ||
    				// If the character is the second character and is in the range [0-9]
    				// (U+0030 to U+0039) and the first character is a `-` (U+002D), […]
    				(
    					index === 1 &&
    					codeUnit >= 0x0030 && codeUnit <= 0x0039 &&
    					firstCodeUnit === 0x002D
    				)
    			) {
    				// http://dev.w3.org/csswg/cssom/#escape-a-character-as-code-point
    				result += '\\' + codeUnit.toString(16) + ' ';
    				continue;
    			}
    
    			// If the character is not handled by one of the above rules and is
    			// greater than or equal to U+0080, is `-` (U+002D) or `_` (U+005F), or
    			// is in one of the ranges [0-9] (U+0030 to U+0039), [A-Z] (U+0041 to
    			// U+005A), or [a-z] (U+0061 to U+007A), […]
    			if (
    				codeUnit >= 0x0080 ||
    				codeUnit === 0x002D ||
    				codeUnit === 0x005F ||
    				codeUnit >= 0x0030 && codeUnit <= 0x0039 ||
    				codeUnit >= 0x0041 && codeUnit <= 0x005A ||
    				codeUnit >= 0x0061 && codeUnit <= 0x007A
    			) {
    				// the character itself
    				result += string.charAt(index);
    				continue;
    			}
    
    			// Otherwise, the escaped character.
    			// http://dev.w3.org/csswg/cssom/#escape-a-character
    			result += '\\' + string.charAt(index);
    
    		}
    
    		return '#' + result;
    
    	};
    
    	/**
    	 * Stop YouTube, Vimeo, and HTML5 videos from playing when leaving the slide
    	 * @private
    	 * @param  {Element} content The content container the video is in
    	 * @param  {String} activeClass The class asigned to expanded content areas
    	 */
    	var stopVideos = function ( content, settings ) {
    
    		// Check if stop video enabled
    		if ( !settings.stopVideo ) return;
    
    		// Only run if content container is open
    		if ( !content.classList.contains( settings.contentActiveClass ) ) return;
    
    		// Check if the video is an iframe or HTML5 video
    		var iframe = content.querySelector( 'iframe');
    		var video = content.querySelector( 'video' );
    
    		// Stop the video
    		if ( iframe ) {
    			var iframeSrc = iframe.src;
    			iframe.src = iframeSrc;
    		}
    		if ( video ) {
    			video.pause();
    		}
    
    	};
    
    	/**
    	 * Add focus to content
    	 * @private
    	 * @param  {node}   content  The content to bring into focus
    	 * @param  {object} settings Options
    	 */
    	var adjustFocus = function ( content, settings ) {
    
    		if ( content.hasAttribute( 'data-houdini-no-focus' ) ) return;
    
    		// If content is closed, remove tabindex
    		if ( !content.classList.contains( settings.contentActiveClass ) ) {
    			if ( content.hasAttribute( 'data-houdini-focused' ) ) {
    				content.removeAttribute( 'tabindex' );
    			}
    			return;
    		}
    
    		// Get current position on the page
    		var position = {
    			x: root.pageXOffset,
    			y: root.pageYOffset
    		};
    
    		// Set focus and reset position to account for page jump on focus
    		content.focus();
    		if ( document.activeElement.id !== content.id ) {
    			content.setAttribute( 'tabindex', '-1' );
    			content.setAttribute( 'data-houdini-focused', true );
    			content.focus();
    		}
    		root.scrollTo( position.x, position.y );
    
    	};
    
    	/**
    	 * Open collapsed content
    	 * @public
    	 * @param  {String} contentID The ID of the content area to close
    	 * @param  {Element} toggle The element that toggled the close action
    	 * @param  {Object} options
    	 */
    	houdini.closeContent = function ( contentID, toggle, options ) {
    
    		// Variables
    		var localSettings = extend( settings || defaults, options || {} );  // Merge user options with defaults
    		var content = document.querySelector( escapeCharacters( contentID ) ); // Get content area
    
    		// Sanity check
    		if ( !content ) return;
    
    		// Toggle the content
    		stopVideos( content, localSettings ); // If content area is closed, stop playing any videos
    		if ( toggle ) {
    			toggle.classList.remove( localSettings.toggleActiveClass );// Change text on collapse toggle
    		}
    		content.classList.remove( localSettings.contentActiveClass ); // Collapse or expand content area
    		adjustFocus( content, localSettings );
    
    		// Run callbacks after toggling content
    		localSettings.callbackClose( content, toggle );
    
    	};
    
    	/**
    	 * Open collapsed content
    	 * @public
    	 * @param  {String} contentID The ID of the content area to open
    	 * @param  {Element} toggle The element that toggled the open action
    	 * @param  {Object} options
    	 */
    	houdini.openContent = function ( contentID, toggle, options ) {
    
    		// Variables
    		var localSettings = extend( settings || defaults, options || {} );  // Merge user options with defaults
    		var content = document.querySelector( escapeCharacters( contentID ) ); // Get content area
    		var group = toggle && toggle.hasAttribute( 'data-group') ? document.querySelectorAll('[data-group="' + toggle.getAttribute( 'data-group') + '"]') : [];
    
    		// Sanity check
    		if ( !content ) return;
    
    		// If a group, close all other content areas
    		forEach(group, function (item) {
    			houdini.closeContent( item.hash, item );
    		});
    
    		// Open the content
    		if ( toggle ) {
    			toggle.classList.add( localSettings.toggleActiveClass ); // Change text on collapse toggle
    		}
    		content.classList.add( localSettings.contentActiveClass ); // Collapse or expand content area
    		adjustFocus( content, localSettings );
    		content.removeAttribute( 'data-houdini-no-focus' );
    
    		// Run callbacks after toggling content
    		localSettings.callbackOpen( content, toggle );
    
    	};
    
    	/**
    	 * Handle has change event
    	 * @private
    	 */
    	var hashChangeHandler = function (event) {
    
    		// Get hash from URL
    		var hash = root.location.hash;
    
    		// If clicked collapse is cached, reset it's ID
    		if ( collapse ) {
    			collapse.id = collapse.getAttribute( 'data-collapse-id' );
    			collapse = null;
    		}
    
    		// If there's a URL hash, open the content with matching ID
    		if ( !hash ) return;
    		var toggle = document.querySelector( settings.selectorToggle + '[href*="' + hash + '"]' );
    		houdini.openContent( hash, toggle );
    
    	};
    
    	/**
    	 * Handle toggle click events
    	 * @private
    	 */
    	var clickHandler = function (event) {
    		
    		// Don't run if right-click or command/control + click
    		if ( event.button !== 0 || event.metaKey || event.ctrlKey ) return;
    		
    		// Check if a toggle was clicked
    		var toggle = getClosest( event.target, settings.selectorToggle );
    		if ( !toggle || !toggle.hash ) return;
    
    		// Custom: Check if we're toggling without updating hashes
    		if ( !toggle.classList.contains( settings.useHashClass ) ) {
    			event.preventDefault();
    			if ( !toggle.classList.contains( settings.toggleActiveClass ) ) {
    				houdini.openContent( toggle.hash, toggle );
    				return;
    			}
    		}
    
    		// If the tab is already open, close it
    		if ( toggle.classList.contains( settings.toggleActiveClass ) ) {
    			event.preventDefault();
    			houdini.closeContent( toggle.hash, toggle );
    			return;
    		}
    
    		// Get the collapse content
    		collapse = document.querySelector( toggle.hash );
    
    		// If tab content exists, save the ID as a data attribute and remove it (prevents scroll jump)
    		if ( !collapse ) return;
    		collapse.setAttribute( 'data-collapse-id', collapse.id );
    		collapse.id = '';
    
    		// If no hash change event will happen, fire manually
    		if ( toggle.hash === root.location.hash ) {
    			event.preventDefault();
    			hashChangeHandler();
    		}
    
    	};
    
    	/**
    	 * Handle content focus events
    	 * @private
    	 */
    	var focusHandler = function (event) {
    
    		// Variables
    		collapse = getClosest( event.target, settings.selectorContent );
    
    		// Only run if content exists and isn't open already
    		if ( !collapse || collapse.classList.contains( settings.contentActiveClass ) ) return;
    
    		// Save the ID as a data attribute and remove it (prevents scroll jump)
    		var hash = collapse.id;
    		collapse.setAttribute( 'data-collapse-id', hash );
    		collapse.setAttribute( 'data-houdini-no-focus', true );
    		collapse.id = '';
    
    		// If no hash change event will happen, fire manually
    		if ( hash === root.location.hash.substring(1) ) {
    			hashChangeHandler();
    			return;
    		}
    
    		// Otherwise, update the hash
    		root.location.hash = hash;
    
    	};
    
    	/**
    	 * Destroy the current initialization.
    	 * @public
    	 */
    	houdini.destroy = function () {
    		if ( !settings ) return;
    		document.documentElement.classList.remove( settings.initClass );
    		document.removeEventListener('click', clickHandler, false);
    		document.removeEventListener('focus', focusHandler, true);
    		root.removeEventListener('hashchange', hashChangeHandler, false);
    		settings = null;
    		collapse = null;
    	};
    
    	/**
    	 * Initialize Houdini
    	 * @public
    	 * @param {Object} options User settings
    	 */
    	houdini.init = function ( options ) {
    
    		// feature test
    		if ( !supports ) return;
    
    		// Destroy any existing initializations
    		houdini.destroy();
    
    		// Merge user options with defaults
    		settings = extend( defaults, options || {} );
    
    		// Add class to HTML element to activate conditional CSS
    		document.documentElement.classList.add( settings.initClass );
    
    		// Listen for all click events
    		document.addEventListener('click', clickHandler, false);
    		document.addEventListener('focus', focusHandler, true);
    		root.addEventListener('hashchange', hashChangeHandler, false);
    
    		// If URL has a hash, activate hashed content by default
    		hashChangeHandler();
    
    	};
    
    
    	//
    	// Public APIs
    	//
    
    	return houdini;
    
    });
    
  • URL: /components/raw/accordion/houdini.custom.js
  • Filesystem Path: components/components/accordion/houdini.custom.js
  • Size: 14.2 KB

Accordion Component

Key Features

  • #link integration - activating an accordion will update the page url, and visiting a page url with a #link will deep-link to that content.
  • Keyboard accessible
  • ARIA Metadata
  • Optional use of anchor links and url updating

Options

  • Can be set to either Modal or Plural behaviour:
    • Modal allows only a single panel to be expanded at once, and automatically collapses other panels on activation of a new one.
    • Plural allows panels to be independently expanded or collapsed, without affecting the others.
    • Default mode is Plural.
  • Individual panels can have a default value for being expanded or collapsed.
  • Supports a dark or a light colour scheme, based on the scheme attribute being set.

Properties

Accordion

  • title [optional, html]
  • intro [optional, html]
  • tag [required, string]
  • modal [optional, default false]
  • usehash [optional, default true]
  • fullWidth [optional, default false]
  • contents [Array of Accordion Panel]
  • scheme [optional, string “dark” or undefined]

Accordion Panel

  • tag [required, string]
  • label [required, string]
  • expanded [optional, default false]
  • content [required, html]