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.

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

Creating an MP3 Text to Speech file with IBM Watson

I have been referring to the documentation for implementing the IBM Watson Text-to-Speech API using Node.JS. My goal is to generate output files in MP3 format. The documentation suggests modifying the base code, but I'm struggling with this. The resu ...

Troubleshoot the Error: EEXIST - Directory already exists at 'C:UsersPhantom' while setting up a React application[RESOLVE]

I'm trying to set up react and start my first project, but I encountered an issue during the installation process. How can I resolve this? Error: EEXIST: file already exists, mkdir 'C:\Users\Phantom' TypeError: Cannot read propert ...

Table Dimensions Dream Weaver

After creating a basic PHP webpage using Dream Weaver CS5 Version 11.0 Build 4964, I encountered an issue. My webpage is structured with a table containing 10 rows, each row consisting of a single cell. Within each table cell, there is a nested table tha ...

Transforming the jQuery tooltip to be shown in a column layout

Hello, I am currently using the displayTag library to showcase some tables. My goal is to include a tooltip on each display:column element by utilizing jQuery. Below is the code snippet in question: <c:set var="titleName"><wp:i18n key="FILENAME" ...

Error in Next.js 11: Unable to access property 'canonicalBase' as it is undefined

Encountering an issue after upgrading from Next.js version 10.0.2 to 11.0.1. Since the update, I am unable to initiate a project due to the following error: Cannot read property 'canonicalBase' of undefined In my _app.tsx file, the Next imports ...

Utilize PHP to generate a table from data

I successfully created a table using information retrieved from my database, and everything is displaying as intended. However, I am facing an issue where adding a background color applies it to every row in the table. How can I add a background color wit ...

jQuery insert custom text into HTML and eliminate malfunctioning elements

Using jQuery, I have created a form where the entered text is appended to certain elements. This functionality works correctly. Additionally, I have included a remove link with each added element. When someone clicks on the remove button, it should remove ...

The background color in CSS remains stagnant, refusing to change despite

Can someone help me figure out why the background color isn't changing in my simple code? I've double-checked that the external CSS is properly linked because the text color of h1 is changing. <!DOCTYPE html> <html> < ...

The notify.js fails to display notifications in the event of an error

Why am I not receiving any error notifications even when there is an error message in the response object? $.ajax(settings).done(function (response) { if ( "error_message" in response ) { console.log(response); $.notify(" ...

Obtaining the response object for the web browser

Greetings! I currently have a function within router.js in NODEJS : const authenticateUser = (req, res, next) => { //something }; Whenever my application is live, this function is triggered. I am looking for a way to examine the response object. Is ...

Developing a two-dimensional JavaScript array using an AJAX PHP request

I've been working with a MySQL table that stores image data. My goal is to extract this image data and store it in a JavaScript array. The fields I need for the array are "image_ref" and "image_name." To achieve this, I understand that I'll nee ...

Leveraging ternary operators within HTML elements

Currently, I am utilizing the Vue.js framework to create a dynamic list that updates based on two different list objects. My main objective is to adjust the border of the list cards depending on a specific condition. Below are the defined cards: <li ...

Adjust text size based on device orientation changes

I'm struggling to dynamically change the font size based on screen orientation and width. How can I adjust the font size when switching between landscape and portrait mode? I attempted using an event listener, but it's not updating the font size. ...

Efficient communication in Angular: Sharing data among controllers and components

Recently joining a new project, I am faced with the task of implementing a small feature. In my setup, there are 2 distinct views: Cars.html and Wheels.html The Cars.html view is associated with the controller Cars.controller.js On the other hand, the W ...

Alternate routing based on conditions in Angular

I've used the "$urlRouterProvider.otherwise('{route here}')" syntax in angular to create a catch-all route in Angular UI-Router. One thing I'm curious about is whether it's possible to have conditional "otherwise" routing based o ...

Calculating the total of fields from populated documents using Mongoose

In my application, I have two main models: User and Track. A User can complete various Tracks and earn points for each one. The schema for the User model looks like this: let userSchema = new mongoose.Schema({ name: {type: String, required: true}, ...

Combine two CSS effects in succession using Jquery

I am attempting to combine two CSS transition effects together. The first code snippet works well with 'animate': $('.txt').css({'transition': 'transform .5s ease-in-out ', 'transform': 'translate(3 ...

What could be the reason for the failure of .simulate("mouseover") in a Jest / Enzyme test?

I have a scenario where a material-ui ListItem triggers the display of a material-ui Popper containing another ListItem on mouse over using the onMouseOver event. While this functionality works as expected, I am facing difficulties replicating the behavior ...

Unable to access child props in parent event handler in React

Currently utilizing React for building a UI, the structure involves a parent component and a child component as shown below: // Child Component var ListItem = React.createClass({ render: function() { var link_details = ( <div> ...

Discover the power of EJS embedded within HTML attributes!

There are two cases in which I am attempting to use an EJS tag within an HTML attribute. SITUATION 1: <input style="background-image: url(<%= img.URL %>)" /> SITUATION 2: <input onchange="handleCheckboxChange(<%= i ...