How can I align rectangles with different heights to display side by side using Javascript?

Currently, I am designing a press page for a website where the headlines/articles are displayed in rectangles. To achieve this layout, I am using the following CSS:

.press-blocks{
    column-count: 4;
    column-gap: 2em;
    padding-left: 10%;
    padding-right: 10%;
}
.press-item{ 
    display: inline-block;
    margin: 0 0 5em;
    width: 100%;
    text-align: justify;
}

The entire "press" section of the page is enclosed within a single press-blocks container, and each individual article resides within its own press-item. This setup allows me to easily maintain chronological order by adding new articles at the top of the page. However, the current layout displays articles in a top to bottom, left to right format like so:

1    5    9    13
2    6    10   14
3    7    11   15
4    8    12   16

I am seeking a solution to adjust the display to read from left to right, top to bottom instead:

1    2    3    4
5    6    7    8
9    10   11   12
13   14   15   16

I have reviewed the W3Schools tutorial on display: inline-block, and while their example is horizontally oriented, it does not involve a fixed number of columns as mine does. It is important to me that my design maintains exactly 4 columns and expands vertically as new content is added. Additionally, I wish to preserve the consistent vertical spacing between items.

Picture these rectangles evenly distributed with equal horizontal and vertical spaces between them. https://i.sstatic.net/HRLB6.png

Answer №1

There are a couple of approaches you could consider for this task. Based on your comment, it seems like you're interested in creating a masonry grid using JavaScript (the first solution). However, I'll also provide an alternative solution using flexbox. While the flexbox approach might not be exactly what you're looking for, it does not require JavaScript.


JavaScript Solution

This method utilizes JavaScript to generate the masonry grid since CSS alone cannot achieve this effect.

Example:

<div class="masonry">
    <div class="masonry-brick">...</div>
    <div class="masonry-brick">...</div>
    <div class="masonry-brick">...</div>
    <div class="masonry-brick">...</div>
    <div class="masonry-brick">...</div>
    <div class="masonry-brick">...</div>
    <div class="masonry-brick">...</div>
    <div class="masonry-brick">...</div>
</div>
.masonry { 
    display: flex;
    flex-flow: column wrap;
    max-height: 800px; /* Adjust height as needed */
    margin-left: -8px; /* Gutter adjustment */
    width: 100%;
}

.masonry-brick {
    margin: 0 8px 8px 0; /* Gutter spacing */
}
/**
 * @param grid       Object  The Masonry Element 
 * @param gridCell   Object  The Masonry bricks
 * @param gridGutter Integer The Vertical Space between bricks 
 * @param gridCol    Integer Number of columns
 */

function masonry(grid, gridCell, gridGutter, gridCol) {
    let g = document.querySelector(grid),
    gc = document.querySelectorAll(gridCell),
    gcLength = gc.length, // Total number of cells in the masonry
    gHeight = 0, // Initial height of our masonry
    i; // Loop counter

    // Calculate total height of all cells in masonry
    for(i=0; i<gcLength; ++i) {
        gHeight+=gc[i].offsetHeight+parseInt(gridGutter);
    }

    // Calculate and set masonry height
    g.style.height = gHeight/gridCol + gHeight/(gcLength+1) + "px";
}

masonry(".masonry", ".masonry-brick", 8, 4);

Flexbox Solution

This method involves using display: flex; and flex-wrap: wrap; on the parent container of the blocks. Each block is then set to occupy 25% of the parent's width.

Example:

<div class="parent">
    <div class="child"></div>
    <div class="child"></div>
    <div class="child"></div>
    <div class="child"></div>
    <div class="child"></div>
    <div class="child"></div>
    <div class="child"></div>
    <div class="child"></div>
</div>
.parent {
    display: flex;
    flex-wrap: wrap;
    width: 100%;
}
.child {
    height: 200px;
    width: 25%;
    background-color: red;
}

Both approaches will give you the desired left-to-right, top-to-bottom layout. However, only the JavaScript method will allow for custom heights and individual positioning of each "cell."

Answer №2

A New Approach to Javascript

Introducing a fresh take on javascript with the utilization of CSS Grid to form an innovative masonry grid layout.

<div class="masonry">
    <div class="masonry-brick">...</div>
    <div class="masonry-brick">...</div>
    <div class="masonry-brick">...</div>
    <div class="masonry-brick">...</div>
    <div class="masonry-brick">...</div>
    <div class="masonry-brick">...</div>
    <div class="masonry-brick">...</div>
    <div class="masonry-brick">...</div>
</div>
.masonry {
    display: grid;
    grid-gap: 1em;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); /* Adjust columns based on viewport */
    grid-auto-rows: 0;
}
function resizeAllMasonryItems(){
    // Gather all items in one list
    let allItems = document.querySelectorAll(".masonry-brick");

    // Obtain grid object, row-gap, and implicit row size
    let grid = document.querySelector(".masonry"),
    rowGap = parseInt(window.getComputedStyle(grid).getPropertyValue("grid-row-gap")),
    rowHeight = parseInt(window.getComputedStyle(grid).getPropertyValue("grid-auto-rows"));

    // Loop through each masonry-brick element
    for (let i = 0; i < allItems.length; i++){
        // Calculate height of brick and assign grid value
        let rowSpan = Math.ceil((allItems[i].getBoundingClientRect().height + rowGap) / (rowHeight + rowGap));

        // Set the spanning as calculated above
        allItems[i].style.gridRowEnd = "span " + rowSpan;
    }
}

Slight adjustments to grid values may be necessary to achieve desired aesthetics, but this method should get you there.

Answer №3

UPDATE: I have included code to calculate height and make adjustments using filler .size elements. However, this method is not responsive and may encounter issues with image heights. It might be necessary to utilize resize observers and image onload event hooks similar to those mentioned in other examples.
I don't believe this strategy evens out the heights as effectively as one of the examples listed on the css-tricks website.
Nevertheless, it should offer good performance benefits.
I suggest utilizing one of the masonry libraries recommended at the bottom of the css-tricks page https://css-tricks.com/piecing-together-approaches-for-a-css-masonry-layout/
The link provides a comprehensive collection of approaches to address this issue.

I've developed a solution that doesn't rely on JavaScript but necessitates inserting a "break4" element to force wrapping. It operates by employing flex-direction: column, leveraging nth-child to adjust order, and using order to introduce line breaks for wrapping purposes.
Unfortunately, it also requires a fixed height greater than the content size.

I suspect there might be a workaround using last-nth-child or first so that the extra element is not required, but I haven't cracked that yet. It seems achievable though.

Kindly note that the JavaScript snippet is solely for generating the HTML because I'm a bit lazy. You do not necessarily need to insert it using JS.

for(i=0;i<20;i++)document.querySelector('.container').innerHTML+=`<div style="height:${Math.floor(Math.random()*100+10)}px" class="item">${i+1}</div>`
document.querySelector('.container').innerHTML+=`
<div class="size" style="flex-grow:1;order:1"></div>
<div class="size" style="flex-grow:1;order:2"></div>
<div class="size" style="flex-grow:1;order:3"></div>
<div class="size" style="flex-grow:1;order:4"></div>
<div class="break4"></div>` // break4 force wrap, .size elements to calculate space between bottom of container and bottom of columns

// find smallest .size element and use to calculate size
document.querySelector('.container').style.height=(document.querySelector('.container').offsetHeight-[...document.querySelectorAll('.size')].reduce((acc,{offsetHeight})=>Math.min(acc,offsetHeight),Infinity))+'px'
.container {
  display: flex;
  flex-flow: column wrap;
  align-content: space-between;
height:1000px;
}

.item {
  border: 1px solid blue;
  width: 24%;
}

.item:nth-child(4n+1) { order: 4; border:1px solid red }
.item:nth-child(4n+2) { order: 1; border:1px solid blue}
.item:nth-child(4n+3) { order: 2; border:1px solid yellow}
.item:nth-child(4n)   { order: 3; border:1px solid green}

.container::before,
.container::after {
  content: "";
  flex-basis: 100%;
  width: 0;
  order: 2;
}
.break4 {
  content: "";
  flex-basis: 100%;
  width: 0;
  order: 3;
}
<div class="container"><div>

Answer №4

CSS Flexbox can be utilized for this task.

To implement, simply include: display: flex and flex-wrap: wrap in the .press-blocks class.

Add width: 25% to the .press-item class as well.

flex-wrap: wrap is used for creating line breaks.

For instance, setting width: 25% results in 4 columns (or width: 33% for 3 columns, etc).

<style>
.press-blocks {
    display: flex;
    flex-wrap: wrap;
    column-gap: 2em;
    padding-left: 10%;
    padding-right: 10%;
}
.press-item { 
    margin: 0 0 5em;
    width: 25%;
    text-align: justify;
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The issue with updating state in React using dispatch within the same method is not working

Initially, I had a situation where the state was updating correctly with two separate methods (onClick). const sizesMeasureChoise = useSelector(getSizesMeasureChoise); const chooseMeasure = (measure) => { dispatch(setSizeMeasureChoise(measure)); ...

Detect changes in class properties using Node.js

Is it feasible to establish direct proxy watchers for class properties in Node.js? class User{ constructor(name){ this.name = name; let pObject = new Proxy(this,{ set: () => { console.log("something cha ...

Using JavaScript to search for a specific string within a row and removing that row if the string is detected

I need help with a script that removes table rows containing the keyword STRING in a cell. However, my current script is deleting every other row when the keyword is found. I suspect this has to do with the way the rows are renumbered after deletion. How c ...

Is it possible to modify the background-image using Typescript within an Angular project?

I have been struggling to change the background image in my Angular project using Typescript. I attempted to make the change directly in my HTML file because I am unsure how to do it in the CSS. Here is the line of code in my HTML file: <div style="ba ...

When Index.html is hosted via Express, the component fails to render

Following a tutorial has led me to the end and I've made changes to my App.js as shown below: import React, { Component } from "react"; import "./App.css"; class App extends Component { render() { return ( <div> <p>lm ...

Using Tween animations with Three.js

I have some queries regarding tween js within three.js. Below is the code snippet where the particles are generated as shown in the image: Image 1 // Code snippet for initializing var scene; var renderer; var container; var stats; var sphere; // Omitted ...

Is it considered fundamentally inappropriate to call $scope.$digest within $scope.$on?

I recently inherited some AngularJS code, and my knowledge of both the codebase and Angular itself is limited. Within the code I inherited, there are instances where $scope.$digest is being called inside a $scope.$on method within a controller. Here's ...

Troubleshooting lack of error responses from Backend RestAPI with Axios

I am currently facing an issue with receiving a response from my backend system (Node.js Express RestAPI). exports.send = function(req, res) { console.log("sent function running!") let contact = new Contact(req.body) contact.send() ...

Having difficulty modifying the styling of a paragraph within a div container

I have been working on a function that is supposed to adjust the font-size and text-align properties of a paragraph located within a div tag once a button is pressed. function customizeText() { document.getElementById('centretext').innerHTML = ...

Implementing relationships in microservices with Prisma and NestJS

Schema for User Management service: model User { id String @id @default(uuid()) username String @unique email String @unique password String } Schema for File Management service: model File ...

Having trouble with ESLint in VSCode? The ESLint extension seems to be ignoring linting rules after starting a new project within VSCode

I recently started using the ESLint extension in my VSCode editor for my React project. After creating the starter files, I ran the following command in my terminal: eslint --init This allowed me to choose the AirBnb style guide with React, which generat ...

One of the great features of Next.js is its ability to easily change

At the moment, my dynamic path is configured to display events by their ID [id].js localhost:3000/event/1 But I would like it to be structured as follows: localhost:3000/city/date/title. All of this information is available in the events database, but I&a ...

Using Axios to retrieve data from a MySQL database is a common practice in web development. By integrating Vue

I have developed another Vue.js admin page specifically for "writer" where I can display post data fetched from a MySQL database. The admin page called "admin" is functioning properly and responding with all the necessary data. The following code snippet ...

Mapping objects in an array with Javascript

This code snippet is intended for a React Native Chat app. The structure of my data should look something like this: const chatData = [ { id: 1, name: 'John Doe', messages: [ {text: 'Hello', sentAt: 'time here' ...

The best method for quickly loading a webpage that is over 20MB in size

My website is a single-page calendar featuring a full year's worth of images. With 344 requests and a total load size of 20MB, the page consists of a simple PHP script without a database. The requests include a CSS file (15KB), 4 JS files (20KB), two ...

What methods can be used to ensure the document in mongoose is not deleted by updating the expires property on the createdAt field?

I have implemented account verification in this app and created a user account that is set to delete itself after 30 seconds if not verified. createdAt: { type: Date, default: Date.now, index: { expires: 30, }, }, However, I am now searching f ...

Adding to a webpage's URL with PHP prior to rendering the content

Currently, I am investigating how to add a random query string at the end of a URL when someone visits my page for the first time. Let's say an individual goes to my website at www.example.com. If everything proceeds as planned, they will land on my i ...

Is it appropriate to utilize response headers (specifically 400 error codes) to communicate errors, especially when working with x-editable?

Exploring the capabilities of the plugin reveals that it offers two distinct callbacks upon posting a result: error and success. The error callback is triggered in cases where the server does not respond with a 200 header. This means that if the server d ...

Tips for utilizing Material UI's Tooltip feature alongside a TableRow

I'm currently facing an issue where I'm trying to include a MUI TableRow inside a Tooltip component in order to display a long uuid when hovering over the row. However, I noticed that all the columns of the row are being compressed into the first ...

Efficient guide to unlock the secrets of JS height measurements

I've noticed that there are several different 'Height' related properties in JavaScript such as clientHeight, Window.height, scrollHeight, offsetHeight, and more. Although I have a general idea of what they do, I am seeking a formal and det ...