<div class="container mt-5">
<div class="text-center text-white mb-5">
<h1 class="display-4">Accordion</h1>
<small>Note: Your browser must support ES6 and async/await.</small>
</div>
<div class="row justify-content-center">
<div class="col-12 col-sm-10">
<div id="accordion">
<!-- First card -->
<div class="card">
<div class="card-header">
<button id="0" class="btn btn-link" href="#">
<strong>React</strong>
</button>
</div>
<div class="collapse show">
<div class="card-block">
In computing, React (sometimes styled React.js or ReactJS) is an open-source JavaScript library for building user interfaces.
<br><br>
It is maintained by Facebook, Instagram and a community of individual developers and corporations. As of July 2017, according to JavaScript analytics service Libscore, websites such as Airbnb, Buffer, Bleacher Report, Feedly, HelloSign, Imgur, Netflix, SeatGeek and others use React.
<br><br>
React allows developers to create large web-applications that use data that can change over time, without reloading the page. It aims primarily to provide speed, simplicity and scalability. React processes only user interfaces in applications. This corresponds to View in the Model-View-Controller (MVC) template, and can be used in combination with other JavaScript libraries or frameworks in MVC, such as AngularJS.
</div>
</div>
</div>
<!-- Second card -->
<div class="card">
<div class="card-header">
<button id="1" class="btn btn-link" href="#">
<strong>History</strong>
</button>
</div>
<div class="collapse">
<div class="card-block">
React was created by Jordan Walke, a software engineer at Facebook. He was influenced by XHP, an HTML component framework for PHP. It was first deployed on Facebook's newsfeed in 2011 and later on Instagram.com in 2012. It was open-sourced at JSConf US in May 2013. React Native, which enables native Android, iOS, and UWP development with React, was announced at Facebook's React.js Conf in February 2015 and open-sourced in March 2015. On April 18, 2017, Facebook announced React Fiber, a new core algorithm of React framework library for building user interfaces. React Fiber will become the foundation of any future improvements and feature development of the React framework.
</div>
</div>
</div>
<!-- Third card -->
<div class="card">
<div class="card-header">
<button id="2" class="btn btn-link" href="#">
<strong>Unidirectional Data Flow</strong>
</button>
</div>
<div class="collapse">
<div class="card-block">
Properties, a set of immutable values, are passed to a component's renderer as properties in its HTML tag. A component cannot directly modify any properties passed to it, but can be passed callback functions that do modify values. This mechanism's promise is expressed as "properties flow down; actions flow up".
</div>
</div>
</div>
<!-- Fourth card -->
<div class="card">
<div class="card-header">
<button id="3" class="btn btn-link" href="#">
<strong>Virtual DOM</strong>
</button>
</div>
<div class="collapse">
<div class="card-block">
Another notable feature is the use of a "virtual Document Object Model", or "virtual DOM". React creates an in-memory data structure cache, computes the resulting differences, and then updates the browser's displayed DOM efficiently. This allows the programmer to write code as if the entire page is rendered on each change, while the React libraries only render sub components that actually change.
</div>
</div>
</div>
<!-- Fifth card -->
<div class="card">
<div class="card-header">
<button id="4" class="btn btn-link" href="#">
<strong>JSX</strong>
</button>
</div>
<div class="collapse">
<div class="card-block">
React components are typically written in JSX, a JavaScript extension syntax allowing quoting of HTML and using HTML tag syntax to render subcomponents. This is a React-specific grammar extension to JavaScript like the now-defunct E4X. HTML syntax is processed into JavaScript calls of the React framework. Developers may also write in pure JavaScript. JSX is similar to another extension syntax created by Facebook for PHP, XHP. JSX looks like regular HTML.
<br><br>
An example of JSX code:
<pre class="mb-0"><code>
class App extends React.Component {
render() {
return (
<div>
<Header />
<Content />
<Footer />
</div>
);
}
}
</code></pre>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
body {
background: #343436;
}
.card > div:nth-child(2) {
will-change: height;
}
class Accordion {
constructor (elements) {
this.state = {
elements: elements,
expanded: elements.indexOf(document.getElementsByClassName('show')[0]),
animating: false
};
this.handleClick = this.handleClick.bind(this);
}
setState (nextState) {
return new Promise((resolve) => {
Object.assign(this.state, nextState);
resolve();
});
}
setElementHeight (element, height) {
return new Promise((resolve) => {
Object.assign(element.style, { height: `${height}px` });
resolve();
});
}
getElementHeight (element) {
return new Promise((resolve) => {
const offsetHeight = element.offsetHeight;
const { marginTop, marginBottom } = window.getComputedStyle(element);
const height = [
offsetHeight,
parseInt(marginTop),
parseInt(marginBottom)
].reduce((acc, val) => acc + val, 0);
resolve(height);
});
}
addElementClass (element, classes) {
return new Promise((resolve) => {
element.classList.add(...classes);
resolve();
});
}
removeElementClass (element, classes) {
return new Promise((resolve) => {
element.classList.remove(...classes);
resolve();
});
}
resetElementStyle (element) {
return new Promise((resolve) => {
Object.assign(element, { style: '' });
resolve();
});
}
forceDOMReflow (element) {
return new Promise((resolve) => {
resolve(element.offsetHeight);
});
}
animateCollapse (id) {
return new Promise(async (resolve) => {
const element = this.state.elements[id];
const height = await this.getElementHeight(element);
await this.setState({ animating: true });
await this.addElementClass(element, ['collapsing']);
await this.removeElementClass(element, ['collapse', 'show']);
await this.setElementHeight(element, height);
await this.forceDOMReflow(element);
await this.setElementHeight(element, 0);
window.setTimeout(async () => {
await this.addElementClass(element, ['collapse']);
await this.removeElementClass(element, ['collapsing']);
await this.resetElementStyle(element);
await this.setState({ animating: false });
resolve();
}, 350);
});
}
animateExpand (id) {
return new Promise(async (resolve) => {
const element = this.state.elements[id];
let height;
await this.setState({ animating: true });
await this.addElementClass(element, ['collapsing']);
await this.removeElementClass(element, ['collapse']);
await this.setElementHeight(element, 0);
height = await this.getElementHeight(element.children[0]);
await this.setElementHeight(element, height);
window.setTimeout(async () => {
await this.addElementClass(element, ['collapse', 'show']);
await this.removeElementClass(element, ['collapsing']);
await this.resetElementStyle(element);
await this.setState({ animating: false });
resolve();
}, 350);
});
}
async handleClick (event) {
const id = parseInt(event.currentTarget.id);
if (this.state.animating) return;
if (this.state.expanded === id) {
await this.animateCollapse(id);
await this.setState({ expanded: -1 });
} else if (this.state.expanded === -1) {
await this.animateExpand(id);
await this.setState({ expanded: id });
} else {
await Promise.all([
this.animateCollapse(this.state.expanded),
this.animateExpand(id)
]);
await this.setState({ expanded: id });
}
}
main () {
this.state.elements
.map(element => element.previousElementSibling)
.map(element => element.children[0])
.forEach((element) => {
element.addEventListener('click', this.handleClick);
});
}
}
// Accordion divs
const accordionDivs = Array
.from(document.getElementById('accordion').children)
.map(element => element.children[1]);
const accordion = new Accordion(accordionDivs);
accordion.main();