Waterfall is a very popular way of webpage layout, such as the image wall effect. Its feature is that the content is arranged in multiple columns, and the height of each column is adaptive. New content will be automatically filled into the shortest column. By combining AJAX technology, we can achieve the effect of "automatically loading more content when scrolling to the bottom", making users feel that the page content is constantly flowing while browsing. This article will teach you how to easily implement this feature using JavaScript.
What is waterfall flow?
The waterfall is like a dynamic puzzle, dividing the content into several columns with different heights, but overall it looks neat. Its advantage is that it can fully utilize screen space, especially suitable for displaying images, article lists, and other content.
for instance:
If you have a bunch of pictures with different heights, putting them together directly may appear disorganized.
But after using a waterfall layout, the images will automatically be arranged in neat columns, which looks both beautiful and comfortable.
In addition, with the "unlimited loading" function, users do not need to manually click "next page", as long as they keep scrolling down, new content will be automatically loaded.
Key steps to achieve infinite loading of waterfall flow
To achieve this effect, it is not complicated and only needs to be completed in a few steps:
Create a multi column layout:
First, divide the page into several columns (such as 3 columns) and place some content in each column.
Monitor scrolling events:
When the user quickly scrolls to the bottom of the page, the function of loading more content is triggered.
Retrieve data from the server:
Using AJAX technology to request new content from the server.
Dynamically update page:
Insert the new content into the corresponding column and maintain the waterfall effect.
Optimize performance:
Avoid frequent triggering of scrolling events, reduce pressure on the server, and improve user experience.
Here is a complete code example that can achieve infinite waterfall loading effect.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AJAX Waterfall Unlimited Loading</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
background-color: #f9f9f9;
}
.container {
display: flex;
justify-content: space-between;
margin: 20px auto;
max-width: 1200px;
}
.column {
flex: 1;
margin: 0 10px;
min-width: 280px;
}
.item {
margin-bottom: 20px;
background-color: #fff;
border: 1px solid #ddd;
border-radius: 5px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.item img {
width: 100%;
height: auto;
display: block;
}
.loading {
text-align: center;
padding: 10px;
color: #007BFF;
}
.error {
color: red;
text-align: center;
margin-top: 10px;
}
</style>
</head>
<body>
<div id="container"></div>
<div id="loading">Loading...</div>
<div id="error-message"></div>
<script>
const container = document.getElementById('container');
const loading = document.getElementById('loading');
const errorMessage = document.getElementById('error-message');
let page = 1; // Current page number
let isLoading = false; // Is data loading in progress
// Initialize columns and containers
const columnCount = 3; // Number of columns
const columns = [];
for (let i = 0; i < columnCount; i++) {
const column = document.createElement('div');
column.className = 'column';
container.appendChild(column);
columns.push(column);
}
// Simulate obtaining data from the server
function fetchData(page) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (page <= 5) { // Assuming a maximum of 5 pages of data
const data = Array.from({ length: 10 }, (_, i) => ({
id: `${page}-${i}`,
image: `Fill in the random image address here
`, // Random image
title: `Page ${page} - Data Items ${i + 1}`,
}));
resolve(data);
} else {
reject('There is no more data available');
}
}, 1000); // Simulate network latency
});
}
// Render data to the page
function renderData(data) {
data.forEach(item => {
const column = getShortestColumn(); // Find the shortest column
const div = document.createElement('div');
div.className = 'item';
// Add images and titles
const img = document.createElement('img');
img.src = item.image;
const title = document.createElement('div');
title.textContent = item.title;
div.appendChild(img);
div.appendChild(title);
column.appendChild(div);
});
}
// Find the shortest column
function getShortestColumn() {
let shortestColumn = columns[0];
let minHeight = columns[0].offsetHeight;
columns.forEach(column => {
if (column.offsetHeight < minHeight) {
shortestColumn = column;
minHeight = column.offsetHeight;
}
});
return shortestColumn;
}
//Load more data
async function loadMore() {
if (isLoading) return;
isLoading = true;
loading.style.display = 'block'; // Display loading prompt
try {
const data = await fetchData(page);
renderData(data);
page++; // Update page numbers
errorMessage.textContent = ''; // Clear error messages
} catch (error) {
errorMessage.textContent = error;
} finally {
isLoading = false;
loading.style.display = 'none'; // Hide loading prompts
}
}
// Check if it scrolls to the bottom
function isBottom() {
return window.innerHeight + window.scrollY >= document.body.offsetHeight - 200;
}
// Monitor scrolling events
window.addEventListener('scroll', () => {
if (isBottom()) {
loadMore();
}
});
// Initialize loading of first page data
loadMore();
</script>
</body>
</html>
.container: This is the main area of the entire page, which contains several columns of content.
.tem: Each content block contains an image and a paragraph of text, simulating actual waterfall content.
Use Flex layout to divide the page into 3 columns, leaving some spacing between each column. The width of the image occupies the entire column, and the height is automatically adjusted according to the proportion of the image.
Initialization columns: When loading the page, create three columns of containers to store the content. Load data: Every time you scroll to the bottom, call the fetchData function to simulate retrieving new content from the server. New content will be automatically inserted into the shortest column to ensure a neat layout.
Replace the fetchData function with a real API request, such as retrieving data from a database or backend interface.
Detailed analysis of the implementation principle:
1. The implementation principle of waterfall layout
The core of waterfall flow is to divide content into multiple columns and dynamically allocate content items according to height. The following are the key points for implementing a waterfall layout:
Multi column container:
Create multiple columns on the page (such as 3 columns), each column being an independent container.
Using CSS's flex layout or grid layout can easily achieve multi column structures.
Content item insertion logic:
The height of each content item may be different (e.g. inconsistent height of images).
New content items will be automatically inserted into the shortest column based on the current height of each column.
Dynamically calculate the height of each column using JavaScript, find the shortest column, and insert new content.
Dynamic adjustment:
After the new content is loaded, the page needs to recalculate the height of each column to ensure that the layout remains compact at all times.
2. The implementation principle of infinite loading
The core of infinite loading is to monitor users' scrolling behavior and dynamically load more content near the bottom of the page. The following are the key steps for its implementation:
Rolling event monitoring:
Use window.addEventListener ('scroll ', callback) to monitor the user's scrolling behavior.
Determine whether the user has scrolled to the bottom (or near the bottom) of the page, for example, using the following formula:
window.innerHeight + window.scrollY >= document.body.offsetHeight - threshold;
The threshold is a threshold (such as 200px) used to trigger loading in advance.
Asynchronous data loading:
Use AJAX technology to retrieve new data from the server.
In modern front-end development, fetch or XMLHttpRequest are commonly used to implement asynchronous requests.
After the data is returned, dynamically render it onto the page.
Prevent duplicate loading:
Disable the triggering of scrolling events during the loading process to avoid multiple requests.
Use a flag variable (such as isLoading) to manage the loading status.
No further data processing:
When the server returns' no more data ', stop listening for scrolling events and display a prompt message.
3. The implementation principle of combining waterfall flow and infinite loading
The combination of waterfall flow and infinite loading requires both functions to be met simultaneously. The following are the key technical points:
Initialize waterfall layout:
Create a multi column container and set the initial style.
When loading the first page of data, insert the content item into the corresponding column.
Dynamic loading and insertion:
When scrolling to the bottom, request new data from the server.
Insert new data into the shortest column one by one, maintaining a waterfall layout.
Performance optimization:
Throttle the rolling events to reduce unnecessary triggering times.
Using lazy loading techniques to delay loading images and other resources to improve page performance.
4. Analysis of Key Technical Points in Code Implementation
1. Initialize multi column containers
const columnCount = 3; // Number of columns
const columns = [];
for (let i = 0; i < columnCount; i++) {
const column = document.createElement('div');
column.className = 'column';
container.appendChild(column);
columns.push(column);
}
Dynamically create a specified number of columns and add them to the page.
Each column is an independent container, and subsequent content items will be inserted into these columns.
2. Find the shortest column
function getShortestColumn() {
let shortestColumn = columns[0];
let minHeight = columns[0].offsetHeight;
columns.forEach(column => {
if (column.offsetHeight < minHeight) {
shortestColumn = column;
minHeight = column.offsetHeight;
}
});
return shortestColumn;
}
Traverse all columns and find the column with the lowest current height.
New content items will be inserted into this column to ensure a compact layout.
3. Rendering data
function renderData(data) {
data.forEach(item => {
const column = getShortestColumn(); // Find the shortest column
const div = document.createElement('div');
div.className = 'item';
// Add images and titles
const img = document.createElement('img');
img.src = item.image;
const title = document.createElement('div');
title.textContent = item.title;
div.appendChild(img);
div.appendChild(title);
column.appendChild(div);
});
}
Render the new data item by item onto the page.
Each content item contains an image and a paragraph of text, simulating actual waterfall content.
4. Rolling detection and loading more
function isBottom() {
return window.innerHeight + window.scrollY >= document.body.offsetHeight - 200;
}
window.addEventListener('scroll', () => {
if (isBottom()) {
loadMore();
}
});
Monitor scrolling events to determine if the user is approaching the bottom of the page.
If the conditions are met, call the loadMore function to load more data.
5. Asynchronous loading of data
async function loadMore() {
if (isLoading) return;
isLoading = true;
loading.style.display = 'block'; // Display loading prompt
try {
const data = await fetchData(page);
renderData(data);
page++; //Update page numbers
errorMessage.textContent = ''; // Clear error messages
} catch (error) {
errorMessage.textContent = error;
} finally {
isLoading = false;
loading.style.display = 'none'; //Hide loading prompts
}
}
Use asynchronous/await to handle asynchronous requests, ensuring that the code is concise and easy to read.
Update page numbers and hide loading prompts after loading is complete.
5. Throttle handling of rolling events: Rolling events may be frequently triggered, leading to performance issues. You can use setTimeout or requestAnimationFrame to throttle scrolling events.
Lazy loading of images:
If there are a large number of images on the page, they can be loaded when they enter the visible area.
Implement lazy loading using the IntersectionObserver API.
Responsive design:
Dynamically adjust the number of columns based on the screen width, such as reducing the number of columns on a small screen.
The implementation principle of the infinite loading effect of waterfall flow is not complicated, but it requires the comprehensive use of various front-end technologies, including multi column layout, dynamic content insertion, scrolling event monitoring, and asynchronous data loading.
By using the above method, you can easily achieve an infinite loading effect of a waterfall of images. This feature can make the page look more beautiful.