A small, lightweight JavaScript plugin for placing items in straight rows (jQuery and vanilla JS version) – Demo:

Overview

rowGrid.js

rowGrid.js is a small, lightweight (~1000 bytes gzipped) jQuery plugin for placing images (or other items) in straight rows.

The grid is similar to grids on Google Image Search, flickr, shutterstock and Google+ images.

Features:

  • responsive
  • infinite scrolling
  • support for all modern browsers and IE >= 8

A vanilla JavaScript version is available that doesn't require jQuery: https://github.com/brunjo/rowGrid

Example Grid

Do you like this project? Follow me on Twitter @3runjo.

How does it work?

All items must have the same height but the width can be variable. RowGrid.js justifies the items in straight rows so that the width of the rows equals the width of the container/parent element. At first rowGrid.js adjusts the margin between the items. If this is not enough rowGrid.js scales down the items.

Demos & Examples

Examples with explanation: http://brunjo.github.io/rowGrid.js/

Real world example: https://www.pexels.com/

Installation

RowGrid.js requires jQuery 1.7 or above.

<script src="path/to/jquery.min.js"></script>
<script src="path/to/jquery.row-grid.min.js"></script>

Alternativlly, if you don't want to host rowGrid.js yourself, you can use cdnjs: https://cdnjs.com/libraries/rowgrid.js

You can also install it with Bower or npm:

  • Install with Bower: bower install rowGrid.js.
  • Install with npm: npm install rowgrid.js.

Usage

It is important that you either declare the width and height as attributes on the img tag or that you wait until the images are loaded before you start rowGrid.js.

HTML:

<div class="container">
  <div class="item">
    <img src="path/to/image" width="320" height="200" />
  </div>
  <div class="item">
    <img src="path/to/image" width="290" height="200" />
  </div>
  ...
</div>

JS:

var options = {minMargin: 10, maxMargin: 35, itemSelector: ".item"};
$(".container").rowGrid(options);

Endless Scrolling

JS:

// append new items
$(".container").append("<div class='item'><img src='http://placehold.it/310x200' /></div>");
// arrange appended items
$(".container").rowGrid("appended");

Options

$(".container").rowGrid({
    itemSelector: ".item"
    minMargin: 10,
    maxMargin: 35,
    resize: true,
    lastRowClass: "last-row",
    firstItemClass: "first-item"
});

itemSelector (required)

  • value: string (CSS Selector)

You have to set this option. The selector have to reference to all grid items.

minMargin

  • value: number or null
  • default value: null

This is the minimal horizontal margin between the items. The margin is only between the items not between the first/last item and the container.

maxMargin

  • value: number or null
  • default value: null

This is the maximal horizontal margin between the items.

resize

  • value: boolean
  • default value: true

If resize is set to true the layout updates on resize events. This is useful for responsive websites.

lastRowClass

  • value: string
  • default value: last-row

The first item in the last row gets this class.

firstItemClass

  • value: string or null
  • default value: null

The first item in every row gets this class.

lastItemClass

  • value: string or null
  • default value: null

The last item in every row gets this class.

Comments
  • support multiple grids

    support multiple grids

    in your code, you have this piece:

    if(options.resize) {
        $(window).on('resize', {container: this[0]}, function(e){
    

    please scope your events so they wouldn't collide with others code, and change it a little to:

    if(options.resize) {
        $(window).on('resize.rowGrid', function(e){
          // do 'layout' for each "this" element
    }
    

    BUT, you need to take that resize event outside, and wrap the rest of your initialization code with something like:

    $.fn.rowGrid = function( options ) {
        var defaults = {
          minMargin: null,
          maxMargin: null,
          resize: true,
          lastRowClass: 'last-row',
          firstItemClass: null
        };
    
        return this.each(function(){
            if ( this.length == 0 ) {
              console.error( 'No element found for "' + this.selector + '".' );
              return this;
            }
            if ( this.length > 1 ) {
              return this.each(
                function() {
                  $(this).rowGrid( options );
                }
              );
            }
    
            if(options === 'appended') {
              options = this.data('grid-options');
              var $lastRow = this.children('.' + options.lastRowClass);
              var items = $lastRow.nextAll().add($lastRow);
              layout(this[0], options, items);
            } else {
              options = $.extend( {}, defaults, options );
              this.data('grid-options', options);
              layout(this[0], options);
           }
        })
    
            if(options.resize) {
            $(window).on('resize.rowGrid', function(e){
                this.each(function(){
                    layout(this, options);
                });
            });
        }
    
           ....
           ....
          // rest of the code
    });
    
    
    
    
    opened by yairEO 6
  • Sometimes I need to refresh the page to see the effect

    Sometimes I need to refresh the page to see the effect

    Hi,

    Thanks for such a good plugin. It works fine. But sometimes the items are not properly margined and placed. I need to refresh it to see it ok.

    What could be the reason? any idea? Do we have to apply it after all the images are loaded or something. Thanks in advance for your help.

    Mahfuz

    opened by bbt-mahfuz 5
  • last item class

    last item class

    hi brunjo.

    thx for your great plugin i added last item class. perhapas you can update yours.

    ` (function($){ $.fn.rowGrid = function( options ) { return this.each(function() { $this = $(this); if(options === 'appended') { options = $this.data('grid-options'); var $lastRow = $this.children('.' + options.lastRowClass); var items = $lastRow.nextAll(options.itemSelector).add($lastRow); layout(this, options, items); } else { options = $.extend( {}, $.fn.rowGrid.defaults, options ); $this.data('grid-options', options); layout(this, options);

        if(options.resize) {
          $(window).on('resize.rowGrid', {container: this}, function(event) {
            layout(event.data.container, options);
          });
        }
      }
    });
    

    };

    $.fn.rowGrid.defaults = { minMargin: null, maxMargin: null, resize: true, lastRowClass: 'last-row', firstItemClass: null, lastItemClass: null };

    function layout(container, options, items) { var rowWidth = 0, rowElems = [], items = jQuery.makeArray(items || container.querySelectorAll(options.itemSelector)), itemsSize = items.length; // read

    var containerBoundingRect = container.getBoundingClientRect();
    var containerWidth = Math.floor(containerBoundingRect.right - containerBoundingRect.left)-parseFloat($(container).css('padding-left'))-parseFloat($(container).css('padding-right'));
    var itemAttrs = [];
    var theImage, w, h;
    for(var i = 0; i < itemsSize; ++i) {
      theImage = items[i].getElementsByTagName('img')[0];
      if (!theImage) {
        items.splice(i, 1);
        --i;
        --itemsSize;
        continue;
      }
      // get width and height via attribute or js value
      if (!(w = parseInt(theImage.getAttribute('width')))) {
        theImage.setAttribute('width', w = theImage.offsetWidth);
      }
      if (!(h = parseInt(theImage.getAttribute('height')))) {
        theImage.setAttribute('height', h = theImage.offsetHeight);
      }
    
      itemAttrs[i] = {
        width: w,
        height: h
      };
    }
    itemsSize = items.length;
    
    // write
    for(var index = 0; index < itemsSize; ++index) {
      if (items[index].classList) {
        items[index].classList.remove(options.firstItemClass);
        items[index].classList.remove(options.lastItemClass);
        items[index].classList.remove(options.lastRowClass);
      } else {
        // IE <10
        items[index].className = items[index].className.replace(new RegExp('(^|\\b)' + options.firstItemClass + '|' + options.lastRowClass + '(\\b|$)', 'gi'), ' ');
      }
    
      rowWidth += itemAttrs[index].width;
      rowElems.push(items[index]);
    
      // check if it is the last element
      if(index === itemsSize - 1) {
        for(var rowElemIndex = 0; rowElemIndex<rowElems.length; rowElemIndex++) {
          // if first element in row
          if(rowElemIndex === 0) {
            rowElems[rowElemIndex].className += ' ' + options.lastRowClass;
          }
          rowElems[rowElemIndex].style.cssText =
              'width: ' + itemAttrs[index+parseInt(rowElemIndex)-rowElems.length+1].width + 'px;' +
              'height: ' + itemAttrs[index+parseInt(rowElemIndex)-rowElems.length+1].height + 'px;' +
              'margin-right:' + ((rowElemIndex < rowElems.length - 1)?options.minMargin+'px' : 0);
        }
      }
    
      // check whether width of row is too high
      if(rowWidth + options.maxMargin * (rowElems.length - 1) > containerWidth) {
        var diff = rowWidth + options.maxMargin * (rowElems.length - 1) - containerWidth;
        var nrOfElems = rowElems.length;
        // change margin
        var maxSave = (options.maxMargin - options.minMargin) * (nrOfElems - 1);
        if(maxSave < diff) {
          var rowMargin = options.minMargin;
          diff -= (options.maxMargin - options.minMargin) * (nrOfElems - 1);
        } else {
          var rowMargin = options.maxMargin - diff / (nrOfElems - 1);
          diff = 0;
        }
        var rowElem,
          widthDiff = 0;
        for(var rowElemIndex = 0; rowElemIndex<rowElems.length; rowElemIndex++) {
          rowElem = rowElems[rowElemIndex];
          var rowElemWidth = itemAttrs[index+parseInt(rowElemIndex)-rowElems.length+1].width;
          var newWidth = rowElemWidth - (rowElemWidth / rowWidth) * diff;
          var newHeight = Math.round(itemAttrs[index+parseInt(rowElemIndex)-rowElems.length+1].height * (newWidth / rowElemWidth));
          if (widthDiff + 1 - newWidth % 1 >= 0.5 ) {
            widthDiff -= newWidth % 1;
            newWidth = Math.floor(newWidth);
          } else {
            widthDiff += 1 - newWidth % 1;
            newWidth = Math.ceil(newWidth);
          }
          rowElem.style.cssText =
              'width: ' + newWidth + 'px;' +
              'height: ' + newHeight + 'px;' +
              'margin-right: ' + ((rowElemIndex < rowElems.length - 1)?rowMargin : 0) + 'px';
          if(rowElemIndex === 0) {
            rowElem.className += ' ' + options.firstItemClass;
          }
          if(rowElemIndex == rowElems.length-1) {
            rowElem.className += ' ' + options.lastItemClass;
          }
        }
        rowElems = [],
          rowWidth = 0;
      }
    }
    

    } })(jQuery); `

    opened by mamaretti 4
  • Resize fails in Safari (9.1 Mac)

    Resize fails in Safari (9.1 Mac)

    Safari 9.1 for Mac does not seem to "Resize" the grid correctly. When I shrink the browser horizontally, the grid falls apart. When I enlarge the browser horizontally, the grid aligns correctly again.

    The "resize" event is fired and the "layout" function is called, but the grid has many holes. I have included a screenshot using the provided demo.

    image

    Edit: I had some success by adding a delay (and "throttle") to the "layout" call. The images flicker a bit on resize due to the delay, but the grid remains intact. I'd still love to figure out why Safari does this and whether there's a better solution.

    opened by dgsjm 4
  • no need to

    no need to "float" items

    Using floats (in this manner) is considered bad practice. Please use inline-block and if your HTML is not minified, then you will have to hack the container with font-size:0.

    opened by yairEO 4
  • ES6 import

    ES6 import

    Hello,

    how to import it to ES6? I'm getting error that jQuery is not defined, but I'm importing it before rowGrid.

    This is my import: import $ from "jquery"; import rowGrid from 'rowgrid.js';

    Thanks for reply. Ondra

    opened by mrondra 3
  • Questions about the vanilla-js version

    Questions about the vanilla-js version

    I'm planning to migrate away from jQuery and vanilla-js version of rowGrid seems to be the way to go. @brunjo can you please confirm whether (functionally) these versions are equivalent? Also, I'm suspecting that vanilla-js version would need a separate npm entry to be available from npm, correct?

    opened by piotrd 3
  • Few problems with rowGrid.js

    Few problems with rowGrid.js

    Hi!

    Sorry, if this is not very right place for such report. In fact, rowGrid is embeded in CMS, which I use as the base for my website, but maintainers of CMS said that they can't help me as the problems I facing are likely to be on rowGrid side.

    1. First of all rowGrid works great in 99.999% cases when the size of screen is large, lets say 1920px, in most browsers when there is no additional styles applied.

    However I have an option when users apply their own branding to their blogs, meaning there is additional CSS in the page code. One of the significant changes (to my mind) is that there is padding: 20px; style added to the div where photoset resides.

    1

    1

    Alingment also depends on the screen scale and size, f.i. the picture above I got on all (17", 24") 1920x1080 monitors both in Chrome and Mozilla but with 100% scale. If I set screen zoom to 125% which is default in many Windows systems, it looks a little bit different (but still having this issue with alignment).

    1. Secondly I'm still getting this issue, with tiny images but only in Chrome. I have changed this code:
    function setRowGrid() {
               $('.js-topic-photoset-list').rowGrid({
                   itemSelector: '.topic-photoset-item',
                   minMargin: 10,
                    maxMargin: 15,
                   resize: false,
                    lastRowClass: 'topic-photoset-last_row',
                   firstItemClass: "first-item"
                 });
             }
    

    To this one:

    function setRowGrid() {
               $('.js-topic-photoset-list img').load(function () {
                   $('.js-topic-photoset-list').rowGrid({
                        itemSelector: '.topic-photoset-item',
                        minMargin: 10,
                        maxMargin: 15,
                        resize: false,
                        lastRowClass: 'topic-photoset-last_row',
                        firstItemClass: "first-item"
                   });
                 });
             }
    

    And still getting this issue

    1

    2

    You need to press referesh several times to reproduce.

    However it seems that this problem is now only in Chrome. In FF I wasn't able to get to same results.

    You may find that rowGrid.js is not the latest release, but I already tried latest 1.0.3 version and also tried to play with different options like min/max margin, resize, etc. I have test server and I could give you the link to see how it works there. (It's almost the same)

    Please help, have now idea what to try else. Any advice would be appreciated......

    opened by inliquid 3
  • jquery.min.js conflict

    jquery.min.js conflict

    My current project requires an earlier version of jquery.min.js to work with an off canvas nav panel while rowGrid.js uses a later version of jquery.min.js.

    Using both at the same time obviously won't work. Using only the earlier version stops rowGrid.js working and using the version in the RowGrid.js package stops the off canvas nav working.

    Oh what to do!

    opened by outofusernames 3
  • Why not just use CSS?

    Why not just use CSS?

    JavaScript/DOM is an extreme performance hit when looping or adding elements--redraw nightmare.

    From: http://codepen.io/oklsfo/pen/JDnFG

    .container {
      display: flex;
      flex-flow: row wrap;
      justify-content: space-between;
    }
    
    question 
    opened by JawsomeJason 3
  • Relayout doesn't work if when some of the items are set to display:none

    Relayout doesn't work if when some of the items are set to display:none

    I implemented a filter which simply hides some of the items in the grid using css (display:none)

    A css class gets toggled on a container element (ex. body) when this css class is set, some of the items in the grid are set to display:none;

    This requires a relayout of the grid. But both the jquery and the javascript version do not relayout the grid. When I remove the items from the dom, then the relayout works.

    opened by thdk 2
  • please add a destroy function to rowGrid.js

    please add a destroy function to rowGrid.js

    Hello brunjo,

    i like your tool, but i have problems to destroy your resize layout function if i change the view. On mobile i working with a slider, and I want to switch off all functions of row grid.

    Thanks a lot :-)

    opened by Anzka 2
  • for square items, last row looks out of alignment

    for square items, last row looks out of alignment

    If you have items which are all exactly the same initial width and height, and start resizing their container, then the last row doesn't match, and it looks weird. This is a no-go for my client:

    gurushots_-challenge_entries-_2014-03-10_21 55 28

    A grid item should always have the same dimensions as the rest, if they are all the same size. maybe it can be added as an option to the plugin.

    enhancement 
    opened by yairEO 3
Releases(v1.1.0)
Javascript Library to create scrollable table with infinite rows and columns.

Fattable Demo Checkout the demo here. What is it? Fattable is a javascript Library to create table with infinite scroll, with infinite number of rows

Paul Masurel 477 Sep 19, 2022
Grupprojekt för kurserna 'Javascript med Ramverk' och 'Agil Utveckling'

JavaScript-med-Ramverk-Laboration-3 Grupprojektet för kurserna Javascript med Ramverk och Agil Utveckling. Utvecklingsguide För information om hur utv

Svante Jonsson IT-Högskolan 3 May 18, 2022
A small web app that tries to imitate the desktop web version of amazon site, you can add items to the basket, delete them, and have your user authentication feature thanks to Firebase.

Features Here's the feature's included in this project ??‍??‍??‍?? Login Page ?? Products Page ?? Cart and Checkout Page ?? Sign up function with Goog

Murad Rahmanzada 16 Aug 22, 2022
Hemsida för personer i Sverige som kan och vill erbjuda boende till människor på flykt

Getting Started with Create React App This project was bootstrapped with Create React App. Available Scripts In the project directory, you can run: np

null 4 May 3, 2022
Kurs-repo för kursen Webbserver och Databaser

Webbserver och databaser This repository is meant for CME students to access exercises and codealongs that happen throughout the course. I hope you wi

null 14 Jan 3, 2023
A single-page application that allow users to add their To Do items. The items could be checked as completed and the completed task can be removed. Built with JavaScript, HTML and CSS

To Do list Application This is a single page application that allows users to keep track of their tasks. Users can add the task and also check the che

Micheal Oguntayo 4 Oct 14, 2022
A Zotero add-on that scans your Markdown reading notes, tags the associated Zotero items, and lets you open notes for the Zotero items in Obsidian.

Zotero Obsidian Citations Adds colored tags to Zotero items that have associated Markdown notes stored in an external folder. Open an associated Markd

Dae 210 Jan 4, 2023
A secondhand marketplace where you can post items for sale, interact with sellers, save items you are interested in.

Curbside - the secondhand market place that's actually pleasant to use Post items for sale, interact with sellers, save items you are interested in. A

Curbside 14 Sep 9, 2022
A small javascript DOM manipulation library based on Jquery's syntax. Acts as a small utility library with the most common functions.

Quantdom JS Quantdom is a very small (about 600 bytes when ran through terser & gzipped) dom danipulation library that uuses a Jquery like syntax and

Sean McQuaid 7 Aug 16, 2022
✏️ A small jQuery extension to turn a static HTML table into an editable one. For quickly populating a small table with JSON data, letting the user modify it with validation, and then getting JSON data back out.

jquery-editable-table A small jQuery extension to turn an HTML table editable for fast data entry and validation Demo ?? https://jsfiddle.net/torrobin

Tor 7 Jul 31, 2022
Vanilla JavaScript version of jquery-hoverIntent

sv-hover-intent sv-hover-intent is a JavaScript plugin for handling mouse hovers on a delay. It tracks when the mouse movement has slowed enough that

Scott 6 Mar 18, 2022
Skip the ads on Thingiverse, and get straight to the good stuff.

Thingiverse STL Downloader Those ads sure are annoying. Skip 'em, and get straight to the good stuff. This repository is here for the normal purposes

Stephan Casas 17 Mar 13, 2022
Straight forward plotting built on D3

D3xter About Simple and powerful syntax to make common charts with minimal code. Highly flexible plotting for deep customization. Sensible defaults bu

Nathan Epstein 341 Oct 20, 2022
💰 Straight-forward budgeting.

financier ⚙ Github — ?? Production app — Staging app — ?? Staging Docs — ?? Trello — ?? Website — ?? Docker A web-based, offline-first app. Built with

Financier 40 Dec 17, 2022
Get started with GatsbyJS straight away without having to spend a whole day configuring your usual addons.

Gatsby Starter Infinite Get started with GatsbyJS straight away without having to spend a whole day configuring your usual addons. This starter includ

Matt Patterson 3 Jun 27, 2022
🚀 A really straight-forward SurrealDB Rest API wrapper.

SurrealDB Node.js npm i surrealdb Quick Start surreal start --root user --pass user import SurrealDB from 'surrealdb' const Surreal = new SurrealDB('

Jareer Abdullah 5 Dec 10, 2022
Make the content slide prettily across the screen with variable sizes of scrolling items, in any of four directions, pausing while the mouse is over the marquee, and all with vanilla JavaScript.

TEG Marquee Make the content slide prettily across the screen with variable sizes of scrolling items, in any of four directions, pausing while the mou

Paul B. Joiner 0 Dec 30, 2021
jQuery-plugin for add/remove dom-items with renaming form-elements (arrays)

dynamicrows jQuery-plugin for add/remove rows by cloning existing row / renaming form-elements (arrays). Requirements jQuery >=2.0 if move-action used

Dennis Dohle 0 Nov 9, 2020
Nftix-demo-ui - Demo UI used in my NFT course on Egghead.io

NFTix Demo UI This repository contains the UI used for my Egghead course on building a NFT ticketing system ?? ?? If you're watching the videos, use t

Ryan Harris 10 Dec 17, 2022