HW Accelerated Accordion

Translations-only, height-changes-free accordion for all your mobile needs.

HTML

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0" />

<title>HW Accelerated Accordion for iPhone / iPod Touch / Android / iPad</title>

<script type="text/javascript" src="accordion.js?v1"></script>

<script type="text/javascript">
var myAccordion;

function loaded() {
    myAccordion = new accordion('accordion', true);
}

document.addEventListener('DOMContentLoaded', loaded, false);
</script>

</head>
<body>
<div id="accordion">
    <div>
        <div>First Law</div>
        <div>A robot may not injure a human being or, through inaction, allow a human being to come to harm.A robot may not injure a human being or, through inaction, allow a human being to come to harm.A robot may not injure a human being or, through inaction, allow a human being to come to harm.A robot may not injure a human being or, through inaction, allow a human being to come to harm.A robot may not injure a human being or, through inaction, allow a human being to come to harm.A robot may not injure a human being or, through inaction, allow a human being to come to harm.A robot may not injure a human being or, through inaction, allow a human being to come to harm.A robot may not injure a human being or, through inaction, allow a human being to come to harm.</div>
    </div>

    <div>
        <div>Second Law</div>
        <div>A robot must obey any orders given to it by human beings, except where such orders would conflict with the First Law.</div>
    </div>
    
    <div>
        <div>Third Law</div>
        <div>A robot must protect its own existence as long as such protection does not conflict with the First or Second Law.</div>
    </div>
    
    <div>
        <div>Zeroth Law</div>
        <div>A robot may not harm humanity, or, by inaction, allow humanity to come to harm.</div>
    </div>
    
    <div>
       ...

CSS

<style type="text/css" media="all">
body {
    padding:0;
    margin:0;
    border:0;
}

body {
    font-size:16px;
    -webkit-user-select:none;
    -webkit-text-size-adjust:none;
    font-family:georgia;
    line-height:140%;
}

#accordion {
    padding-bottom:200px;
}

#accordion > div {
    width:100%;
}

#accordion > div > div:first-child {
    -webkit-box-sizing:border-box;
    width:100%; height:48px;
    line-height:48px;
    overflow:hidden;
    white-space:no-wrap;
    padding:0 10px 0 40px;
    border-bottom:1px solid #888;
    border-top:1px solid #eee;
    background:url(arrow.png) 10px 50% no-repeat,
        -webkit-gradient(linear, 0 0, 0 100%, from(#f4f4f4), to(#a0a0a0));
    text-shadow:0 1px 0 #fff;
    font-weight:bold;
    -webkit-tap-highlight-color: rgba(0,0,0,0);
}

#accordion > div.acOpen > div:first-child {
    background-position:-1014px 50%;
}

#accordion > div > div:last-child {
    -webkit-box-sizing:border-box;
    width:100%;
    padding:10px;
    background:#e3e3e3;
    font-style:italic;
}

/* The followings are the only styles needed by the accordion */
#accordion {
    position:relative;
}

#accordion > div {
    position:absolute;
    overflow:hidden;
}

#accordion > div > div:first-child {
    position:relative;
    z-index:11 !important;        /* z-index can be any value greater than "div:last-child" z-index */
}

#accordion > div > div:last-child {
    position:absolute;
    z-index:10;
}
</style>

JavaScript

(function(){
function accordion (el, scroll) {
    var that = this,
        top = 0,
        accordions, i, l;

    that.el = document.getElementById(el);
    that.scroll = scroll===true;

    // Get the wrappers
    that.wrappers = document.querySelectorAll('#' + el + ' > div');

    // Get the headers
    that.handles = document.querySelectorAll('#' + el + ' > div > div:first-child');

    // Get the accordions
    accordions = document.querySelectorAll('#' + el + ' > div > div:last-child');

    // Assign default styles
    for (i=0, l=that.wrappers.length; i<l; i++) {
        that.wrappers[i].style.top = top + 'px';
        that.wrappers[i].style.webkitTransitionProperty = '-webkit-transform';
        that.wrappers[i].style.webkitTransitionTimingFunction = 'cubic-bezier(0,0,0.25,1)';
        that.wrappers[i].style.webkitTransitionDuration = '0';
        that.wrappers[i].style.webkitTransform = translateOpen + '0,0' + translateClose;
        that.wrappers[i]._pos = i;
        top += that.wrappers[i].offsetHeight;
    }
    
    that.el.style.height = top + 'px';

    for (i=0, l=that.handles.length; i<l; i++) {
        that.handles[i].addEventListener('click', that, false);
    }

    for (i=0, l=accordions.length; i<l; i++) {
        accordions[i].style.webkitTransitionProperty = '-webkit-transform';
        accordions[i].style.webkitTransitionTimingFunction = 'cubic-bezier(0,0,0.25,1)';
        accordions[i].style.webkitTransitionDuration = '0';
        accordions[i].style.webkitTransform = translateOpen + '0,0' + translateClose;
    }
}

accordion.prototype = {
    handleEvent: function (e) {
        if (e.type == 'click') {
            this.toggle(e);
        } else if (e.type == 'webkitTransitionEnd') {
            this.transitionEnd(e);
        }
    },
    
    toggle: function (e) {
        var that = this,
            targetWrapper = e.currentTarget.parentNode,
            targetHandle = targetWrapper.querySelector('div:first-child'),
           ...