Scrolling Container Following Down the Page, Halts at Its Own Bottom (Similar to Twitter)

I am facing an issue and need some help. I am working on creating a three-column page layout where the middle section is for posts, the right section is for links and references (a bit long), and the left section is fixed.

My question is: How can I prevent the right div from moving when it reaches its bottom? Also, if the content in the middle div is shorter than the right div, how do I add a scrollbar to the page just for the right div, similar to what Twitter does?

I have been brainstorming and thought that maybe Twitter uses two separate divs for those sections. One acts normally, while the other remains fixed at the bottom. The normal one stretches the page for scrolling, while the fixed one sticks on top of it. However, I am unsure if this is the correct approach.

Is it possible to achieve this with pure CSS? (I am using TailwindCSS)

Here is my idea presented visually. (Alternatively, you can look at the Twitter homepage feed)

https://i.stack.imgur.com/zHMmO.jpg

https://i.stack.imgur.com/Sgleo.jpg

Additionally, here is a gif demonstration:

click

Answer №1

Some of the solutions offered here may not be ideal. To achieve this, you need to add an event listener for window scroll.

The solution you are seeking is a "scrolling sticky sidebar". You can find a neat implementation with minimal code here.

Here are the key steps:

Step One:

Create a sidebar container and a subcontainer wrapper

<div class="sidebar">
  <div class="content-wrapper">
    <!-- content -->
    <div></div>
    <div></div>
    <div></div>
    <div></div>
  </div>
</div>

Step Two:

Select the elements and monitor scroll positions before adjusting values as needed

let sidebar = document.getElementsByClassName("sidebar")[0];
let sidebar_content = document.getElementsByClassName("content-wrapper")[0];

window.onscroll = () => {
        let scrollTop = window.scrollY; 
        let viewportHeight = window.innerHeight; 
        let contentHeight = sidebar_content.getBoundingClientRect().height; 
        let sidebarTop = sidebar.getBoundingClientRect().top + window.pageYOffset; 
}

if(scrollTop >= contentHeight - viewportHeight + sidebarTop) {
  sidebar_content.style.transform = `translateY(-${contentHeight - viewportHeight + sidebarTop}px)`;
  sidebar_content.style.position = "fixed";
}
else {
  sidebar_content.style.transform = "";
  sidebar_content.style.position = "";
}

Outcome:

https://i.stack.imgur.com/hbZGv.gif

For further details, refer to the link provided above.

Answer №2

If you're searching for a solution, look no further. Here is what you need to do:

1. Utilize Flexbox and the position:sticky

To achieve the desired effect, make use of the flexibility that comes with a flexbox layout. Set both the position:sticky property and specify an align-self value based on the user's scrolling behavior. For the next step involving JavaScript to work effectively, this initial setup is crucial.

2. Employ JavaScript during scrolling to adjust margin, position, and align-self

The custom JavaScript code monitors changes in the scrolling direction. When such changes occur, it does the following:

  1. Changes the align-self property to either flex-end (while scrolling up) or flex-start (while scrolling down).
  2. Sets the bottom value (while scrolling up) or the top value (while scrolling down) to the calculated result of window height - sidebar height. This accounts for the portion of the sidebar not visible on-screen when scrolling.
  3. Determines the remaining space available at the top (during downward scrolls) or at the bottom (during upward scrolls). It then assigns this as a top or bottom margin to maintain the sidebar's visual position without disruption.

By setting these margins, the sidebar can smoothly scroll back into view immediately once the user starts moving upwards again, eliminating the need for them to manually reach the top to release the sticky position.

Live Demonstration

Check out the live demo below, accessible via Codepen, or visit my personal website for more examples at this link.

UPDATE: Following feedback from damzaky, I have incorporated additional JavaScript logic in order to ensure the sidebar resumes scrolling upwards automatically whenever necessary.

UPDATE 2: Substantial modifications have been made to the JavaScript code resulting in improved functionality (as per current observations).

Answer №3

If you're looking to create a sticky sidebar, consider using the JavaScript library called sticky-sidebar.

var sidebar = new StickySidebar('#sidebar', {
  containerSelector: '#main',
  innerWrapperSelector: '.sidebar__inner',
});
body {
  background: #3f87c2;
  margin: 0;
  padding: 0;
  text-align: center;
}

#main {
  display: flex;
}

#middle {
  flex-grow: 1;
  background: #4dd2ff;
}

#sidebar {
  min-height: 1000px;
  width: 200px;
  background: #4dd2ff;
  margin-left: 15px;
}

.sidebar {
  will-change: min-height;
}

.sidebar__inner {
  transform: translate(0, 0);
  /* For browsers don't support translate3d. */
  transform: translate3d(0, 0, 0);
  will-change: position, transform;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/sticky-sidebar/3.3.1/sticky-sidebar.min.js" integrity="sha512-iVhJqV0j477IrAkkzsn/tVJWXYsEqAj4PSS7AG+z1F7eD6uLKQxYBg09x13viaJ1Z5yYhlpyx0zLAUUErdHM6A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<div id="main">
  <div id="middle">
    <div>Middle Div Content (Sooo long)</div>
    <div style="margin-top: 2400px">End of Middle Content</div>
  </div>
  <div id="sidebar" class="sidebar">
    <div class="sidebar__inner">
      <div>Right Div Content</div>
      <div style="margin-top: 900px">Right Div Content Bottom</div>
    </div>
  </div>
</div>

You could attempt to create this from scratch, but using this library ensures compatibility and performance optimizations. Check out the full documentation here.

Answer №4

To prevent an element from scrolling, you can apply the following CSS code: position: sticky; bottom: 0

For more information on how the "position: sticky;" property works, check out this post on Stackoverflow How does the "position: sticky;" property work?

I hope this helps answer your question!

Edit: [Give it a try]

.main {
  width: 100%;
  height: 1000px;
  display: flex;
}

.first {
  width: 30%;
  background-color: red;
}

.second {
  width: 40%;
  background-color: green;
}

.third {
  width: 30%;
  background-color: blue;
  height: 500px;
  position: sticky; 
  top: 0px;
}

p {
  margin-left: 20px;
}
<div class="main">
  <div class="first">
    <p>
      Left content.
    </p>
  </div>

  <div class="second">
    <p>
      Main content.
    </p>
  </div>
  <div class="third">
    <p>
      Right content.
    </p>
  </div>
</div>

Answer №5

My investigation into the HTML and CSS structure of Twitter using developer tools revealed an interesting implementation. I attempted to replicate this implementation by creating three vertical sections, with the middle and right sections enclosed within a parent div similar to how it is done on Twitter. This setup results in the scrollbar appearing on the parent div instead of the individual sections, providing a seamless scrolling experience. Feel free to test the functionality by scrolling through the middle section (scrollbars are hidden).

Additional Information:

  • The passive: true option in the scrollbar event is utilized to enhance browser performance for the scroll event, as scroll events can be resource-intensive.

  • If the content in your right section is limited, like on Twitter, and there is minimal scrolling involved, consider implementing the IntersectionObserver method highlighted in the referenced CSS Tricks article for improved performance. This approach allows for smoother scrolling when reaching the bottom or top of the right section, enhancing the overall user experience.

    right.scrollTo({
        top: right.scrollHeight,
        behavior: "smooth",
    });
    

For more insights, refer to this answer where I employ the IntersectionObserver technique to display a button based on specific criteria.

Should you have any queries or require further clarification, feel free to inquire in the comments section below.

const content = document.querySelector('.content-container');
const right = document.querySelector('.right');

const scrollSpeed = 1;

console.log(right);

content.addEventListener("scroll", (e) => {
  console.log(content.scrollTop);
  right.scrollTop = content.scrollTop * scrollSpeed;
}, {
  passive: true
})
body {
  margin: 0;
  padding: 0;
}

.main {
  display: flex;
  height: 100vh;
  overflow-y: hidden;
}

.middle {
  flex: 1;
  height: auto;
}

.left,
.right {
  width: 150px;
  max-height: 100vh;
  overflow-y: auto;
}

.left {
  background: gray;
}

.left,
.middle,
.right {
  padding: 1rem;
}

.right {
  background: lightgray;
  position: sticky;
  top: 0;
  overflow-y: auto;
  padding: 0;
  padding: 1rem;
}

.content-container {
  flex: 1;
  display: flex;
  position: relative;
  overflow-y: auto;
  z-index: 0;
  background: whitesmoke;
}

.right::-webkit-scrollbar,
.left::-webkit-scrollbar,
.content-container::-webkit-scrollbar {
  display: none;
}

.right,
.left,
.content-container {
  -ms-overflow-style: none;
  /* Internet Explorer 10+ */
  scrollbar-width: none;
  /* Firefox */
}


/* helper classes to overflow containers */

.menu {
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 1rem;
  list-style: none;
  font-size: 1.1rem;
}

.middle-section-content {
  display: flex;
  flex-direction: column;
  gap: 2.5rem;
}

.middle-section-content div {
  min-height: 150px;
}
<div class="main">
  <div class="left">
    <h2>
      First
    </h2>
    <ul class="menu">
      <li>
        Menu Item
      </li>
      <li>
        Menu Item
      </li>
      <li>
        Menu Item
      </li>
      <li>
        Menu Item
      </li>
      <li>
        Menu Item
      </li>
      <li>
        Menu Item
      </li>
      <li>
        Menu Item
      </li>
      <li>
        Menu Item
      </li>
      <li>
        Menu Item
      </li>
      <li>
        Menu Item
      </li>
      <li>
        Menu Item
      </li>
      <li>
        Menu Item
      </li>
      <li>
        Menu Item
      </li>
      <li>
        Menu Item
      </li>
      <li>
        Menu Item
      </li>
      <li>
        Menu Item
      </li>
      <li>
        Menu Item
      </li>
      <li>
        Menu Item
      </li>
      <li>
        Menu Item
      </li>
      <li>
        Menu Item
      </li>
    </ul>
    <div>
      End of Left Section
    </div>
  </div>
  <div class="content-container">
    <div></div>
    <div class="middle">
      <h2>
        Middle Section
      </h2>

      <div class="middle-section-content">

        <div>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</div>
        <div>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</div>
        <div>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</div>
        <div>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</div>
        <div>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</div>
        <div>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</div>
        <div>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</div>

      </div>
      <div>
        End of Middle Section
      </div>

    </div>
    <div class="right">
      <h2>
        Right
      </h2>
      <ul class="menu">
        <li>
          Side Bar Content Item
        </li>
        <li>
          Side Bar Content Item
        </li>
        <li>
          Side Bar Content Item
        </li>
        <li>
          Side Bar Content Item
        </li>
        <li>
          Side Bar Content Item
        </li>
        <li>
          Side Bar Content Item
        </li>
        <li>
          Side Bar Content Item
        </li>
        <li>
          Side Bar Content Item
        </li>
        <li>
          Side Bar Content Item
        </li>
        <li>
          Side Bar Content Item
        </li>
        <li>
          Side Bar Content Item
        </li>
        <li>
          Side Bar Content Item
        </li>
        <li>
          Side Bar Content Item
        </li>
        <li>
          Side Bar Content Item
        </li>
        <li>
          Side Bar Content Item
        </li>
        <div>
          End of Right Section
        </div>
      </ul>
    </div>
  </div>

</div>

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

Encase an asynchronous function inside a promise

I am currently developing a straightforward web application that manages requests and interacts with a SQL database using Express and Sequelize. My issue arises when I attempt to call an async function on an object, as the this part of the object becomes u ...

The function for utilizing useState with a callback is throwing an error stating "Type does not have

Currently, I am implementing the use of useState with a callback function: interface Props { label: string; key: string; } const [state, setState] = useState<Props[]>([]); setState((prev: Props[]) => [...pr ...

When I switch to a different navigation system, the css class gets removed

Let me clarify what I am looking for: Upon entering the index.php page, LINK_1 button is active. When I switch to LINK_2, it becomes active. I have only one index.php page where I include parts of external pages using PHP. Page_1 With the code I found, ...

Error encountered when extending Typography variant in TypeScript with Material UI v5: "No overload matches this call"

Currently, I am in the process of setting up a base for an application using Material UI v5 and TypeScript. My goal is to enhance the Material UI theme by adding some custom properties alongside the default ones already available. The configuration in my ...

Modifying the Background Color of a Header in React Native

As a newcomer to React Native, I am attempting to change the background color of the header bar (Navigation Bar). Here are some approaches that I have tried: return ( <View style={styles.container}> <NavigationBar titl ...

What measures can I take to ensure that users cannot tamper with the PayPal code?

I've integrated a "Pay Now" button from Paypal onto my website. However, there is an issue where users can easily alter the payment amount before proceeding. Below is the code PayPal has provided: <form action="https://www.paypal.com/cgi-bin/webs ...

jquery logic for iterating through all elements in a select menu encountering issues

In search of a solution to iterate through all options in a dropdown list using code, comparing each to a variable. When a match is found, I aim to set that particular value as the selected item in the dropdown and then exit the loop. Here's what I&ap ...

Facing a problem with the carousel in Angular 6

I am currently working with Angular 6 and I have a topAdvertisementList[] that is supposed to return 2 records to be displayed in my carousel with a fixed image, but for some reason, only one record is showing up in the carousel! I suspect there might be a ...

Enhancing Accessibility for the jQuery Countdown Plugin

Seeking to enhance the accessibility of my website's jQuery countdown, I am striving to adhere to WAI-ARIA guidelines. The specified requirements are as follows: Ensure the area is live so it updates dynamically with the countdown display. Avoid re ...

Using VBA and Selenium to access iframes within HTML with the #document tag

I am currently facing a challenge in accessing the HTML content within two iframes using Selenium Basic in VBA. Due to restrictions on our machines, we are unable to use IE and other tools like Python are not available to us. In the past, I was able to ac ...

Layering digital sheets of paper and rearranging them with the help of CSS

I want to create a visual representation of a stack of paper sheets as a metaphor. The idea is to neatly stack the sheets on top of each other, with only the header of each sheet visible and the rest of the content covered. !-------------- | Sheet 1 +--- ...

Adding a distinct key and its corresponding value to an array in Vue for a unique

I am attempting to add key-value pairs into an array while ensuring their uniqueness. Currently, I am trying the following approach: for (const [key, value] of Object.entries(check)) { console.log(`${key}: ${value}`); this.inputFields. ...

The XHR Get request fails to load the HTML content sent from the Express application

As I embark on building my inaugural express app, I have encountered a shift in sending requests from the front-end. Previously, all requests were initiated by anchor elements for GET requests and form elements for POST requests, with server responses hand ...

Update the jQuery script tag with new content - rewrite everything

I am facing an issue with jquery. I need to change the link to src only when "document.write" is present. Below is my code: myscript.js document.write("TEST ABCD"); test.html <html> <body> <button id="example">click me</button&g ...

Troubleshooting bitrate and quality issues in AWS MediaConvert

Whenever I attempt to initiate a MediaConvert job in CBR, VBR, or QVBR mode with Bitrate or MaxBitrate exceeding 250,000, an error occurs. The message reads: "Unable to write to output file [s3:///videos//***/original.mp4]: [Failed to write data: Access D ...

CSS3 Animation displaying text color change on various elements in a continuous loop

I was attempting to create a dynamic animation where multiple texts change color, similar to what you can see on https://vercel.com. I have successfully figured out how to make one text change color using keyframes, but I am struggling to make each text ch ...

How can you append an object with a defined key to an array in Vue?

Currently developing a vue-application that includes a component for managing driving licenses. Here is an overview of my data setup: data() { return { custom_licenses: [], basic_licenses: [] } } Within my methods, I have the following l ...

Is there a way I can align these items in the center of the span?

Issue at Hand: The SVG and text elements are not aligning to the center of my spans as desired. Attempts Made: I have experimented with different display modes, used margin properties for alignment, and tried adjusting text-align. I even searched for so ...

Error encountered while attempting to load JSON data into HTML audio element

I need to incorporate data from a JSON file into an HTML audio tag. While I've been able to extract the desired information from the JSON, I'm facing difficulty loading it into HTML. Here's what I've tried so far: <?php $json = file ...

When attempting to execute a script that includes document.write, it will not function as expected

Our web program is utilizing ajax and jquery, specifically Netsuite. I've been attempting to adjust elements on a page using document.ready and window.load methods in order to load an external script onto the page. Regardless of whether I load this ex ...