Hier ist ein sehr schönes und einfaches Script.
<style>
/* line in center */
.timeline {
position: relative;
width: 100%;
}
.timeline:before {
content: " ";
display: block;
position: absolute;
top: 0;
left: calc(50% - 2px);
width: 4px;
height: 100%;
background: red;
}
/* events */
.event {
position: absolute;
top: 0;
left: 0;
box-sizing: border-box;
width: 50%;
height: auto;
padding: 0 20px 0 0;
}
.event--right {
/* Move padding to other side.
* It is important that the padding does not change, because
* changing the height of an element (which padding can do)
* while layouting is not handled by the JavaScript. */
padding: 0 0 0 20px;
}
/* discs on timeline */
.event:after {
content: "";
display: block;
position: absolute;
top: calc(50% - 5px);
width: 0;
height: 0;
border-style: solid;
}
.event--left:after {
right: 0;
border-width: 10px 0 10px 20px;
border-color: transparent transparent transparent #de2d26;
}
.event--right:after {
left: 0;
border-width: 10px 20px 10px 0;
border-color: transparent #de2d26 transparent transparent;
}
/* event styling */
.event__body {
padding: 20px;
background: #de2d26;
color: rgba(255, 255, 255, 0.9);
}
</style>
<div class="timeline">
<div class="event">
<div class="event__body">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus varius sodales purus, id gravida ipsum accumsan quis. Donec ultrices orci quis ex consequat mollis. Etiam in gravida enim.
</div>
</div>
<div class="event">
<div class="event__body">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus varius sodales purus, id gravida ipsum accumsan quis. Donec ultrices orci quis ex consequat mollis. Etiam in gravida enim.
</div>
</div>
<div class="event">
<div class="event__body">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus varius sodales purus, id gravida ipsum accumsan quis. Donec ultrices orci quis ex consequat mollis. Etiam in gravida enim.
</div>
</div>
<div class="event">
<div class="event__body">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus varius sodales purus, id gravida ipsum accumsan quis. Donec ultrices orci quis ex consequat mollis. Etiam in gravida enim.
</div>
</div>
<div class="event">
<div class="event__body">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus varius sodales purus, id gravida ipsum accumsan quis. Donec ultrices orci quis ex consequat mollis. Etiam in gravida enim.
</div>
</div>
</div>
<script type="text/javascript">
// wrap in anonymous function to not pollute global namespace
(function() {
// find the minimium element in an array, and the index of it
function min(arr) {
var ind = -1, min = false, i;
for (i = 0; i < arr.length; ++i) {
if (min === false || arr[i] < min) {
ind = i;
min = arr[i];
}
}
return {
index: ind,
value: min
};
}
// position events within a single timeline container
function updateTimeline(timeline, minSpace = 10) {
var events = Array.from(timeline.querySelectorAll('.event')),
tops = [0, 0],
bottoms = [0, 0],
minTops = [0, 0];
// determine height of container, upper bound
timeline.style.height = events.reduce(function(sum, event) {
return sum + event.offsetHeight;
}, 0) + 'px';
// position events in timeline
events.forEach(function(event) {
// find highest point to put event at
// first put it with its top aligned to the lowest bottom - this will
// always yield a good solution, we check better ones later
var h = event.offsetHeight,
y = min(bottoms),
x = (y.index === 0 ? 0 : 50),
b = y.value + h,
m = (tops[1 - y.index] + bottoms[1 - y.index]) / 2;
event.className = 'event';
event.classList.add('event--' + (y.index === 0 ? 'left' : 'right'));
// try to squeeze element up as high as possible
// first try to put midpoint of new event just below the midpoint of
// the last placed event on the other side
if (m + minSpace - h / 2 > minTops[y.index]) {
y.value = m + minSpace - h / 2;
b = y.value + h;
}
// it would be even better if the top of the new event could move
// all the way up - this can be done if its midpoint is below the
// midpoint of the last event on the other side
if (minTops[y.index] + h / 2 > m + minSpace) {
y.value = minTops[y.index];
b = y.value + h;
}
// update tops and bottoms for current event
tops[y.index] = Math.ceil(y.value);
bottoms[y.index] = Math.ceil(b + minSpace);
minTops[y.index] = bottoms[y.index];
// update tops and bottoms for other side, as applicable
if (y.value + (h / 2) + minSpace > bottoms[1 - y.index]) {
tops[1 - y.index] = bottoms[1 - y.index];
minTops[1 - y.index] = bottoms[1 - y.index];
}
bottoms[1 - y.index] = Math.ceil(Math.max(
bottoms[1 - y.index],
y.value + (h / 2) + minSpace
));
// put event at correct position
event.style.top = y.value + 'px';
event.style.left = x + '%';
});
// set actual height of container
timeline.style.height = (Math.max.apply(null, bottoms) - minSpace) + 'px';
}
// position events within all timeline containers on the page
function updateAllTimelines(minSpace = 10) {
Array.from(document.querySelectorAll('.timeline'))
.forEach(function(timeline) {
updateTimeline(timeline, minSpace);
});
}
// initialize timeline by calling above functions
var space = 10;
updateAllTimelines(space);
window.addEventListener('resize', function() {
updateAllTimelines(space);
});
}());
</script>
Demo auf JSFiddle.