Several days ago I tried to incorporate Isotope into my site, and combine it with Fancybox 2 to create a nice looking gallery with filtering/sorting possibilities. Since many pages contain a lot of images, I prefer to show and append each image directly after it has finished loading rather than showing a loader until all images have been loaded.
In order to achieve this, I came up with this piece of code so far:
$( function() {
// init isotope
var $container = $('.isotope_container').isotope({
itemSelector: '.isotope_image',
layoutMode: 'fitRows',
transitionDuration: '0.7s'
});
// reveal initial images
$container.isotopeImagesReveal($('#images_container').find('.isotope_image'));
});
$.fn.isotopeImagesReveal = function( $items ) {
var iso = this.data('isotope');
var itemSelector = iso.options.itemSelector;
// hide by default
$items.hide();
// append to container
this.append( $items );
$items.imagesLoaded().progress( function( imgLoad, image ) {
// get item
// image is imagesLoaded class, not <img>, <img> is image.img
var $item = $( image.img ).parents( itemSelector );
// un-hide item
$item.show();
// isotope does its thing
iso.appended( $item );
});
return this;
};
});
The code above already does to some extent what I am after. It 'pulls' the images initially present in the <div id='images_container'>, and appends an item to isotope once an image has finished loading. The problem however is that the code appends and shows images in the order they finish loading (image loaded first becomes #1 in grid, then #2 etc.). Adding the sortby parameter to the isotope options does not work, as the images are not loaded at the point isotope is initiated. Re-sorting them with isotope after all images have been appended this way is possible, but looks very messy.
What I would like to achieve is to have the images loaded in the order they are present in the <div id='images_container'>. Chronologically when it comes to the original document order so to say. So basically start loading image 1, show and append it once it has been loaded. Then proceed to image 2, show and append it after loading, etc. until all images have been processed.
I think the solution is to alter the $.fn.isotopeImagesReveal function to process each of the div items in chronological order, but I can't figure out how.
Update: added example of current code. http://codepen.io/micksanders/pen/KwXmwO
Firstly you need to add a sorting field to your markup. I added a data-order="xxxx" attribute to all elements with the .isotope_image class, like so:
<div class='isotope_image' data-order="1">
<p>1</p>
<img src='http://lorempixel.com/600/600/sports'>
</div>
...
Then you need to add that sorting field to your isotope initialisation code... both the getSortData and sortBy options are needed:
var $container = $('.isotope_container').isotope({
itemSelector: '.isotope_image',
layoutMode: 'fitRows',
transitionDuration: '0.7s',
getSortData: {
order: "[data-order]"
},
sortBy: 'order'
});
Finally at the end of your imagesLoaded.progress() function, use iso.insert($item) instead of iso.appended($item).
Here's the full amended code:
http://codepen.io/BenStevens/pen/MYEmBb
$( function() {
var $container = $('#container').isotope({
itemSelector: '.item',
masonry: {
columnWidth: 200
}
});
$('#load-images').click( function() {
var $items = getItems();
$container.isotopeImagesReveal( $items );
});
});
$.fn.isotopeImagesReveal = function( $items ) {
var iso = this.data('isotope');
var itemSelector = iso.options.itemSelector;
// hide by default
$items.hide();
// append to container
this.append( $items );
$items.imagesLoaded().progress( function( imgLoad, image ) {
// get item
// image is imagesLoaded class, not <img>, <img> is image.img
var $item = $( image.img ).parents( itemSelector );
// un-hide item
$item.show();
// isotope does its thing
iso.appended( $item );
});
return this;
};
function randomInt( min, max ) {
return Math.floor( Math.random() * max + min );
}
function getItem() {
var width = randomInt( 150, 400 );
var height = randomInt( 150, 250 );
var item = '<div class="item">'+
'<img src="http://lorempixel.com/' +
width + '/' + height + '/nature" /></div>';
return item;
}
function getItems() {
var items = '';
for ( var i=0; i < 12; i++ ) {
items += getItem();
}
// return jQuery object
return $( items );
}
See reveal each image as they load
Related
Hello everyone and I hope you're doing well. I am using Isotope and below you can see the JavaScript that I have written. I find it impossible to center li elements if they are the Isotope elements. To see what I mean by centering, please see the images below. I've managed to center the whole Isotope to the screen but I need the elements to be centered too and not just float to the left side.
Let's start with my script code:
<script>
$(document).ready(function(e) {
$(".ullist li").addClass('element-item');
});
</script>
<script>
$(document).ready(function(e) {
// external js: isotope.pkgd.js
// init Isotope
var $grid = $('.grid').isotope({
itemSelector: '.element-item',
//layoutMode: 'fitRows',
});
//$('.grid').isotope({ layoutMode : 'fitRows' });
// filter functions
var filterFns = {
// show if number is greater than 50
numberGreaterThan50: function() {
var number = $(this).find('.number').text();
return parseInt( number, 10 ) > 50;
},
// show if name ends with -ium
ium: function() {
var name = $(this).find('.name').text();
return name.match( /ium$/ );
}
};
// bind filter button click
$('#filters').on( 'click', 'a', function() {
var filterValue = $( this ).attr('data-filter');
// use filterFn if matches value
filterValue = filterFns[ filterValue ] || filterValue;
$grid.isotope({ filter: filterValue });
});
// change is-checked class on buttons
$('.secmenu ul a').each( function( i, buttonGroup ) {
var $buttonGroup = $( buttonGroup );
$buttonGroup.on( 'click', 'a', function() {
$buttonGroup.find('.is-checked').removeClass('is-checked');
$( this ).addClass('is-checked');
});
});});
</script>
<script>
$(function(){
var $container = $('.grid'),
$body = $('body'),
colW = 20,
columns = null;
$container.isotope({
// disable window resizing
resizable: true,
masonry: {
columnWidth: colW,
isFitWidth: true
}
});
$(window).smartresize(function(){
// check if columns has changed
var currentColumns = Math.floor( ( $body.width() -10 ) / colW );
if ( currentColumns !== columns ) {
// set new column count
columns = currentColumns;
// apply width to container manually, then trigger relayout
$container.width( columns * colW )
.isotope('reLayout');
}
}).smartresize(); // trigger resize to set container width
});
</script>
Basic HTML structure:
<ul class="ullist grid">
<li> ... </li>
<li> ... </li>
<li> ... </li>
<li> ... </li>
</ul>
Isotope works pretty well with no issues (so far). This is my current layout:
And this is the desired layout.
I even checked here and tried to use David DeSandro's repository but with no success. So please guys can you help me here to achieve the layout above?
Thank you all in advance.
jQuery isotope centering
You might need to add some extra markup and increase the number of columns that Isotope is fitting items into.
<ul class="ullist grid">
<li><span class="box">...</span></li>
<li><span class="box">...</span></li>
<li><span class="box">...</span></li>
<li><span class="box">...</span></li>
</ul>
You'll then set the number of columns to something that is divisible by both 2 and 3. If you create 6, you can have the first 6 li items span two columns, and the final two span 3:
Then, use CSS to position your .boxes within the li items.
This gets tricky if you don't know how many 'stray' items you'll have at the end - you might need to use javascript to figure this out, append a class (such as .span-2, .span-3, .span-6
The best solution for this is using the flex-box if you not concerned to support for old browsers(below IE8). Check this pen for flex-box solution codepen Link.
ul {
width: 600px;
list-style: none;
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
}
li {
width: calc((100% / 3) - 60px);
height: 200px;
background-color: #d91c5c;
margin: 30px;
}`
so i am playing around with the js-isotope library. works great so far, but have an issue on a specific task.
i have a grid which represents brands for clothing. the brands are organized by the starting letter.
so it looks like this: http://towa-online.at/sagmeister/marken/
i created 2 isotope-objects:
alphabet-grid: grid-items containing the list of brands
var $container_alphabet = $( '.sagmeister-brand-grid' ).isotope({
itemSelector: '.grid-item',
masonry: {
gutter: 40,
columnWidth: 320
},
filter : '*',
animationOptions: {
duration: 750,
easing : 'linear',
queue : false
}
});
brand-grid: each list inside an alphabet
var $container_brands = $( 'ul.brand-by-letter' ).isotope({
itemSelector : '.brand-name',
layoutMode : 'vertical',
filter : function(){
var $this = $( this );
var result_query = search_query ? $this.text().match( search_query ) : true;
var result_cat = search_cat ? $this.is( search_cat ) : true;
return result_query && result_cat;
},
animationOptions: {
duration: 750,
easing : 'linear',
queue : false
}
});
what i wanna do is, after filtering the brands ( that works fine ), i am checking on the "arrangeComplete"-event, if maybe some letters are emtpy. if so, i add a class "no-items" to the grid-item from the alphabet-grid and at the end i am triggering isotope for that.
$container_brands.on( 'arrangeComplete', function( event, filteredItems ){
var $grid_item = $( '#' + event.currentTarget.id ).parent();
var has_items = ( filteredItems.length > 0 );
if ( has_items ){
$grid_item.removeClass( 'no-items' ).addClass( 'has-items' );
} else {
$grid_item.removeClass( 'has-items' ).addClass( 'no-items' );
}
if ( $grid_item.is( ':last-child' ) )
$container_alphabet.isotope();
});
it seems that the triggering doesn't work every time, but just from time to time.
anyone thought on this one?
so, i was able to find a solution myself. thanks anyway, it helped me to describe the problem and have an another look at it :)
my solution was to alter the structure a little bit and then I put in a custom filter-function for the grid-items for the alphabet, which is checking if all children are filtered out or not. if so, hide the alphabet-item :)
nice start for the week, like that ^^
cheers
PS: if you need a more detailed explanation, feel free to ask. would be glad to help!
I cant figure out how to have the initial fadein animation when masonry is initialized at first page load exactly like the original plugin's page:
http://masonry.desandro.com/
Any help suggestions?
I used the imagesLoaded progress deferred API and Masonry's appended method.
$('#load-images').click( function() {
var $items = getItems();
// hide by default
$items.hide();
// append to container
$container.append( $items );
$items.imagesLoaded().progress( function( imgLoad, image ) {
// get item
// image is imagesLoaded class, not <img>, <img> is image.img
var $item = $( image.img ).parents('.item');
// un-hide item
$item.show();
// masonry does its thing
$container.masonry( 'appended', $item );
});
});
Items are first hidden, then appended to the container. After each item is loaded, it un-hidden then gets revealed by Masonry.
Source: http://codepen.io/desandro/pen/iHevA
So if you check out: http://uniplaces.micrositesonline.info/blog/cities/ you'll see the isotope masonry plugin in action. The entire theme is from https://themetrust.com/demos/swell/. The issue is, on our site, the isotope plugin loads in a strange manner, the div that contains the masonry images fails to adjust the height properly and thus, it sometimes gets cut off. You can typically replicate this by reloading the page once it has loaded.
The code containing the js is in 'themetrust.js':
///////////////////////////////
// Project Filtering
///////////////////////////////
function projectFilterInit() {
if( jQuery('#filter-nav a').length > 0 ) {
jQuery('#filter-nav a').click(function(){
var selector = jQuery(this).attr('data-filter');
jQuery('#projects.thumbs').isotope({
filter: selector,
hiddenStyle : {
opacity: 0,
scale : 1
}
});
if ( !jQuery(this).hasClass('selected') ) {
jQuery(this).parents('#filter-nav').find('.selected').removeClass('selected');
jQuery(this).addClass('selected');
}
return false;
});
} // if() - Don't have this element on every page on which we call Isotope
}
///////////////////////////////
// Project thumbs
///////////////////////////////
function isotopeInit() {
setColumns();
gridContainer.isotope({
resizable: true,
layoutMode: 'masonry',
masonry: {
columnWidth: colW
}
});
jQuery(".thumbs .small").css("visibility", "visible");
}
///////////////////////////////
// Isotope Grid Resize
///////////////////////////////
function setColumns()
{
var columns;
var gw = gridContainer.width();
var ww = jQuery(window).width()
if(ww<=700){
columns = 1;
}else if(ww<=870){
columns = 2;
}else{
columns = 3;
}
colW = Math.floor(gw / columns);
jQuery('.thumbs .small').each(function(id){
jQuery(this).css('width',colW+'px');
});
jQuery('.thumbs .small').show();
}
function gridResize() {
setColumns();
gridContainer.isotope({
resizable: false,
layoutMode: 'masonry',
masonry: {
columnWidth: colW
}
});
}
///////////////////////////////
// Center Home Banner Text
///////////////////////////////
function centerHomeBannerContent() {
var bannerContent = jQuery('.home #banner-content');
var bannerContentTop = (windowHeightAdjusted/2) - (jQuery('.home #banner-content').actual('height')/2);
bannerContent.css('margin-top', bannerContentTop+'px');
bannerContent.show();
}
///////////////////////////////
// Initialize
///////////////////////////////
jQuery.noConflict();
jQuery(document).ready(function(){
jQuery(".content-area").fitVids();
mmenu_nav();
jQuery('#video-background').height(windowHeight);
video_resize();
if(!isMobile()){
getVideoBGs();
}
jQuery('body').imagesLoaded(function(){
projectFilterInit();
isotopeInit();
centerHomeBannerContent();
});
jQuery(window).smartresize(function(){
gridResize();
//full_width_images();
video_resize();
mmenu_nav();
centerHomeBannerContent()
});
//Set Down Arrow Button
jQuery('#down-button').click(function(){
jQuery.scrollTo( ".middle", {easing: 'easeInOutExpo', duration: 1000} );
});
//pull_out_the_quote();
//full_width_images();
});
We've tried modifying it to no avail, removing and tweeking, but nothing seems to work. At this point we think it make be the css transition initialized by the class isotope-item, so we removed it, which seems to work but we are not entirely sure why. Is it possible to retain the transitions and get the isotope plugin to behave with them reliably?
WOOOO that theme is mental to say the least.
There are so many HTTP request's it's not surprising it's failing to load some scripts within the exec time.
Right because this is a theme and we don't want to mess about with stuff to much for updating sake's I would recommend using autoptomize
It will compress and conjoin all your scripts and css files into one nice neat and easy to download file so that no render blocking or partial loading occur's
Just reviewed your site on http://uniplaces.micrositesonline.info/blog/cities/, probably "jquery.isotope.js" file is missing on your directly. Make sure the presence of "jquery.isotope.js" at JS folder. lets try
Currently I'm using AJAX to pull in data from our webservice. The issue I'm having is that it doesn't want to load the data into the masonry layout, everything in .social_block floats left one under the next (and I haven't set any float for these). So masonry isn't working :(
I wanted the following to happen: Load initial items from webservice in the masonry layout and on "infinite" scroll it would make the paged request from the webservice to append new items into the masonry layout.
So the questions are as follows:
- Why aren't my webservice items loading using masonry and just loading to the left of page?
- How can I use infinite scroll with my existing AJAX request so it pulls in new data into the masonry layout using the paging code I have in place as (first request load http://example.com/ automatically, second request load http://example.com/1 on first infinite scroll, third request http://example.com/2 on second infinite scroll, etc.)?
As an added note, if I add in an alert rather than console.log before line $container.imagesLoaded(function(){ it seems to slow things down but then loads the initial request into masonry format - weird!
<div id="container">
</div>
<p id="loadmore">
<button id="more">'Load More!</button>
</p>
<script src="js/vendors/jquery/jquery-1.10.0.min.js"></script>
<script src="js/vendors/masonry/jquery.masonry.min.js"></script>
<script src="js/vendors/jquery/jquery.infinitescroll.min.js"></script>
<script>
$(function(){
var $container = $('#container');
//alert('Masonry loads items in the nice layout if I do this');
$container.imagesLoaded(function(){
$container.masonry({
itemSelector: '.block',
columnWidth: 100
});
});
$container.infinitescroll({
navSelector : '#loadmore', // selector for the paged navigation
nextSelector : '#more', // selector for the NEXT link (to receive next results)
itemSelector : '.block', // selector for all items to retrieve
loading: {
finishedMsg: 'No more pages to load.',
img: 'http://i.imgur.com/6RMhx.gif'
}
},
// trigger Masonry as a callback
function( newElements ) {
// hide new items while they are loading
var $newElems = $( newElements ).css({ opacity: 0 });
// ensure that images load before adding to masonry layout
$newElems.imagesLoaded(function(){
// show elems now they're ready
$newElems.animate({ opacity: 1 });
$container.masonry( 'appended', $newElems, true );
});
}
);
// set here so it is in reach
var page = 0;
// this will call the required url with new json data.
function loadPage(page) {
var url = 'http://example.com/' + page;
$.getJSON(url, function(data) {
var cont = $('#container');
$.each(data.data, function(index, obj) {
var item = obj.Message;
cont.append(
$('<li>', {"class": "block"}).append(
$('<span>', {"class": item.Type}).append(
$('<span>', {"class":"post_image"}).append(
$('<img>', {src:item.postImageLarge})
)
)
)
)
)
});
//$.each(data.data, function(key, val) { console.log('Data key: ', key, ', Value: ', val)});
});
}
// load more handler
$('#more').click(function(){
page = page + 1;
loadPage(page); //load more items
});
// initial run with page empty
loadPage('');
});
</script>