Blog-Archiv

Dienstag, 29. Dezember 2015

Replacement for CSS fixed position

The CSS declaration position: fixed pins an HTML element to the browser's view-port. Such an element does not move when you scroll the page content away. But when you apply this CSS to an element that sits within a page-internal scroll-pane, the element would ignore its relative parent and go to top-level, try it out below.

Red button CSS position: absolute fixed

On the right side you find a red menu-button.
It does not scroll with this text, so it is kind of "fixed",
although its CSS position property is set to absolute.
Try to change it to fixed by radio-buttons above.

We learn that position: fixed is good just for the browser's view-port, not for view-ports of page-internal scroll-panes. So when we develop CSS concepts like "sticky header", we always have to change source-code when we want to apply them to elements being in other scroll-containers than the browser itself.

There are voices saying

You shouldn't use internal scroll-panes.
Users do not like to scroll, and it removes accessibility from your web-page.
Besides, it is not printable.

One of the computer application archetypes is the master-details view. Both the master (left) and the details (right) need to be independently scrollable. Think of the JavaDoc page layout, where two left-side frames serve as iterative navigation for a right-side content display. Yes, HTML frames have been deprecated. But: why have they been there? Because they were useful! There will always be situations where you need an internal scroll-pane on a web-page.

There are even more reasons to replace position: fixed than internal scroll-panes. It seems to be not well supported on mobile browsers.

So this article will be about a "fixed" menu button (see my previous Blog) that does not scroll with the page. And it will not use position: fixed, and there will be no JavaScript. Its CSS source-code, with according HTML structure, will be reusable also for buttons that are nested into internal scroll-panes.

How to avoid position: fixed

I would like to distinguish now between

  1. the browser scroll-bar, and
  2. any internal scroll-bar shown by a height-restricted DIV.

To get rid of position: fixed, we need to replace the browser scroll-bar by an internal scroll-bar that should look exactly the same.

The idea is to configure the HTML and BODY tags to be 100% height (I wrote about this in a passed Blog), and then set the BODY to position: relative, so that subsequent elements can be positioned absolute to it. This gives the same effect as position: fixed.

Care must be taken that no position: static content is a direct child of BODY, except just one scroll-pane, which must be configured to height: 100% and overflow: auto (show scrollbar when necessary). The only static content allowed within BODY are elements that have float: left or float: right, and thus do not shift the vertical start-coordinate down.

CSS

Here are the relevant CSS parts (without beautifications like restoring the browser default padding).

Restrict page to the browser's view-port size (avoiding any scrollbars):

      html, body {
        height: 100%;
        width: 100%;
        padding: 0;
        margin: 0;
        overflow: hidden; /* make any eager browser scrollbar disappear */
      }

Define scroll-pane-containers and scroll-panes:

      .scroll-pane-container {
        position: relative;
      }
      .scroll-pane {
        height: 100%;
        width: 100%;
        overflow: auto;
      }

HTML

Here is the necessary HTML structure for this to work.

  <body class="scroll-pane-container">
    
    <div class="scroll-pane">

      <!-- any static content goes here and only here-->

    </div>
    
  </body>

When having long content within that page, you will see a scroll-bar on the right that looks exactly like the browser's scroll-bar, and it also will act the same way. Thus we replaced the browser scroll-bar by an internal scroll-bar of a "ground" DIV.

Sticky Button Example

Here comes an example for the new way to define fixed. We declare sticking to top-right corner by positioning absolutely, and placing the sticky element into the parent element where we want it to stick to.

CSS

      .sticky-top-right {
        position: absolute;
        top: 0;
        right: 1.3em; /* leave place for a scrollbar */
      }

HTML

  <body class="scroll-pane-container">
    
    <input class="sticky-top-right" type="button" value="&#9776;"/>

    <div class="scroll-pane">
        
      <div style="height: 60em;">
        I am the top-level scrolled content.<br>
        Please scroll down to check whether the button moves.
      </div>
      
    </div>

  </body>

Mind that the button is declared where no static content should be: between the scroll-pane-container (BODY) and the scroll-pane. This does not matter because the button is not a static content, its position is absolute.

By the way, you could put the button also inside the scroll-pane. It would make no difference as long as the scroll-pane is not of position: relative.

Compound Example Page

Here is a compound example that shows that the same CSS can drive a top-level sticky button AND a nested sticky button.

I am the top-level scrolled content.
Please scroll down to check whether the button moves, and to see the nested scroll pane.
I am the nested scrolled content.
Please scroll down to check whether the button moves.

The CSS in head contains just the necessary declarations. Irrelevant stylings have been done down in the HTML by inline style attributes.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1"/>
    <title>Replace Fixed Position by Absolute</title>
    
    <style type="text/css">
      html, body {
        height: 100%;
        width: 100%;
        padding: 0;
        margin: 0;
        overflow: hidden; /* make any eager browser scrollbar disappear */
      }
      .scroll-pane-container {
        position: relative;
      }
      .scroll-pane {
        height: 100%;
        width: 100%;
        overflow: auto;
      }
      .sticky-top-right {
        position: absolute;
        top: 0;
        right: 1.3em; /* leave place for a scrollbar */
      }
    </style>
  </head>
  
  <body class="scroll-pane-container">
    
    <input class="sticky-top-right" type="button" value="&#9776;"/>

    <div class="scroll-pane">
        
      <div style="height: 60em;">
        I am the top-level scrolled content.<br>
        Please scroll down to check whether the button moves, and to see the nested scroll pane.
      </div>
      
      <div class="scroll-pane-container" style="height: 20em; margin-left: 10%; margin-right: 10%;">
      
        <input class="sticky-top-right" type="button" value="&#9776;"/>
    
        <div class="scroll-pane">
          <div style="height: 30em;">
            I am the nested scrolled content.<br>
            Please scroll down to check whether the button moves.
          </div>
        </div>

      </div>
      
    </div>
    
  </body>
</html>

As you can see here, the definition of scroll-pane-container as CSS class actually made sense. In this example the class is reused in the element-hierarchy of the nested scroll-pane. Mind that all internal scroll-pane-container instances will restrict their height in some way, so it does not make sense to put the height or width definition into the class.

You can see this concept applied to the pure-CSS slide-menu button of my previous Blog on my homepage.




Keine Kommentare: