Do you have a page with a lot of blocks and you need to keep the right information all along your scroll through the page? Check this out

Hi all!

No teasing, demo is here: https://test-play.softr.app/freeze-block
You will understand exactly what it does… you just have to scroll to see the magic.
To be viewed on desktop! No point in using this on mobile so I prevented it to work on mobile

Now the custom codes to do it! Note that the header is not set as “Fixed”.
Note also that there is a little bug with my javascript that makes the scroll to top not perfect as the header starts to overlap the list details block… In order to fix this, very simple workaround: below your header, add a custom code block, without anything inside and set the padding top and bottom to 7xs (almost the minimum). It will solve it perfectly

In the header code of the page settings (CSS and styles, of course!):

<style>
  #list-details1 {
    z-index: 999;
    transition: transform 0.3s ease-in-out;
  }

  .list-details1--fixed {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
  }
</style>

In the footer code of the page settings (javascript of course!):

<script>
  document.addEventListener('DOMContentLoaded', () => {
    if (window.innerWidth >= 640) {
      const listDetails1 = document.querySelector('#list-details1');
      const header = document.querySelector('header');
      const headerHeight = header.offsetHeight;
      const listDetails1Top = listDetails1.getBoundingClientRect().top + window.pageYOffset;

      function toggleListDetails1Fixed() {
        const scrollY = window.pageYOffset;
        if (scrollY >= listDetails1Top - headerHeight) {
          listDetails1.classList.add('list-details1--fixed');
          header.style.marginBottom = `${listDetails1.offsetHeight}px`;
        } else {
          listDetails1.classList.remove('list-details1--fixed');
          header.style.marginBottom = 0;

          if (scrollY <= listDetails1Top) {
            header.style.marginTop = 0;
          }
        }

        const scaleFactor = 0.7;
        const scale = 1 - (Math.min(scrollY, listDetails1Top) / listDetails1Top) * (1 - scaleFactor);
        listDetails1.style.transform = `scale(${scale})`;
      }

      window.addEventListener('scroll', toggleListDetails1Fixed);
      window.addEventListener('resize', toggleListDetails1Fixed);
    }
  });
</script>
2 Likes

UPDATE

With the following update inside the JavaScript part of the code (so everything in the footer code of the page settings), you will be able to change the width of the list-details block on scroll (I found that the width on scroll was a little too wide with the previous code).
AND you will be able to choose the position of the list details block on scroll (center, left or right).
Way better to let your users see the right information in the other blocks when scrolling.

Page demo is still https://test-play.softr.app/freeze-block

<script>
document.addEventListener('DOMContentLoaded', () => {
  if (window.innerWidth >= 640) {
    const listDetails1 = document.querySelector('#list-details1');
    const header = document.querySelector('header');
    const headerHeight = header.offsetHeight;
    const listDetails1Top = listDetails1.getBoundingClientRect().top + window.pageYOffset;

    function toggleListDetails1Fixed() {
      const scrollY = window.pageYOffset;
      if (scrollY >= listDetails1Top - headerHeight) {
        listDetails1.classList.add('list-details1--fixed');
        header.style.marginBottom = `${listDetails1.offsetHeight}px`;

        // Change fixed width on scroll
        const listDetails1Width = 800;
        listDetails1.style.width = `${listDetails1Width}px`;

        // Position on left, right, or center. Only change the value of const (here set a "center"), don't touch the if lines
        const listDetails1Position = 'center';
        if (listDetails1Position === 'left') {
          listDetails1.style.left = 0;
          listDetails1.style.right = 'auto';
          listDetails1.style.marginRight = 'auto';
        } else if (listDetails1Position === 'right') {
          listDetails1.style.right = 0;
          listDetails1.style.left = 'auto';
          listDetails1.style.marginLeft = 'auto';
        } else {
          listDetails1.style.left = '50%';
          listDetails1.style.right = 'auto';
          listDetails1.style.marginLeft = `-${listDetails1Width / 2}px`;
          listDetails1.style.transform = 'translateX(-50%)';
        }
      } else {
        listDetails1.classList.remove('list-details1--fixed');
        header.style.marginBottom = 0;

        if (scrollY <= listDetails1Top) {
          header.style.marginTop = 0;

          // Reset position and width
          listDetails1.style.width = 'auto';
          listDetails1.style.left = 'auto';
          listDetails1.style.right = 'auto';
          listDetails1.style.marginLeft = 'auto';
          listDetails1.style.marginRight = 'auto';
          listDetails1.style.transform = 'none';
        }
      }

      const scaleFactor = 0.7;
      const scale = 1 - (Math.min(scrollY, listDetails1Top) / listDetails1Top) * (1 - scaleFactor);
      listDetails1.style.transform = `scale(${scale})`;
    }

    window.addEventListener('scroll', toggleListDetails1Fixed);
    window.addEventListener('resize', toggleListDetails1Fixed);
  }
});
</script>
4 Likes

This is fanastic! I am trying to make the background of the floating block transparent / opacity of 0, but am having issues doing so. Do you have any suggestions?

Hey @matthieu_chateau,

I missed this amazing setup :slight_smile: Thanks so much.

Here is the code for adding transparency
Here is the demo https://test-play.softr.app/freeze-block-transparent

Don’t ask me more because it was hell on earth to get to something correct :joy:.
It is not perfect as the whole list details gets the transparency, but I’m not able to do more.

<script>
document.addEventListener('DOMContentLoaded', () => {
  if (window.innerWidth >= 640) {
    const listDetails1 = document.querySelector('#list-details1');
    const header = document.querySelector('header');
    const headerHeight = header.offsetHeight;
    const listDetails1Top = listDetails1.getBoundingClientRect().top + window.pageYOffset;

    function toggleListDetails1Fixed() {
      const scrollY = window.pageYOffset;
      if (scrollY >= listDetails1Top - headerHeight) {
        listDetails1.classList.add('list-details1--fixed');
        header.style.marginBottom = `${listDetails1.offsetHeight}px`;

        const listDetails1Width = 800;
        listDetails1.style.width = `${listDetails1Width}px`;

        const listDetails1Position = 'center';
        if (listDetails1Position === 'left') {
          listDetails1.style.left = 0;
          listDetails1.style.right = 'auto';
          listDetails1.style.marginRight = 'auto';
        } else if (listDetails1Position === 'right') {
          listDetails1.style.right = 0;
          listDetails1.style.left = 'auto';
          listDetails1.style.marginLeft = 'auto';
        } else {
          listDetails1.style.left = '50%';
          listDetails1.style.right = 'auto';
          listDetails1.style.marginLeft = `-${listDetails1Width / 2}px`;
          listDetails1.style.transform = 'translateX(-50%)';
        }

      } else {
        listDetails1.classList.remove('list-details1--fixed');
        header.style.marginBottom = 0;

        if (scrollY <= listDetails1Top) {
          header.style.marginTop = 0;

          listDetails1.style.width = 'auto';
          listDetails1.style.left = 'auto';
          listDetails1.style.right = 'auto';
          listDetails1.style.marginLeft = 'auto';
          listDetails1.style.marginRight = 'auto';
          listDetails1.style.transform = 'none';
        }
}

      const scaleFactor = 0.7;
      const scale = 1 - (Math.min(scrollY, listDetails1Top) / listDetails1Top) * (1 - scaleFactor);
      listDetails1.style.opacity = scale;
      listDetails1.style.transform = `scale(${scale})`;
    }

    window.addEventListener('scroll', toggleListDetails1Fixed);
    window.addEventListener('resize', toggleListDetails1Fixed);
  }
});
</script>

That look(ed) fantastic!
But…First time your demo worked like intended. Second time opening your demo the floating effect didn’t appear…

And the third? :sweat_smile:
Works fine for me, even when scrolling soon during content load

Mhh…tried it in Chrome and on an iPad and indeed works very nice.
Maybe got something to do with me resizing Safari to half screen, and after that it didn’t work in Safari anymore - even after resizing to full-screen again.
Nevertheless, awesome custom code!

Yes, I made the code to not work on mobile size: any screen or window size below 640px will make the code disable, as explained in the first post.
(This type of design is not made for mobile, the added value is for desktop)

You can stop this limitation by removing the line if (window.innerWidth >= 640) {

Thanks so much!

Hey @matthieu_chateau,
This looks amazing! :smiley:
Unfortunately, I’m having trouble assigning the custom code to the static “Simple Text” block myself. After a couple of tries to change the list-details1 block name in your code to the name of my block I went the other way around and tried renaming the Softr block itself to “list-details1” and keeping the code as is, all without success. Does it only work with list details block or can you tell anything else I’m missing?

Hi,
I made the test with the static block “simple text”, it works perfectly.
Note that this code is not made to be used on mobile.

The <style> part of the code must be inserted in the header code of the page settings
The <script> part of the must be inserted in the footer code of the page settings

The code works in relation with the header. So your header must have the same name/Id in the code. Specifically this line: const header = document.querySelector(‘header’);
It is ‘header’ beacause my header has this name

Copy paste the codes by changing it to list-details1 doesn’t change anything, except less work for you (so that might be better). If your header has a different name, you can do the same

1 Like

Hi @matthieu_chateau,
Thank you, changing the header block name to ‘header’ fixed it! :pray:t4:
What I find strange though is that if the header block has any other name than ‘header’ e.g. ‘header-test’ and I put ‘header-test’ into the line

const header = document.querySelector(‘header-test’);

it doesn’t work. Anyways, using ‘header’ works so thanks a ton, I love the feature! :black_heart:

Yes, this is because you would also need to change other parts of the code with ‘header-test’. Basically all where the header name is used as a selector.

Ah, that makes sense.