Skip to content
This repository was archived by the owner on Sep 2, 2022. It is now read-only.

Commit 6a31d24

Browse files
committed
Add a JS tiler to fix layout of tiles
The HTML now includes data attributes telling the JS tiler the grid location of each tile, it's size, and the gap between tiles (this data lives on the container, not each tile). The tiler then iterates over each tile and absolutely positions each tile accordingly.
1 parent a79213b commit 6a31d24

9 files changed

+123
-10
lines changed

source/_blog_grid.erb

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<div class="container--grid">
1+
<div class="container--grid" data-tile-layout data-tile-gap="6">
22
<%
33
tiler = Tiler.new(columns: 3, rows: articles.size * 2)
44
articles.each do |article|
@@ -11,12 +11,12 @@
1111
end
1212
end
1313
tiles = tiler.lay_tiles
14-
tiles.each do |row|
15-
row.each do |article_tile|
14+
tiles.each.with_index do |row, row_idx|
15+
row.each.with_index do |article_tile, column_idx|
1616
next if article_tile == Tiler::NoTile
1717
next if article_tile.is_a? Tiler::LargeTileReference
1818
%>
19-
<%= partial article_tile.contents.last, locals: { article: article_tile.contents.first } %>
19+
<%= partial article_tile.contents.last, locals: { article: article_tile.contents.first, row: row_idx, column: column_idx } %>
2020
<%
2121
end
2222
end

source/_blog_large_tile.erb

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<a class="blog-tile blog-large-tile" href="<%= article.url %>">
1+
<a class="blog-tile blog-large-tile" href="<%= article.url %>" data-tile data-tile-width="2" data-tile-height="2" data-tile-row="<%= row %>" data-tile-column="<%= column %>">
22
<div class="blog-large-tile__container">
33
<div class="blog-large-tile__article-image-container">
44
<img class="blog-large-tile__article-image" src="<%= article.data.main_image %>" alt="Article image">

source/_blog_medium_tile.erb

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<a class="blog-tile blog-medium-tile" href="<%= article.url %>">
1+
<a class="blog-tile blog-medium-tile" href="<%= article.url %>" data-tile data-tile-width="1" data-tile-height="2" data-tile-row="<%= row %>" data-tile-column="<%= column %>">
22
<div class="blog-medium-tile__container">
33
<div class="blog-medium-tile__top-content">
44
<div class="blog-medium-tile__title">

source/_blog_small_tile.erb

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<a class="blog-tile blog-small-tile" href="<%= article.url %>">
1+
<a class="blog-tile blog-small-tile" href="<%= article.url %>" data-tile data-tile-width="1" data-tile-height="1" data-tile-row="<%= row %>" data-tile-column="<%= column %>">
22
<div class="blog-small-tile__content">
33
<div class="blog-small-tile__title">
44
<%= truncate(article.title, length: 70) %>

source/assets/javascripts/application.js

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//= require tiler
12
//= require waypoints.min
23

34
var waypointElement = document.querySelector('[data-header-waypoint]');

source/assets/javascripts/tiler.js

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
(function() {
2+
"use strict";
3+
4+
var Tiler = function(container) {
5+
// fail early without doing anything if the container is missing...
6+
if (container === null) { return; }
7+
this.container = container;
8+
this.tiles = container.querySelectorAll('[data-tile]');
9+
// ... of if it has no tiles in it
10+
if (this.tiles.length === 0) { return; }
11+
this.layoutTiles();
12+
};
13+
14+
Tiler.prototype.layoutTiles = function() {
15+
var cellDimensions = this.cellDimensions(),
16+
topLeftPosition = this.topLeftPosition(),
17+
tileGap = this.tileGap(),
18+
containerHeight = 0,
19+
containerWidth = 0;
20+
for (var i = 0; i < this.tiles.length; i++) {
21+
var tile = this.tiles[i];
22+
this.layTile(tile, topLeftPosition, cellDimensions, tileGap);
23+
var tileBottom = (tile.offsetTop + tile.offsetHeight),
24+
tileRight = (tile.offsetLeft + tile.offsetWidth);
25+
26+
if (tileBottom > containerHeight) { containerHeight = tileBottom; }
27+
if (tileRight > containerWidth) { containerWidth = tileRight; }
28+
}
29+
this.container.style.width = containerWidth + 'px';
30+
this.container.style.height = containerHeight + 'px';
31+
};
32+
33+
Tiler.prototype.layTile = function(tile, topLeft, dimensions, tileGap) {
34+
var row = this.getNumberFromData(tile, 'tileRow'),
35+
column = this.getNumberFromData(tile, 'tileColumn'),
36+
width = this.getNumberFromData(tile, 'tileWidth', 1),
37+
height = this.getNumberFromData(tile, 'tileHeight', 1);
38+
39+
if ((row === undefined) || (column === undefined)) {
40+
// don't try to lay the tile if we don't know where it goes
41+
return;
42+
}
43+
44+
tile.style.position = 'absolute';
45+
tile.style.top = (topLeft.top + ((dimensions.height + tileGap) * row)) + 'px';
46+
tile.style.left = (topLeft.left + ((dimensions.width + tileGap) * column)) + 'px';
47+
tile.style.width = ((dimensions.width * width) + (tileGap * Math.max(width - 1, 0))) + 'px';
48+
tile.style.height = ((dimensions.width * width) + (tileGap * Math.max(width - 1, 0))) + 'px';
49+
};
50+
51+
Tiler.prototype.topLeftPosition = function() {
52+
if (this._topLeftPosition === undefined) {
53+
for (var i = 0; i < this.tiles.length; i++) {
54+
var tile = this.tiles[i];
55+
if ((tile.dataset.tileRow === "0") && (tile.dataset.tileColumn === "0")) {
56+
this._topLeftPosition = { top: tile.offsetTop, left: tile.offsetLeft };
57+
return this._topLeftPosition;
58+
}
59+
}
60+
}
61+
return this._topLeftPosition;
62+
};
63+
64+
Tiler.prototype.cellDimensions = function() {
65+
if (this._cellDimensions === undefined) {
66+
var aTile = this.container.querySelector('[data-tile]');
67+
if (aTile === null) {
68+
// if there are no tiles in the container, fail fast
69+
return;
70+
}
71+
var cellWidth = Math.ceil(aTile.offsetWidth / this.getNumberFromData(aTile, 'tileWidth', 1)),
72+
cellHeight = Math.ceil(aTile.offsetHeight / this.getNumberFromData(aTile, 'tileHeight', 1));
73+
this._cellDimensions = { width: cellWidth, height: cellHeight };
74+
}
75+
return this._cellDimensions;
76+
};
77+
78+
Tiler.prototype.tileGap = function() {
79+
if (this._tileGap === undefined) {
80+
this._tileGap = this.getNumberFromData(this.container, 'tileGap', 6);
81+
}
82+
return this._tileGap;
83+
};
84+
85+
Tiler.prototype.getNumberFromData = function(element, dataName, defaultValue) {
86+
var value = parseInt(element.dataset[dataName]);
87+
if (isNaN(value)) {
88+
return defaultValue;
89+
} else {
90+
return value;
91+
}
92+
}
93+
94+
var readyFunc = function() {
95+
var mql = window.matchMedia("(min-width: 660px)");
96+
if (mql.matches) {
97+
new Tiler(document.querySelector('[data-tile-layout]'));
98+
}
99+
};
100+
101+
if (document.readyState != 'loading') {
102+
readyFunc();
103+
} else {
104+
document.addEventListener("DOMContentLoaded", readyFunc);
105+
}
106+
}());

source/assets/stylesheets/styles/_blog_large_tile.scss

+3-1
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
text-decoration: none;
77

88
@include breakpoint($tablet-breakpoint) {
9-
float: left;
9+
display: inline-block;
1010
width: 66%;
11+
margin-bottom: 0px;
12+
padding-right: 0px;
1113
}
1214
}
1315

source/assets/stylesheets/styles/_blog_medium_tile.scss

+3-1
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
text-decoration: none;
77

88
@include breakpoint($tablet-breakpoint) {
9-
float: left;
9+
display: inline-block;
1010
width: 33%;
11+
margin-bottom: 0px;
12+
padding-right: 0px;
1113
}
1214
}
1315

source/assets/stylesheets/styles/_blog_small_tile.scss

+3-1
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
text-decoration: none;
77

88
@include breakpoint($tablet-breakpoint) {
9-
float: left;
9+
display: inline-block;
1010
width: 33%;
11+
margin-bottom: 0px;
12+
padding-right: 0px;
1113
}
1214
}
1315

0 commit comments

Comments
 (0)