avris-columnist
Version:
Simple and lightweight multi-column design
105 lines (82 loc) • 2.87 kB
JavaScript
module.exports = class Columnist {
constructor($container) {
this.$container = $container;
this.timer = null;
this.columnCount = null;
}
layout() {
const columns = this._countColumns();
// first row can get messed up if column count changes and they never get reset. let's just force it
if (columns !== this.columnCount) {
[...this.$container.children].forEach($col => {
$col.style.marginTop = null;
});
}
this.columnCount = columns;
let interventions = 0;
const redraw = () => {
let hasChanges = false;
const margins = {};
let i = 0;
[...this.$container.children].forEach($col => {
const style = this._getStyle($col);
if (!style) {
return;
}
const extraHeight = parseInt(style.marginBottom.slice(0, -2), 10)
+ parseInt(style.paddingBottom.slice(0, -2), 10)
+ parseInt(style.borderBottomWidth.slice(0, -2), 10);
margins[i] = Math.max(0, $col.offsetHeight - $col.children[0].offsetHeight - extraHeight);
if (margins[i - columns] !== undefined) {
const mT = `-${margins[i - columns]}px`.replace('-0px', '0px');
if ($col.style.marginTop !== mT) {
$col.style.marginTop = mT;
hasChanges = true;
}
}
i++;
});
interventions++;
if (hasChanges && interventions < 100) {
window.requestAnimationFrame(redraw);
}
}
window.requestAnimationFrame(redraw);
return this;
}
_countColumns() {
const columnWidth = this._getColumnWidth();
return columnWidth ? Math.floor(this.$container.offsetWidth / columnWidth) : 1;
}
_getColumnWidth() {
for (let $col of [...this.$container.children]) {
if (this._getStyle($col)) {
return $col.offsetWidth - 1;
}
}
return null;
}
_getStyle($el) {
const style = $el.currentStyle || window.getComputedStyle($el);
if (style.display === 'none') {
return null;
}
return style;
}
start(interval = 300) {
this.timer = setInterval(() => this.layout(), interval);
return this;
}
destroy() {
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
}
[...this.$container.children].forEach(($col, i) => {
if (this._getStyle($col)) {
$col.style.marginTop = null;
}
});
return this;
}
}