Acordeón suave con detalles elemento de divulgación
| Autor: | Giana |
|---|---|
| Views Total: | 256 |
| Sitio oficial: | Ir a la web |
| Actualizado: | January 4, 2019 |
| Licencia: | MIT |
Vista prévia
Descripción
Crear un acordeón suavemente plegable usando JavaScript, CSS3 animaciones, y detalles y Resumen Elements.
Funcionamiento
Codicode el acordeón.
<div class="container collapse"> <details> <summary>Accordion 1</summary> <div class="details-wrapper"> <div class="details-styling"> Details 1 </div> </div> </details> <details> <summary>Accordion 2</summary> <div class="details-wrapper"> <div class="details-styling"> Details 2 </div> </div> </details> <details> <summary>Accordion 3</summary> <div class="details-wrapper"> <div class="details-styling"> Details 3 </div> </div> </details> </div>
El CSS necesario & estilos CSS3 para el acordeón.
/*
Please wrap your collapsible content in a single element or so help me
Must add a transition or it breaks because that's the whole purpose of this
Only one transition-duration works (see explanation on line #141 in JS)
You can add more to an inner wrapper (.details-styling)
*/
.collapse-init summary + * {
transition: all 0.25s ease-in-out;
overflow: hidden;
}
/*
Closed state. Any CSS transitions work here
The JS has a height calculation to make sliding opened/closed easier, but it's not necessary
Remove the height prop for a simple toggle on/off (after all that work I did for you?)
*/
.collapse-init :not(.panel-active) summary + * {
height: 0;
opacity: 0;
-webkit-transform: scale(0.9);
transform: scale(0.9);
-webkit-transform-origin: bottom center;
transform-origin: bottom center;
}
.collapse-init summary {
list-style: none;
}
.collapse-init summary::-webkit-details-marker {
display: none;
}
.collapse-init summary::before {
display: none;
}
.collapse-init summary {
cursor: pointer;
}
etails {
background: #fff;
border: 1px solid #d6d1e0;
border-bottom: 0;
list-style: none;
}
details:first-child {
border-radius: 6px 6px 0 0;
}
details:last-child {
border-bottom: 1px solid #d6d1e0;
border-radius: 0 0 6px 6px;
}
summary {
display: block;
transition: 0.2s;
font-weight: 700;
padding: 1em;
}
summary:focus {
outline: 2px solid #5b13ec;
}
.collapse-init summary::after {
border-right: 2px solid;
border-bottom: 2px solid;
content: '';
float: right;
width: 0.5em;
height: 0.5em;
margin-top: 0.25em;
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
transition: inherit;
}
[open] summary {
background: #5b13ec;
color: #f8f5fe;
}
[open] summary::after {
margin-top: 0.5em;
-webkit-transform: rotate(225deg);
transform: rotate(225deg);
} Cargue el detalles-Element-polyfill para el soporte de IE/Edge.
<script src="https://cdn.jsdelivr.net/gh/javan/[email protected]/dist/details-element-polyfill.js"></script>
El JavaScript principal para el acordeón.
var _createClass = function () {function defineProperties(target, props) {for (var i = 0; i < props.length; i++) {var descriptor = props[i];descriptor.enumerable = descriptor.enumerable || false;descriptor.configurable = true;if ("value" in descriptor) descriptor.writable = true;Object.defineProperty(target, descriptor.key, descriptor);}}return function (Constructor, protoProps, staticProps) {if (protoProps) defineProperties(Constructor.prototype, protoProps);if (staticProps) defineProperties(Constructor, staticProps);return Constructor;};}();function _classCallCheck(instance, Constructor) {if (!(instance instanceof Constructor)) {throw new TypeError("Cannot call a class as a function");}}miscPolyfillsForIE();
// main function
Collapse = function () {
function Collapse(container) {var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};_classCallCheck(this, Collapse);
var defaults = {
accordion: false,
initClass: 'collapse-init',
activeClass: 'panel-active',
heightClass: 'collapse-reading-height' };
this.settings = Object.assign({}, defaults, options);
this._container = container;
this._panels = container.querySelectorAll("details");
this.events = {
openingPanel: new CustomEvent('openingPanel'),
openedPanel: new CustomEvent('openedPanel'),
closingPanel: new CustomEvent('closingPanel'),
closedPanel: new CustomEvent('closedPanel') };
}
// Sets height of panel content
_createClass(Collapse, [{ key: '_setPanelHeight', value: function _setPanelHeight(panel) {
var contents = panel.querySelector("summary + *");
contents.style.height = contents.scrollHeight + "px";
}
// Removes height of panel content
}, { key: '_removePanelHeight', value: function _removePanelHeight(panel) {
var contents = panel.querySelector("summary + *");
contents.style.height = null;
}
//=== Open panel
}, { key: 'open', value: function open(panel) {
panel.dispatchEvent(this.events.openingPanel);
panel.open = true;
}
// Add height and active class, this triggers opening animation
}, { key: '_afterOpen', value: function _afterOpen(panel) {
this._setPanelHeight(panel);
panel.classList.add(this.settings.activeClass);
}
// Remove height on animation end since it's no longer needed
}, { key: '_endOpen', value: function _endOpen(panel) {
panel.dispatchEvent(this.events.openedPanel);
this._removePanelHeight(panel);
}
//=== Close panel, not toggling the actual [open] attr!
}, { key: 'close', value: function close(panel) {
panel.dispatchEvent(this.events.closingPanel);
this._afterClose(panel);
}
// Set height, wait a beat, then remove height to trigger closing animation
}, { key: '_afterClose', value: function _afterClose(panel) {var _this = this;
this._setPanelHeight(panel);
setTimeout(function () {
panel.classList.remove(_this.settings.activeClass);
_this._removePanelHeight(panel);
}, 100); //help, this is buggy and hacky
}
// Actually closes panel once animation finishes
}, { key: '_endClose', value: function _endClose(panel) {
panel.dispatchEvent(this.events.closedPanel);
panel.open = false;
}
//=== Toggles panel... just in case anyone needs this
}, { key: 'toggle', value: function toggle(panel) {
panel.open ? this.close(panel) : this.open(panel);
}
//=== Accordion closes all panels except the current passed panel
}, { key: 'openSinglePanel', value: function openSinglePanel(panel) {var _this2 = this;
this._panels.forEach(function (element) {
if (panel == element && !panel.open) {
_this2.open(element);
} else {
_this2.close(element);
}
});
}
//=== Opens all panels just because
}, { key: 'openAll', value: function openAll() {var _this3 = this;
this._panels.forEach(function (element) {
_this3.open(element);
});
}
//=== Closes all panels just in case
}, { key: 'closeAll', value: function closeAll() {var _this4 = this;
this._panels.forEach(function (element) {
_this4.close(element);
});
}
// Now put it all together
}, { key: '_attachEvents', value: function _attachEvents() {var _this5 = this;
this._panels.forEach(function (panel) {
var toggler = panel.querySelector("summary");
var contents = panel.querySelector("summary + *");
// On panel open
panel.addEventListener("toggle", function (e) {
var isReadingHeight = panel.classList.contains(_this5.settings.heightClass);
if (panel.open && !isReadingHeight) {
_this5._afterOpen(panel);
}
});
toggler.addEventListener("click", function (e) {
// If accordion, stop default toggle behavior
if (_this5.settings.accordion) {
_this5.openSinglePanel(panel);
e.preventDefault();
}
// On attempting close, stop default close behavior to substitute our own
else if (panel.open) {
_this5.close(panel);
e.preventDefault();
}
// On open, proceed as normal (see toggle listener above)
});
/*
transitionend fires once for each animated property,
but we want it to fire once for each click.
So let's make sure to watch only a single property
Note this makes complex animations with multiple transition-durations impossible
Sorry
*/
var propToWatch = '';
// On panel finishing open/close animation
contents.addEventListener("transitionend", function (e) {
// Ignore transitions from child elements
if (e.target !== contents) {
return;
}
// Set property to watch on first fire
if (!propToWatch) propToWatch = e.propertyName;
// If watched property matches currently animating property
if (e.propertyName == propToWatch) {
var wasOpened = panel.classList.contains(_this5.settings.activeClass);
wasOpened ? _this5._endOpen(panel) : _this5._endClose(panel);
}
});
});
} }, { key: 'init', value: function init()
{
// Attach functionality
this._attachEvents();
// If accordion, open the first panel
if (this.settings.accordion) {
this.openSinglePanel(this._panels[0]);
}
// For styling purposes
this._container.classList.add(this.settings.initClass);
return this;
} }]);return Collapse;}();
// initialize the accordion
var makeMePretty = document.querySelector(".collapse");
var accordion = new Collapse(makeMePretty, { accordion: true }).init();





