I have an masonry isotope grid that has n number of rows with two column sizes: 160px by 160px and 320px by 320px and I'd like to assign different styles to the first and last element of each row. My rows could have anywhere from 4 elements to 7 elements. I've been struggling with this for a bit, and wondering if it's possible.
HTML
<div id="grid" style="position: relative; overflow: hidden; height: 960px;"
class="isotope">
<div class="one_by_one">
<img class="thumb" src="https://s3.amazonaws.com/stitch-images/products/gucci.png"
/>
</div>
<div class="one_by_one">
<img class="thumb" src="https://s3.amazonaws.com/stitch-images/products/gucci.png"
/>
</div>
<div class="one_by_one">
<img class="thumb" src="https://s3.amazonaws.com/stitch-images/products/gucci.png"
/>
</div>
<div class="one_by_one">
<img class="thumb" src="https://s3.amazonaws.com/stitch-images/products/gucci.png"
/>
</div>
<div class="one_by_one">
<img class="thumb" src="https://s3.amazonaws.com/stitch-images/products/gucci.png"
/>
</div>
<div class="one_by_one">
<img class="thumb" src="https://s3.amazonaws.com/stitch-images/products/gucci.png"
/>
</div>
<div class="two_by_two">
<img class="thumb" src="https://s3.amazonaws.com/stitch-images/products/gucci.png"
/>
</div>
<div class="one_by_one">
<img class="thumb" src="https://s3.amazonaws.com/stitch-images/products/gucci.png"
/>
</div>
<div class="one_by_two">
<img class="thumb" src="https://s3.amazonaws.com/stitch-images/products/gucci.png"
/>
</div>
<div class="one_by_one">
<img class="thumb" src="https://s3.amazonaws.com/stitch-images/products/gucci.png"
/>
</div>
<div class="one_by_one">
<img class="thumb" src="https://s3.amazonaws.com/stitch-images/products/gucci.png"
/>
</div>
<div class="one_by_one">
<img class="thumb" src="https://s3.amazonaws.com/stitch-images/products/gucci.png"
/>
</div>
<div class="two_by_two">
<img class="thumb" src="https://s3.amazonaws.com/stitch-images/products/gucci.png"
/>
</div>
<div class="one_by_one">
<img class="thumb" src="https://s3.amazonaws.com/stitch-images/products/gucci.png"
/>
</div>
<div class="one_by_one">
<img class="thumb" src="https://s3.amazonaws.com/stitch-images/products/gucci.png"
/>
</div>
<div class="one_by_one">
<img class="thumb" src="https://s3.amazonaws.com/stitch-images/products/gucci.png"
/>
</div>
<div class="one_by_two">
<img class="thumb" src="https://s3.amazonaws.com/stitch-images/products/gucci.png"
/>
</div>
<div class="one_by_one">
<img class="thumb" src="https://s3.amazonaws.com/stitch-images/products/gucci.png"
/>
</div>
<div class="one_by_one">
<img class="thumb" src="https://s3.amazonaws.com/stitch-images/products/gucci.png"
/>
</div>
<div class="two_by_two">
<img class="thumb" src="https://s3.amazonaws.com/stitch-images/products/gucci.png"
/>
</div>
<div class="one_by_one">
<img class="thumb" src="https://s3.amazonaws.com/stitch-images/products/gucci.png"
/>
</div>
<div class="one_by_one">
<img class="thumb" src="https://s3.amazonaws.com/stitch-images/products/gucci.png"
/>
</div>
<div class="one_by_two">
<img class="thumb" src="https://s3.amazonaws.com/stitch-images/products/gucci.png"
/>
</div>
<div class="one_by_one">
<img class="thumb" src="https://s3.amazonaws.com/stitch-images/products/gucci.png"
/>
</div>
</div>
CSS
#grid {
margin:auto;
margin-top:55px;
margin-bottom:200px;
width:1140px
}
#grid .thumb {
width:97%;
height:97%
}
#grid .one_by_one {
width:160px;
height:160px;
background:url(https://s3.amazonaws.com/stitch-images/assets/cell_1x1.png);
cursor:pointer
}
#grid .one_by_two {
width:160px;
height:320px;
background:url(https://s3.amazonaws.com/stitch-images/assets/cell_1x2.png);
cursor:pointer
}
#grid .two_by_two {
width:320px;
height:320px;
background:url(https://s3.amazonaws.com/stitch-images/assets/cell_2x2.png);
cursor:pointer
}
JS
$("#grid").isotope masonry: layoutMode: 'fitRows'
View my Jsfiddle http://jsfiddle.net/TDma4/
Here's one way to approach the problem.
Working Solution: http://jsbin.com/ufuleb/21/
CSS
.first, .last {
border:2px dashed blue;
opacity:.75;
}
JavaScript
$("#grid").isotope(
{ layoutMode : 'fitRows' });
// Last div element
$("#grid div:last-child").addClass("last");
var maxWidth = 0;
// Use max to handle last div
$("#grid div").each(function (i) {
matrix = matrixToArray($(this).css("-transform"));
// identify first elements
if ( parseInt(matrix[4],10) == 0 ) {
$(this).addClass("first");
}
// identify last elements
if ( parseInt(matrix[4],10) > parseInt(maxWidth,10) ) {
maxWidth = matrix[4];
} else {
$(this).prev().addClass("last");
maxWidth = 0;
}
});
// Util function for parsing -webkit-transform
function matrixToArray(matrix) {
return matrix.substr(7, matrix.length-8).split(', ');
}
Loop through each div
and track the current xT
value (CSS -webkit-transform). Whenever the maximum value is passed, just update the previous value which should be the last element of each row. The very last element is handled with :last-child
. Note that this solution also handles the overall #grid
width changing.
Example Output
This could probably be optimized further but at least provides a starting point.
I got some help from this answer: https://stackoverflow.com/a/5968313/1085891
For reference:
Isotope has a itemPositionDataEnabled
option that exposes the position of each element. Using this, along with a onLayout
handler, you can compute the first and last elements of each row (demo):
$('#grid').isotope({
itemPositionDataEnabled: true,
onLayout: function (elems, instance) {
var items, rows, numRows, row, prev, i;
// gather info for each element
items = elems.map(function () {
var el = $(this), pos = el.data('isotope-item-position');
return {
x: pos.x,
y: pos.y,
w: el.width(),
h: el.height(),
el: el
};
});
// first pass to find the first and last items of each row
rows = [];
i = {};
items.each(function () {
var y = this.y, r = i[y];
if (!r) {
r = {
y: y,
first: null,
last: null
};
rows.push(r);
i[y] = r;
}
if (!r.first || this.x < r.first.x) {
r.first = this;
}
if (!r.last || this.x > r.last.x) {
r.last = this;
}
});
rows.sort(function (a, b) { return a.y - b.y; });
numRows = rows.length;
// compare items for each row against the previous row
for (prev = rows[0], i = 1; i < numRows; prev = row, i++) {
row = rows[i];
if (prev.first.x < row.first.x &&
prev.first.y + prev.first.h > row.y) {
row.first = prev.first;
}
if (prev.last.x + prev.last.w > row.last.x + row.last.w &&
prev.last.y + prev.last.h > row.y) {
row.last = prev.last;
}
}
// assign classes to first and last elements
elems.removeClass('first last');
$.each(rows, function () {
this.first.el.addClass('first');
this.last.el.addClass('last');
});
}
});
Update: Fixed algorithm based on JSuar's feedback
Update #2: Fixed an issue when items are taller than 2 rows
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With